Professional Documents
Culture Documents
CONTENT
INTRODUCTION
7
7
7
DEVELOPER TOOLS
Troubleshooting
The Debugger
9
9
MAPPING TABLES
Mapping Quarters to the Orders table
MonthYear
Cleaning up the table structure
ADVANCED SCRIPTING
Condition on a field in a table
Aggregating Data
Joining tables
Concatenation
Preceding Load on Preceding Load
19
19
23
26
31
31
39
39
39
39
40
41
51
51
54
56
63
63
64
64
65
65
65
81
Performance Tuning
Best Practices for QlikView File Optimization
Creating Incremental Loads
81
83
85
95
Overview
Example: Balances at Specific Dates
Example: Balances for Transactions (in/out)
95
95
98
10QLIKVIEW SECURITY
Access control
Access levels
Access control database
Inherited access restrictions
Hidden script
Adding Section Access
Access control for certain commands
Further access control
Unattended Command Line Reload Considerations
Access restrictions on selected field values
Field value limitation in Section Access
63
101
101
101
102
103
104
104
109
110
111
111
112
115
115
116
119
119
121
1 INTRODUCTION
Many students will come to this course having already taken QlikView
classroom training. This chapter covers formatting and coventions used in
the course manual.
Exercise/Do:
This is a sample of instructions you would see to complete an exercise containing a sequence of steps.
1
2
3
All commands, as well as all names of menus, dialogs and buttons are in the
following font style: File - Open
All names of list boxes, graphs and specific data in list boxes, etc. are in the
following font style: Country
All file names are in the following font style: QlikViewCourse.qvw
Tips and Notes are outlined in a highlighted box, as you see below:
This sample sentence is used to illustrate important points in the text, tips
and notes to consider as you complete the course materials
2 DEVELOPER TOOLS
Objectives
2.1 Troubleshooting
Debugging is an art. There are best practices and guidelines that can help in
the debugging process. The key point to take away from debugging is to
change one item at a time and then examine the impact of that change. If
more than one item is changed without an examination, debugging becomes
difficult. This portion of the class will not teach you how to debug an document but will point you in the direction of where to look if your document
does not operate or look like you expected it to. These basic tools and processes are the best place to start when debugging a QlikView document.
Also, during script execution in debug mode, the debugger allows for monitoring of variables. This is useful for validation of variable state at different
stages of script execution.
Remember, always make sure the Generate Logfile checkbox is checked
under Settings|Document Properties|General. Also remember, missing or
misplaced commas and semicolons are the source of many error messages.
Check for these common problems first.
Figure 1. Breakpoints
You will notice in the Figure above that three breakpoints are set at lines 38,
39 and 44. Note that the debugger will not stop at line 39 since it is not the
first statement in the code block.
This could be a LOAD statement, a SQL LOAD with a preceding LOAD, or a
grouped LOAD statement. If you trace each statement back from the semicolon you will see that while the key word MAPPING on line 39 is part of a
code block ending in a semi-colon, MAPPING is not the first line of the code
block Shippers: is.
2.2.1 Variables
If you have created variables in the load script, you can view the value of the
variable as the script is loading. You might be surprised that it does not hold
the expected value(s). Create additional variables to investigate more states
during script loading.
10
Exercise: Debugging
Do:
1
2
3
4
5
6
7
8
9
11
12
Exercise
Do:
1
2
13
14
Exercise
Do:
1
2
3
4
5
6
7
8
9
Take a sample of the data and do not create a data model that is more
that two tables, if possible. There should not be more than three columns in each table or more that fifteen rows. Use the inline wizard or
Excel to create the tables.
When building complex expressions, use a Straight Table chart and build
expressions a single column at a time.
Do:
1
2
3
Expression
Label
Sum(LineSalesAmount) Sales
Count(LineNo)
Lines
Avg(LineSalesAmount) AvgSales
Sum(Quantity)
Qty
4
15
Expression
Qty/Lines
Sales/Qty
Label
AvgQty
UnitPrice
Here you see that you can type an expression directly or you can use an
expression label as an alias to the expression to get the same results.
Note: You can also reference an expression by its Table Column number
(zero-based index) but DONT DO IT!
5
Build your complex expression one column at a time and then move the
expression to the placeholder of the Label that is referenced in the
expression. This is the best approach for slowly building complex
expressions.
Note: You cannot nest aggregations (aggregate an aggregation) except by
using the aggr() function, covered later in this course.
Dates
In many cases, date fields are key fields. To join tables from different systems, there are cases where dates need to be converted to a common format
before dates from disparate datasources will match properly. It is always a
good idea to check the key field using a Table Box to verify that the date format is consistent throughout the entire key field.
Do:
1
2
16
Next, create a Table Box including all columns, sort by Date ascending,
and scan the data. You should see something like the Table Box below.
Note that the dates look the same but similar dates do not link.
To determine what is causing the problem, create a Text Object that displays the expression
=num(Date, ###.######)
and alternately select each of the two similar dates. Note the fractional
part of each displayed date in the Text Object. The date from one table is
actually a timestamp while the other date has no time associated with it.
17
Next fix Date2 with the floor() function (floor(Date2)) changing Date2
from a timestamp to a date. (Note the floor() function truncates a decimal number and returns the next lower integer value, i.e., 2.7 becomes
2). Now reload the data. The result should look like this where you
have rows that now link.
18
3 MAPPING TABLES
Objectives
19
Figure 2. Month field with key indicator in the Available Fields listing.
By changing our Quarters table into a MAPPING table, we will be able to
integrate the Quarters field into the same table as Month (the Orders table).
The MAPPING prefix is used on a LOAD or SELECT statement to create a
mapping table. Tables read via MAPPING LOAD or MAPPING SELECT are
treated differently from other tables. They will be stored in a separate area
of memory and used only as mapping tables during script execution. After
script execution they will be automatically dropped.
A mapping table must have two fields, the first one containing comparison
values and the second the desired mapping values. The two fields must be
named, but the names have no relevance in themselves. The field names have
no connection to field names in regular input tables. When mapping tables
are used to map a certain field value or expression, that value will be compared to the values in the first field of the mapping table. If found, the original value will be replaced by the corresponding value in the second field of
the mapping table. If not found, no replacement is made.
The syntax is:
mapping ( load statement | select statement )
20
Exercise
Do:
1
2
3
4
5
6
7
21
22
3.2 MonthYear
We will complete our time dimension fields by creating a new field that
makes every month unique. There are, of course, several ways to accomplish
this. In this course, we will create the field MonthYear by using QlikView
date functions based on the OrderDate field, along with a date formatting
function to provide the correct display format for our new month field.
23
Exercise
Do:
1
2
3
4
24
5
6
25
26
Exercise
Do:
1
2
Shippers_Map:
MAPPING LOAD
ShipperID,
CompanyName AS Shipper;
SQL SELECT *
FROM Shippers;
Add the following line to the bottom of the Orders table.
applymap('Shippers_Map', ShipperID,
'MISSING') AS Shipper
The above line of script tells QlikView to use the word MISSING in the
Shipper field where no matching ShipperID values can be found.
27
7
8
Verify that your Orders table script should resemble the following;
//*************** Orders table
***************
Orders:
LOAD CustomerID,
EmployeeID,
EmployeeID AS EmployeeSalesID,
Freight,
OrderDate,
year(OrderDate) AS Year,
month(OrderDate) AS Month,
day(OrderDate) AS Day,
applymap('Quarters_Map',
num(month(OrderDate)),
null()) AS Quarter,
date(monthstart(OrderDate), 'MMMYYYY') AS
MonthYear,
OrderID,
OrderID AS OrderIDCounter,
ShipperID,
applymap('Shippers_Map', ShipperID,
'MISSING')
AS Shipper;
SQL SELECT *
FROM Orders;
Save the document and Reload the script.
Take a look in the Table Viewer to see that in fact the Shipper field is
now a part of the Orders table.
28
2
3
4
5
Launch QlikView and save a working copy of the AdditionalMappingExercises.qvwQlikView file in the working directory for this chapter
(C:\QlikViewTraining\DeveloperII\Chapter03).
Open the Edit Script dialog.
Create a tab after the Main tab and name it Mapping Loads.
Move the Shippers_Map and Quarters_Map script to the Mapping
Loads tab.
Map the Divisions table to the Customers table. Make sure to remove
(or comment out) the Divisions table from the Dimensions tab and create a mapping table on the Mapping Loads tab.
Although script examples are at the bottom of this page, they are for reference only should you need help. We encourage you to try to add the
Mapping Load and ApplyMap script on your own.
Are there any other tables that can be mapped to another table? Check
the Table Viewer. Make sure to look for tables with only two fields. Discuss with the course Instructor.
//*************** Divisions ***************
Divisions_Map:
MAPPING LOAD
DivisionID,
DivisionName;
SQL SELECT *
FROM Divisions;
//*************** Customers ***************
Customers:
LOAD
Address,
City,
CompanyName,
ContactName,
Country,
CustomerID,
DivisionID,
applymap ('Divisions_Map', DivisionID)
as
Division,
Fax,
Phone,
PostalCode,
29
StateProvince;
SQL SELECT *
FROM Customers;
30
In our example data, there is a Budget table for Employees and Offices. We
are going to load this into our QlikView document. The Budget table is built
as a cross table and we need to convert this when we read it into QlikView.
We will also add a field to the Budget table that allows us to change the values of the budget to help in planning.
31
2
3
4
5
6
7
8
9
Launch QlikView and save a working copy of the LoadingBudgetData.qvw QlikView file in the working directory for this chapter
(C:\QlikViewTraining\DeveloperII\Chapter04).
Open the Edit Script dialog.
Create a new tab following the Sales Person tab and call it Budget.
Click on the Table Files button and open the Budget.xls file from the
data folder for this chapter.
In the File Wizard, start by setting the Header Size to one line.
Next, we need to make sure that there are no empty rows in the Office
field. Click on the Next button and then click on Enable Transformation Step to transform the table and then click the Fill tab.
Click the Fill button and then Cell Condition. We want the cell to fill if
it is empty. Click OK, OK and Next to return to the File Wizard.
Click on Crosstable to change the table from a cross table to a normal table.
Click on the Qualifier Fields and type 2 to expand them to include both
the Office and EmployeeID fields in purple.
A qualifying field in a cross table, is a field that should not be altered
during the Cross table load.
2006 is not a qualifying field. This is the first of the fields we want to
transform so that the years are placed in one field and the budget values
are placed in another field.
10 Name the Attribute BudgetYear.
11 Name the data BudgetAmount.
12 Click OK.
33
13 Click Finish. You should have the following table in the script.
CROSSTABLE(BudgetYear, BudgetAmount, 2)
LOAD
Office,
EmployeeID,
[2006],
[2007],
[2008],
[2009],
[2010]
FROM Datasources\Budget.xls (biff, header is line,
embedded labels, table is [Sheet1$],
filters(Replace(1, top, StrCnd(null))))
;
14 Name this table BudgetTemp.
15 Save and Reload the document.
Open the Table Viewer. As you can see, there is a synthetic key between
the BudgetTemp and the Employees table. We want to remove this synthetic key. We will adjust our script to get the data we need from the
BudgetTemp table, and then we will drop it.
16 Go to the Script Editor.
34
Do:
1
2
3
Now we will address the synthetic key field in the Budget statement and
add the input field to set the budget prognosis. If it is not already open,
launch the QlikView document you have been working on in this chapter.
Go to the Script Editor and place the cursor right after the SET statements on the Main tab.
Enter the following statement.
INPUTFIELD BudgetPrognosis;
The INPUTFIELD statement tells QlikView that the field will be an
INPUT field. You have to state this in the script before you actually read
the field in a table.
35
Now, we can use the INPUT field BudgetPrognosis to set different budget values if we need to alter the budget to correspond to the actual values.
Create a new Table Box titled Sales Budget consisting of the following
fields: SalesPerson, BudgetYear, BudgetAmount, and BudgetPrognosis.
Move your mouse cursor over the BudgetPrognosis column in the table
box. An entry arrow icon will appear.
8
9
36
Click on the Input icon on any row and enter any number.
Right-click on the BudgetPrognosis column. Notice the related options
of Restore Single Value, Restore Possible Values, and Restore All
Values.
QlikView Developer II |
Now, we can use the INPUT field BudgetPrognosis to set different budget values if we need to alter the budget to correspond to the actual values.
37
5 ADVANCED SCRIPTING
There are several key measures to create in this chapter. We need to calculate
these in the script. They are OrderLineAmount, CostOfGoodsSold and
Margin. To make these calculation fields, we need to do some advanced
scripting. There is also a key field, CategoryType, which we need to create.
Objectives
Learn and use
Conditions in tables
Aggregation
Joining tables
Preceding Load on Preceding Load
39
5.4 Concatenation
Another way to join data together from multiple tables is to use concatenation. There are two ways to concatenate data. We will explore each of these
methods.
5.4.1 Automatic Concatenation
If the field names and the number of fields of two or more loaded tables are
exactly the same, QlikView will automatically concatenate the results of the
different LOAD or SELECT statements into one table.
Example:
LOAD a, b, c FROM Table1.csv;
LOAD a, c, b FROM Table2.csv;
The resulting logical table has the fields a, b and c. The number of
records is the sum of the numbers of records in table 1 and table 2.
Rules:
- The number and names of the fields must be exactly the same.
- The order of the fields listed in each statement is arbitrary.
- The order of the two statements is arbitrary.
5.4.2 Forced Concatenation
If two or more tables do not have exactly the same set of fields, it is still possible to force QlikView to concatenate the two tables. This is done with the
Concatenate prefix in the script, which concatenates a table with another
named table or with the last previously created logical table.
Example:
LOAD a, b, c FROM Table1.csv;
Concatenate LOAD a, c FROM Table2.csv;
The resulting logical table has the fields a, b and c. The number of records in
the resulting table is the sum of the numbers of records in table 1 and table
2.
The value of field b in the records coming from table 2 is NULL.
Rules:
- The names of the fields must be exactly the same.
- The order of the fields listed in each statement is arbitrary.
- Unless a table name of a previously loaded table is specified in the concatenate statement the concatenate prefix uses the last previously created
logical table. The order of the two statements is thus not arbitrary.
40
41
Exercises
Do: Condition on a Field in a Table
1
2
3
4
Launch QlikView and save a working copy of the AdvancedScripting.qvw QlikView file in the working directory for this chapter
(C:\QlikViewTraining\DeveloperII\Chapter05).
Open the Edit Script dialog and go to the Dimensions tab.
Find the Categories table and place the cursor after the last field of this
table.
Type a comma and press ENTER to get to a new row. Type the following
to create the CategoryType.
IF(CategoryID = 5 OR CategoryID = 6, 'Footwear',
'Clothing') AS CategoryType;
The IF statement in QlikView uses the following syntax:
if( condition , then , else )
5
6
The condition should be evaluated to be either true or false. If the condition is true, the then part will be processed. However, if the condition is
false, the else portion of the statement will be processed.
Save the script and Reload.
Look at the fields. You can now see that we have a new field named CategoryType.
Do: Aggregation
1
2
3
Open the QlikView file you have been working on in this chapter.
Open the Script Editor and place the cursor after the OrderDetails table
on the Orders tab.
Add the following statement to your script:
LOAD
OrderID,
sum(LineSalesAmount) AS
OrderSalesAmount
RESIDENT OrderDetails
GROUP BY OrderID;
4
43
Go to the Script again and place the cursor just in front of LOAD in the
table just created.
Type LEFT JOIN (Orders) in front of the LOAD statement. The result
should be as below.
LEFT JOIN (Orders)
LOAD
Here we use a LEFT JOIN load because we want to make sure that we do
not get any values of Orders that do not exist in the Orders table. In
QlikView, the default join behavior is a full outer join. Therefore, if there
are no matching fields between the two joined tables, you will get a Cartesian product of the records. Since we are specifying OrderID in both
tables, and we are specifying Left, only the records matching OrderID
included in the Orders table will be included. We include the OrderSalesAmount field because that is what we want to add to the Orders table.
Save and Reload the script.
Do: Concatenation
1
2
Open the Edit Script dialog in the QlikView file you have been working
on in this chapter.
Position your cursor on the File Data tab directly after the Employees
table has been loaded. We need to duplicate the fields we currently have
for Employees, so we will not use the File Wizard in this case. Instead,
copy the Employee LOAD statement, and paste the copied text after the
original text.
Since the new file data format matches our first file, we only need to
change the source of the data. Revise the From clause in the new load
statement to read as follows:
FROM Datasources\Employees_New.xls (biff, embedded
labels, table is [Employee$]);
4
5
44
This will always concatenate these two tables together, even if inadvertent script changes are made later to one of the loads, but not the other.
The new Employee LOAD statement should now begin as follows:
Concatenate (Employees) Load
7
You may have noticed that there are very few differences between our
two Employee LOAD statements. In fact, we can use another QlikView
feature to load the same data in just a single load statement. By using a
wildcard specification on the FROM file name, QlikView will automatically load from all files matching that specification, and concatenate the
data into a single logical table for you. Since both our file names start
with Emp, and have the .xls file extension, we can use the wildcard
Emp*.xls in the FROM clause. If we make this change, and comment
the second Employee LOAD statement, the script should now read as follows:
Employees:
Load Office & - & EmpID as BudgetKey,
EmpID AS EmployeeID,
//[Last Name],
//[First Name],
[First Name] & ' ' & [Last Name] AS Name,
Title,
[Hire Date],
Year([Hire Date]) AS [HireYear],
Office,
Extension,
[Reports To],
[Year Salary]
FROM Datasources\Emp*.xls (biff, embedded labels,
table is [Employee$]);
//Employees:
//Concatenate (Employee) Load
//Office & - & EmpID as BudgetKey,
//EmpID AS EmployeeID,
//[Last Name],
//[First Name],
//[First Name] & ' ' & [Last Name] AS
Name,
//Title,
//[Hire Date],
//Year([Hire Date]) AS [HireYear],
//Office,
45
//Extension,
//[Reports To],
//[Year Salary]
//FROM Datasources\Employees_New.xls (biff, embedded
labels, table is [Employee$]);
8
9
Save the revised script and the QlikView document. Then Reload, and
verify the Employee data has not changed.
As an optional exercise, you may want to try to determine why the
employees listed in the Employees_New.xls file are not assigned e-mail
addresses (field e-mail is null for these employees). What do you need to
do to correct this problem?
In the QlikView file you have been working on in this chapter, go to the
Script Editor and place the cursor at the bottom of the Mapping Loads
tab.
Create the following table either by typing it from scratch or by using
the Select button.
UnitCost_Map:
MAPPING
LOAD
ProductID,
UnitCost;
SQL SELECT *
FROM Products;
Go to the Orders tab and add the following script line to the bottom of
the OrderDetails table just above the SQL SELECT * line. Remember to
remove the semi-colon from the line above and replace it with a comma.
applymap('UnitCost_Map', ProductID, 0) * Quantity AS
CostOfGoodsSold;
We combine the applymap function with a calculation and create the
CostOfGoodsSold field directly in the preceding LOAD of the OrderDetails table.
The last of the remaining key measures that we need to create in the
script is the Margin. The Margin is calculated as the LineSalesAmount
CostOfGoodsSold. The easiest way to do this is to place a preceding
load on top of the preceding load in the OrderDetails table. You can add
several preceding loads on top of each other and they will be evaluated
from the bottom and up. This means that you can use a field created in a
preceding load in a new preceding load on top of the first one.
46
4
5
6
47
Extra Credit
Do:
1
Launch QlikView and save a working copy of the AdditionalExercises.qvw QlikView file in the extra cradit folder for this chapter
(C:\QlikViewTraining\DeveloperII\Chapter05\ExtraCredit_Chapter05).
To clean up the script a little more, Join the Categories table with the
Products table. Make sure not to get any Categories that do not exist in
the Products table.
//************** Categories table **************
Categories:
LEFT JOIN (Products)
LOAD
CategoryID,
CategoryName,
Description AS CategoryDescription,
IF(CategoryID = 5 OR CategoryID = 6,
'Footwear',
'Clothing') AS
CategoryType;
SQL SELECT *
FROM Categories;
3
4
Sum(LineSalesAmount)
COGS
Sum (CostOfGoodsSold)
Margin
Sum (Margin)
Margin %
Sum (Margin)/ Sum (LineSalesAmount)
Format the table the way you want to.
49
51
The $ sign represents the records of the current selection. The set expression
{$} is, therefore, the equivalent of not stating a set expression at all. {1-$} is
all the more interesting as it defines the inverse of the current selection, that
is, everything that the current selection excludes.
Selections from the Back/Forward stack can be used as set identifiers, by use
of the dollar symbol: $1 represents the previous selection and is equivalent
to pressing the Back button. Similarly, $_1 represents one step forward and
is equivalent to pressing the Forward button. Any unsigned integer can be
used in the Back and Forward notations. $0 represents the current selection.
Finally, bookmarks can be used as set identifiers. Either the bookmark ID or
the bookmark name can be used, BM01 or MyBookmark.
6.1.3 Set Operators
Several operators are used in set expressions. All set operators use sets as
operands, as described above, and return a set as result. The operators are as
follows:
+ Union. This binary operation returns a set consisting of the records
that belong to any of the two set operands.
Exclusion. This binary operation returns a set of the records that
belong to the first but not the other of the two set operands. Also, when
used as a unary operator, it returns the complement set.
* Intersection. This binary operation returns a set consisting of the
records that belong to both of the two set operands.
/ Symmetric difference (XOR). This binary operation returns a set
consisting of the records that belong to either, but not both of the two
set operands.
The order of precedence is
1
2
3
52
53
Note: Searches are case-insensitive and are made over excluded values
too.
Tip: Empty element sets, either explicitly e.g. <Product = {}> or implicitly
e.g. <Product = {"Perpetuum Mobile"}> (a search with no hits) mean no
product, i.e. it will result in a set of records that are not associated with
any product.
Further, the selection within a field can be defined using set operators and
several element sets, such as with modifier
<Year = {"20*", 1997} - {2000}>
which will select all years beginning with 20 in addition to 1997,
except for 2000.
The above notation defines new selections, disregarding the current selection in the field. However, if you want to base your selection on the current
selection in the field and add field values, e.g. you may want a modifier
<Year = Year + {2007, 2008}>. A short and equivalent way to write this is
<Year += {2007, 2008}>, i.e. the assignment operator implicitly defines a
union.
Also implicit intersections, exclusions and symmetric differences can be
defined using *=, = and /=.
Finally, for fields in and-mode, there is also the possibility of forced exclusion. If you want to force exclusion of specific field values, you will need to
use ~ in front of the field name.
Note: A set modifier can be used on a set identifier or on its own. It cannot be used on a set expression. When used on a set identifier, the modifier must be written immediately after the set identifier, e.g. {$<Year =
{2007, 2008}>}. When used on its own, it is interpreted as a modification
of the current selection.
54
A macro expansion always begins with $( and ends with ) and the content
between brackets defines how the text replacement will be done. To avoid
confusion with script macros we will henceforth refer to macro expansions
as dollar-sign expansions.
Note: Macro expansion is unrelated to script macros (VB or Java script
defined in the script module).
6.2.1 Dollar-sign Expansion using a variable
When using a variable for text replacement in the script or in an expression,
the syntax
$ (variablename)
is used. $(variablename) expands to the value in variablename. If variablename does not exist the expansion will be the empty string.
For numeric variable expansions, the syntax
$ (#variablename)
is used. $(#variablename) always yields a legal decimal-point reflection of
the numeric value of variablename, possibly with exponential notation (for
very large/small numbers). If variablename does not exist or does not contain a numeric value, it will be expanded to 0 instead.
6.2.2 Dollar-Sign Expansion with Parameters
Parameters can be used in variable expansions. The variable must then contain formal parameters, such as $1, $2, $3 etc. When expanding the variable, the parameters should be stated in a comma separated list.
If the number of formal parameters exceeds the number of actual parameters, only the formal parameters corresponding to actual parameters will be
expanded. If the number of actual parameters exceeds the number of formal
parameters, the superfluous actual parameters will be ignored.
The parameter $0 returns the number of parameters actually passed by a
call.
6.2.3 Dollar-Sign Expansion with an Expression
Expressions can be used in dollar-sign expansions. The content between the
brackets must then start with an equal sign:
$(=expression)
The expression will be evaluated and the value will be used in the expansion.
Example:
55
56
3
4
5
6
7
Label
=Only(Year)
=Only(Year)-1
Expression
Sum({$<Year={$(=Only(Year))}
>} LineSalesAmount)
Sum({$<Year={$(=Only(Year)1)}>} LineSalesAmount)
Sum({$<Year={$(=Only(Year))}
>} LineSalesAmount) Sum({$<Year={$(=Only(Year)1)}>} LineSalesAmount)
8
9
Click Finish
Save your QlikView file and then continue to edit the Annual Comparison straight table.
10 Set the Sort order to match the depiction, below, remembering that Customer should be set to Text
57
11 On the Visual Cues tab, make the negative values for the year-to-year
comparison red and the positive values green.
58
59
16 With 2009 selected in the Year list box you added at the beginning of the
Exercise, your straight table should look something like the one below:
3
4
5
60
Continue working in the file you have been using in this exercise chapter
so far.
Create a chart that compares sales of products in the category Babywear
with products in the category Childrens Wear over time for the Nice
sales office.
To do this you will need to create an expression using Set analysis and $
Expansion instead of traditional if() statements.
Set the category name to find Babywear and the office to be 4.
Pay attention to the <> and {}.
Solution:
sum({$<CategoryName={'Babywear'}, Office={4}>} LineSalesAmount)
sum({$<CategoryName={'Childrens wear'}, Office={4}>} LineSalesAmount)
Continue working in the file you have been using in this exercise chapter
so far.
Create a table that shows if there is any link between the number of
orders placed by customers and the average order value. The table
should provide information on how many customers have placed one
order, two orders etc, and also the average order value.
There are three steps to this process. First, create a calculated dimension
for the number of orders (as in how many customers had one order,
two orders, three orders, etc.). This requires aggr. First, to count the
number of orders, use
count(distinct OrderID)
and then to aggregate those against the Customer Dimension use
aggr(......, CustomerID)
Putting it together,
aggr(count(Distinct OrderID), CustomerID)
will create the necessary order "buckets" based on customer
61
Next you need a count of customers to populate the # of order buckets we created in the first step.
Count(distinct CustomerID)
And, finally, we need to find the average order amount.
Sales is from the Sales Detail Table and is a line item for every product
sold. Thus one order could have several products and thus several lines
with Sales data, so we need to aggregate by OrderID to get the total
sales amount for an order
aggr(sum(LineSalesAmount),OrderID)
gets you that number and adding the avg() gives you the requested average order amount
avg(aggr(sum(LineSalesAmount),OrderID))
62
63
We can also see what has been loaded in the load process with the previous() function. The previous() function can only be used to look one record
back in the load process. If you need to look further back than one record in
the load process, you have to nest the previous() function as in:
previous(previous(myRecord))
This can be problematic and cumbersome when you want to see the total
net change or percent change.
Let us assume we need to count the number of units that are at a given site
within a week and need to find the percent of utilization for a unit at a given
site.
If the units are at multiple sites but return to the same site within the same
week, peek() and previous() can work but require complex coding with
FOR loops or RESIDENT loads. However, we can get around that by
grouping the information from a tracking table and Left Joining the result
back into the tracking table.
The steps in the exercise demonstrate the difference.
7.4 Aggr()
Note: If you have already completed Chpater 6, skip this explanation.
Aggr() is an advanced function that allows you to calculate an aggregation
of multiple dimensions. As a rule, you can not aggregate an aggregated
64
7.5 Class()
Class() is an aggregation function that can be used to create buckets of
information similar to what you would create in an Accounts Receivable
document showing aged accounts. Another way of thinking of Class() is
that you can group your dimension values.
7.7 IntervalMatch
The IntervalMatch script statement facilitates the mapping of dates to periods or records to slowly changing dimensions, useful for creating a fully
functioning data model across the appropriate time dimensions in your business requirements. The implementation of IntervalMatch requires a few
additional steps than simply to apply the function if we are to avoid synthetic keys being created in our data structure.
IntervalMatch() is a script statement that has the same functionally as
BETWEEN used in a SQL statement. There is one downside to IntervalMatch(),
it creates a synthetic table ($Syn table). $Syn tables can have a high cost in
65
memory and User Interface performance can suffer. One solution to explore
is to LEFT JOIN the IntervalMatch() table into a parent table.
Here is a small example of IntervalMatch(). We are maintaining the approach
of keeping the examples small so that they can be easily verified.
In this exercise we will use IntervalMatch() to find the hours that an employee
is working on-site.
66
Launch QlikView and save a working copy of the QlikView file in the
working directory for this chapter and section (C:\QlikViewTraining\DeveloperII\Chapter07\LinkTables\Linktable.qvw).
Open the LinkTable.XLS Excel file and examine the Sales as well as the
Budget fields. You will see that four customers have a Budget and four
customers purchase goods each month but not the identical customers
that have a Budget. Now reload your working copy of the LinkTable.qvw file. All the information looks correct. Go to the Table Viewer
and look at the structure of the Data Model. A Synthetic Key and a Synthetic Table have been created. This can add processing overhead to the
document and, in some rare cases, can return unexpected results.
Comment the script on the SynKey tab, uncomment the script on the
Using a Key tab and reload the document. Note that we are trying eliminate the Synthetic Key by concatenating the fields that are causing the
Synthetic table. Check the result from the QVW and compare them to
the Excel sheet. You can see that we are returning incorrect information
because of key field mismatches; customers are missing from each table.
Comment the script on the Using a Key tab and uncomment the script
on the Link Table tab and reload the document.
Here we are loading the information from the Sales and Budget table
two times. This generates an intermediate table that has common information from both the tables. The level of detail is maintained in separate
tables for both the Sales and Budget. The link table must be loaded Distinct (only unique key combinations) and it will establish correct relationships.
Create a Table Box that includes all the columns.
67
Comment the script from step 3 (on the Link Table tab) and uncomment
the script on the Concatenate tab. Reload the document. Note the use of
a RecSource field to differentiate concatenated records. Note also the
use of explicit references to CONCATENATE and the table into which the
concatenation will happen. Finally, observe the LEFT JOIN that adds the
Customer field to the table and note that this results in a Data Model
with a single table. Suggest an alternative way to accomplish the same
result.
Look at the result. You will see the same results as Step 1 and Step 3.
Notice that there are null values. Now if you sum Sales Amount and
Budget Amount, they will balance.
Note: This is similar to a union in SQL. Null values do not have a high
cost in QlikView as they normally do in SQL.
Remember: Link tables resolve differences in granularity between fact
tables joining to the same dimensions.
68
Open the LeftJoin.QVW file located in the LeftJoin folder, save a working copy and run the script on the Previous tab. Create two List Boxes
and use the fields Units and Dept.
Select the Unit numbered 54543. Create a Table Box using all fields and
look at the counts.
We still dont have a full count for the units at Dept A2. We would have
to reload the table again. But where do we stop?
The solution is to aggregate the data using a GROUP BY clause from the
Tracking table data and LEFT JOIN the result to the Tracking table.
Comment the information on the Previous tab, uncomment the script on
the Left Join tab, and reload the document. With this approach you now
can find the total number of times a Unit is utilized across all Depts in
any Week. This was done with one additional RESIDENT load.
Your result should look like this:
3
4
5
69
Now you can calculate the percent of utilization of a Unit in a Dept for
a given Week.
Note: The ORDER BY clause is required here, but it will only work for a
RESIDENT LOAD or in a SQL SELECT statement. ORDER BY will not
work with a LOAD FROM statement.
Remember: Group by statements in combination with left join operations
allow for aggregation of values living in tables low down in a parent-child
hierarchy and appending to the higher tables, such as summing the value of
individual order line items into a single value per order.
2
3
70
Last, you need to set a property of the List Box to always have only 1
value selected. Note you cannot do this unless you selected a single
value in the step above.
Now select various DateMonthYear values and you will see Sales change
in one column but not the other.
71
3
4
Open the file, Class.QVW in the Class folder and save a working copy of
the file.
Create variables called vDate (set vDate to a current date and format
vDate as a date, not a numeric) and vWidth (using a pre-pended v or var
to denote variables is a best practice) on the Variables tab under the Settings | Document Properties menu
Create a Calendar Object, click the Variable(s) radio button, and choose
vDate as the variable assigned to the Calendar Object.
Create an Input Box and choose vWidth as the only Displayed Variable.
72
73
74
When you have finished the chart, edit the script creating the following
INLINE table and reload. (Note this could also be done by creating and
loading an external text or Excel file a best practice).
Next create a variable vMyAgg with the value, =Formula as show below.
Remember the equal sign:
Create a copy of the first Chart and replace the expression sum(LineSalesAmount) with
if(Desc = 'Count',
num($(vMyAgg),'###0'),num($(vMyAgg),'$###.00'))
5
6
Note the test to determine how to format the numeric display (integers
or currency). You might try including the format string with the expression in the INLINE table as an additional field and then change the
expression accordingly.
Add two List Boxes with the columns Desc and Formula plus a Text Box
with =vMyAgg in the text area.
The result is that you can change your KPIs on the fly. Your result should
look as follows:
75
76
Result:
The $Syn table has been removed with an INNER JOIN into the
table that has the start and stop values (TabB). This can reduce the overhead of a $Syn table. You also have the discrete records for all occurrences and can use this table to JOIN to other tables.
Note that you have increased the size of TabB. The rows in TabB will
increase to the number of rows that would be in the $Syn table.
77
Extra Credit
Do: Additional AGGR Exercise
1
2
Open the file, Aggr.QVW in the Aggr folder and save a working copy of
the file.
In this example we are going to find the average count of Orders by
Country, Company, Year and Month using the aggr() function.
avg(aggr(count(Distinct OrderID),Country,CompanyName, Year,
Month))
The average LineSalesAmount by Country, Company, Year and Quarter
with the expression below:
avg(aggr(sum(LineSalesAmount),Country,CompanyName, Year,
Quarter))
And, finally, the Total Sales
sum(LineSalesAmount)
Create the chart shown below using the expressions above.
Once you have created the chart, investigate further by changing the
time dimension in the dimensional portion of the aggr() function.
Determine how many Orders were placed in each Month and how many
Months had Orders in them. Try to explain some of the resultant averages in each column.
79
81
Data Structure
Key Fields
Denormalization vs. Normalization
Normalization Definition (Wikipedia):
Normalization splits up data to avoid redundancy (duplication) by moving commonly repeating groups of data into a new table. Normalization
therefore tends to increase the number of tables that need to be joined in
order to perform a given query, but reduces the space required to hold
the data and the number of places where it needs to be updated if the
data changes.
Number of sheets / sheet objects
Remove hidden sheets and sheet objects that are not being used
Remove unused variables
Remember to keep your QlikView document as trim as possible
8.1.2 Document Design
Avoid Show Frequency
Forcing Proper Object Display
Calculation Conditions
Show Conditional
Avoid too many active objects on the same sheet
Minimized objects consume no resources, use them whenever appropriate
8.1.3 Security Impacts - Making the Right Choices
QlikView Publisher Security
Section Access
Publisher Security
Breaking up one large QlikView file into multiple smaller files based on row
level security
Effective for memory management if security profiles do NOT contain much
overlapping data
Section Access
Dynamically reduces the QlikView file at logon based on user authorization
82
83
replaced by transforming them. A typical example is aggregating quantity*price where price is variable. This can be handled by "extended interval
match". If two conditions, e.g. " A AND B " are to be satisfied the test
might be replaced by a condition "C".
Sort text
QlikView automatically evaluates if a Field is to be treated as numeric, text
or general. Fields evaluated as text will be sorted as text which is the slowest
sort operation. This can be replaced manually to sort by load order.
Dynamic captions and text objects
Expressions can be entered almost anywhere that you can enter text. The
evaluation of an expression is however dependent on its environment.
Expressions in charts and straight-and pivot-tables that are defined in the
expressions dialog are embedded and only calculated when the object is
active. For instance they are not calculated when the object is minimized.
On the other hand if the object title is calculated this calculation is performed every time any change occurs. We also have numerous ways of
defining show conditions, calculation conditions etc. These tests will also be
performed at all times. Some expressions are more expensive than others
and of course become more expensive the more frequently they have to be
evaluated. The introduction of asynchronous calculation has shifted the
behavior and these effects may have become more noticeable in your documents. The time functions e.g. Now(), Today() will be evaluated whenever a
recalculation has to be done. Especially the Now() function can become
quite costly since it causes a recalculation of the document every second. For
example
If ( ReloadTime()+3>Now(), 'Old Data', 'New Data')
As a simple test, put the expressions into textboxes. Then try sizing the textbox with Now() in it.
8.2.3 Adding Aggregatable Columns in to Your Script
It is sometimes good to add manual columns in to your script to give your
document the ability to sum up over these values. This will mean you can
then place the complex statement (If) in to your script and have a simple
sum in your dashboard object.
To do this you should simply place an IF THEN ELSE statement in to your
script that substitutes a database column or a 1 or 0 as a value to enable a
summing/count to take place.
84
Examples:
IF(ACTIVE='Y',1,0)
IF(ACTIVE='Y',sales_amount,0)
Forcing through a 0 then takes out all of the unnecessary / unwanted values
when you apply your SUM.
85
Exercises
Do:
In this exercise, we will tie together several Qlikview concepts, including:
QVD incremental loading
Binary Loading
Your production system is overloaded when QlikView pulls all history. The
CIO comes to you and asks that this stop now. How do you fix it?
1
2
Binary Load
1
Close any open QVWs. Create a new QVW and save it as CreateQVDfromBinary.QVW in the QVDs folder. Edit the script or click the
Qlikview File button in the Script Editor to add the BINARY load of the
BaseLineDataModel.QVW file you just saved in the same folder.
Your script should look like this.
Binary baselinedatamodel.qvw;
SET ThousandSep=',';
SET DecimalSep='.';
SET MoneyThousandSep=',';
SET MoneyDecimalSep='.';
SET MoneyFormat='$#,##0.00;($#,##0.00)';
SET TimeFormat='h:mm:ss TT';
SET DateFormat='M/D/YYYY';
SET TimestampFormat='M/D/YYYY h:mm:ss[.fff] TT';
SET
MonthNames='Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sep;Oct;
Nov;Dec';
SET DayNames='Mon;Tue;Wed;Thu;Fri;Sat;Sun';
87
4
5
6
88
do while j > 0
let d = TableName(0);
drop table $(d);
let j = NoOfTables();
loop
Reload and save the file and look in the Datasources\QVDs folder.
You should see:
Close any open QVWs. Create a new QVW and save it as MyIncrementalQVDApplication.QVW in the QVDs folder.
Open the script editor. On the Main tab create a connection to the
QWT.MDB data base.
Next edit the script to load all the previously generated QVD files except
Orders and Shipments. Create a new script tab called Load QVDs and
load the QVDs by clicking the Table Files button, navigating to the
Datasources\QVDs folder, and choosing the first QVD file. Repeat the
process until all QVD files except Orders, and Shipments have been
added to the script. Dont forget to format your script according to best
practices and remember to explicitly name each table. Save your work
frequently.
Create a new script tab called New Orders and add the following code
to the script:
SQL SELECT
*
FROM
Orders
WHERE
year(OrderDate) > 2006
ORDER BY
OrderDate ASC;
Create another new tab called New Order Details and add the following
code to the script:
OrderDetails:
LOAD
LineSalesAmount - CostOfGoodsSold AS Margin,
*
;
LOAD
OrderID & '-' & LineNo AS OrderLineKey,
Discount,
OrderID,
ProductID,
LineNo,
Quantity,
UnitPrice,
UnitPrice * Quantity * (1 - Discount) AS
LineSalesAmount,
applymap('UnitCostMap', ProductID, 0) * Quantity AS
CostOfGoodsSold
WHERE
exists(OrderID, OrderID)
;
SQL SELECT * FROM `Order Details`;
89
Create another new tab called New Shipments and add the following
code to the script:
Shipments:
LOAD
OrderID & '-' & LineNo AS OrderLineKey,
ShipmentDate
WHERE
exists(OrderID, OrderID)
;
SQL SELECT
*
FROM
Shipments
ORDER BY
ShipmentDate;
10 Add another new tab called Load Baseline QVDs and load the information from the Orders.QVD file we created by clicking the Table Files
button and choosing the Orders.QVD file. Add the CONCATENATE
keyword to the LOAD statement and remember to explicitly specify the
table into which you are loading the baseline Orders data.
90
12 Add the following code after Shipments on the Load Baseline QVDs
tab. This code calculates an OrderSalesAmount and joins it into the
Orders table.
LEFT JOIN (Orders)
LOAD
OrderID,
sum(LineSalesAmount) AS OrderSalesAmount
RESIDENT
OrderDetails
GROUP BY
OrderID;
91
13 Last, create a Calendar tab (note that Calendar comes after our QVD
tab) and add the following code to the script:
//set this variable to define dates to generate
Temp:
LOAD
min(OrderDate) AS minDate,
max(OrderDate) AS maxDate
RESIDENT
Orders;
LET varMinDate = Num(Peek('minDate', 0, 'Temp'));
LET varMaxDate = Num(Peek('maxDate', 0, 'Temp'));
DROP TABLE Temp;
// varMinDate to varMaxDate
TempCalendar:
LOAD
$(varMinDate)+Iterno()-1 AS Num,
Date($(varMinDate)+Iterno()-1) AS TempDate
AUTOGENERATE 1 WHILE $(varMinDate)+Iterno()-1<=
$(varMaxDate);
//Building the master calendar with most date
dimensions
MasterCalendar:
LOAD
TempDate AS OrderDate,
week(TempDate) AS Week,
Year(TempDate) AS Year,
Month(TempDate) AS Month,
Day(TempDate) AS Day,
Year2date(TempDate)*-1 AS CurYTDFlag,
Year2date(TempDate,-1)*-1 AS LastYTDFlag,
inyear(TempDate,Monthstart($(varMaxDate)),-1) AS
RC12,
date(monthstart(TempDate), 'MMM-YYYY') AS
MonthYear,
applymap('QuartersMap', month(TempDate),
null()) AS Quarter,
Week(TempDate)&'-'&Year(TempDate) AS
WeekYear,
weekday(TempDate) AS WeekDay
RESIDENT
92
TempCalendar
ORDER BY
TempDate Asc;
DROP TABLE TempCalendar;
Here we created a temporary table (Temp), found the minimum and
maximum OrderDates, and used the peek() function to find the contents of the two fields on the only row of this table.
Note also that we could have done one more Resident Load to reorder
the new, larger Orders table ascending and then used the original peek()
functions to set our min/max date variables. That may have required us
to change a table name and then drop the intermediate table after setting
the variables a minor complication.
14 Now reload the document and create a List Box for Year. All the information is in the document with Order, OrderDetails, and Shipment
information prior to 2007 coming from our baseline QVDs and any new
data going forward coming from a load from our production system.
Whats Missing in all this? You dont normally incrementally update
data by Year; you do it every night, every 3 hours, or possibly on
demand. We havent saved our newly updated Orders, OrderDetails,
and Shipments tables (years 2007 -> 2010) for use in the next update
cycle. Our parameters must be determined by when the QVD files were
last reloaded; read the excellent Qlikview Help article entitled QVD
files and Incremental Load. We need to create regularly scheduled processes that run our updates (with say, Qlikview Server as a starting
point). Perhaps we should just do this for all our tables. We further
assumed Orders, OrderDetails, and Shipments were not being changed
or cancelled or that new Orders, OrderDetails, and Shipments were not
being added into our earlier date ranges; thus, a CreateDate or LastModifiedDate would be handy to have in the data. The Help topic covers all these cases in more detail.
Notes
For development purposes or extended data manipulation, the binary load
operation may come in handy. Only a single binary load statement is
allowed in a script.
For loops and do while/until loops are common script instruments for
designing automation of repetitive script operations.
93
Remember: Storing historical data that does not change into QVD files
and then loading that data together with only recently changed data from
other data sources is a very effective way of :
optimizing reload speed
reducing impact on source systems
allowing for multiple documents to access a single fast access
source of common QVD data
The File | Reduce Data option is a fast and flexible way of producing a subset of data based upon your current selection or dropping all data from
memory during development.
94
9.1 Overview
Sometimes you will find yourself in a situation where you need to create
daily balance information from some sort of transaction table, even for days
when there are no transactions.
In other situations, you might need to record a balance in some sort of transaction table based on the transactions themselves, such as warehouse transactions in/out when there might not be transactions made on all days and
keys.
With QlikView you can handle these situations.
SET
SET
SET
SET
SET
SET
SET
SET
SET
SET
ThousandSep=' ';
DecimalSep=',';
MoneyThousandSep='.';
MoneyDecimalSep=',';
MoneyFormat='#.##0,00 kr;-#.##0,00 kr';
TimeFormat='hh:mm:ss';
DateFormat='YYYY-MM-DD';
TimestampFormat='YYYY-MM-DD hh:mm:ss[.fff]';
MonthNames='jan;feb;mar;apr;maj;jun;jul;aug;sep;okt;nov;dec';
DayNames='m;ti;on;to;fr;l;s';
95
tmpResident:
Load Key,
Date(Date,'YYYYMMDD') as Date,
Balance
FROM [datasources\data Balance.xls] (biff, embedded labels, table is
[Sheet1$])
;
// Variables to set first date and number of dates to generate
Let varStartDate=Num(Yearstart(today())-1);
Let varNoOfDays=Num(today())-$(varStartDate);
// Connects all Keys to all dates within the date range set by
variables
// Generates lots of rows i.e. 10 000 keys * 1 000 days = 10 000 000
rows
tmpJoin:
Load Distinct
Key
Resident tmpResident
Order By Key
;
Join
Load
Date($(varStartDate)+(RecNo()),'YYYYMMDD') as Date
Autogenerate today()-$(varStartDate);
96
Final:
Load distinct
%Key,
BalanceDate,
[Reg Balance], // Just for demo
If([Reg Balance]='x',Peek([Daily Balance]),[Reg Balance]) as [Daily
Balance]
;
Load
Key as %Key,
Date as BalanceDate,
Applymap('mapValue',Key&Date,'x') as [Reg Balance]
Resident tmpJoin
Order By Key, Date;
Drop table tmpResident, tmpJoin;
97
98
Note: you can review the source data and solution in:
c:\QlikViewTraining\DeveloperII\Chapter09
Inventory Balances based on Transactions.qvw
datasources\data trans.xls
99
10 QLIKVIEW SECURITY
Objectives
101
USERID
PASSWORD
SERIAL
NTNAME
102
NTSID
OMIT
QlikView will compare the QlikView serial number with the field SERIAL,
the Windows NT User name and groups with NTNAME, the Windows NT
Domain SID with NTDOMAINSID and the Windows NT SID with NTSID.
It will further prompt for User ID and Password and compare these with the
fields USERID and PASSWORD.
If a valid combination of user ID, a password and environment property is
also found in the Section Access table, then the document is opened with the
corresponding access level. If not, QlikView will deny the user access to the
document. If the user ID and/or the password are not entered correctly
within three attempts, the entire logon procedure must be repeated.
In the logon procedure, QlikView will first check SERIAL, NTNAME,
NTDOMAINSID, and NTSID to see if this information is sufficient to
grant the user access to the document. If so, QlikView will open the document without prompting for USERID and PASSWORD. If only some of the
access fields are loaded, actions appropriate to the missing data are taken,
e.g., prompts for more information.
All the field names listed in LOAD or SELECT statements in the section
access must be written in UPPER CASE. Any field name containing lower
case letters in the database will be converted to upper case before being read
by the LOAD or SELECT statement. However, the USERID and the PASSWORD entered by the end-user opening the QlikView documents are case
insensitive.
Section Access;
LOAD
* INLINE [
ACCESS, USERID, PASSWORD
ADMIN, NEWDBA, ABC
];
Section Application;
.....
103
104
105
Exercise
Do: Setting up Access Security on a QlikView file
1
2
3
4
5
6
7
8
Launch QlikView and save a working copy of the QlikView file in the
working directory for this chapter (C:\QlikViewTraining\DeveloperII\Chapter10)
Once again, be sure to save the document under another Name. e.g.
QlikViewTraining_wSecurity.
Open the Script Editor.
Go to the File menu and Create Hidden Script
Enter the password hidden and confirm the password.
Choose Section Access | Inline from the Insert menu.
Select Basic User Access Table so the ACCESS, USERID and PASSWORD are checked.
Click OK and add the following lines to the table.
107
Click OK. The wizard should have created the following script statement.
Section Access;
LOAD * INLINE [
ACCESS, USERID, PASSWORD
ADMIN, ADMIN, ADMIN
USER, USER, USER
];
Section Application;
Do:
1
Exit QlikView and open your newly named document again. You will
now see the following dialog box where you can enter your UserID and
Password.
108
Do:
Note: It is not possible to complete this exercise with QlikView Personal
Edition.
1
2
3
Make sure you are logged in to the QlikView document as Admin. (You
must have Admin rights to perform the following.)
Go to the Settings menu and Document properties.
Go to the Security tab and select what the Users should and should not
be allowed to do. Make sure that a User is not allowed to Reload the
script or to Save the document.
Make sure to check the Admin Override Security check box so that the
Admin always has permissions to do everything.
109
Close the document, exit QlikView and open it again, and log on as a
User. Note that you will not be able to Save the document or Reload the
script.
Tip: There is a Security tab in the Sheet Properties dialog as well where
you can set security for the current sheet and apply to all sheets.
Do:
1
2
3
SERIAL
2300 2394 7111 8000
2300 2394 7111 8001
It can be seen that only two license numbers have access to the document
we have created, one with User rights and one with Admin rights. Also,
110
note that we are adding restrictions to existing UserIDs (User), and not
replacing current restrictions.
Open the load script, position the cursor and click on the Table Files
button. Load the newly created table Access02.txt after the inline
table, and prior to the SECTION APPLICATION statement, as shown
below.
Access02:
Load
COMPUTER,
SERIAL
FROM Datasources\Access02.txt
(ansi, txt, delimiter is '\t', embedded
labels);
Section Application;
6
7
Assuming that you have not entered your serial number in both records in
the Access02.txt file, you may only log in as a User or Admin. It would be
easy to add a third line to the access control tables which always gave every
serial number User rights, assuming a valid User ID and Password. To do
this, we can add a third computer (e.g. Course3) and enter * as the value in
the Serial field.
111
112
fields, but it is generally preferable to use null for connecting fields, since
this will allow access to ALL data, regardless of whether it has a logical connection to the connecting field.
Do:
1
2
3
4
5
Open the load script and File - Edit Hidden Script (note that you must
have Admin privileges to edit the hidden script). Verify that your cursor
is positioned on the Hidden Script tab.
Now, comment the previously loaded INLINE tables so that they will not
interfere with our new SECTION ACCESS tables.
The first text file to load is SalesSecurity.txt, located in the Datasources
directory. Label this logical table as Access01.
Then, add the SECTION APPLICATION statement.
Next, create a load statement for the SalesInitials.txt file, located in the
Datasources directory. It is good practice to use the Upper function
against the connecting field (SP), since the value must be uppercase to
match the value from SECTION ACCESS. The new statements should
resemble the following.
Section Access;
Access01:
Load
[USERID],
[ACCESS],
SP /* Connecting field for data reduction
*/
FROM Datasources\SalesSecurity.txt
(ansi, txt, delimiter is '\t', embedded labels);
Section Application;
Access_Application:
Load
upper(SP) as SP, /* Connecting field for
data reduction */
[SalesPerson]
FROM Datasources\SalesInitials.txt
(ansi, txt, delimiter is '\t', embedded labels);
6
7
8
113
Go on to the Security tab and make sure that a User cannot edit or
reload the script. Save should not be allowed either (uncheck the checkboxes Edit Script, Reload, Partial Reload, Save Document and Allow
User Reload).
10 Save again and exit the document.
11 Open the document again and log on with Leif as UserID. Notice that
you can view data for this user, as well as the Sales Persons, Tom Lindwall and Frank Roll.
12 Close the document and open it again, this time using James. You will
now be able to see all data again.
114
So far we have focused on loading data using the different options on the
Data page of the script editor.
115
116
117
12.1 Scenario
ACME Inc., is a food & drink distributor selling to different types of stores
across the United States and also internationally. They are interested in getting a better understanding of their Sales and Margin performance across
their business.
They have asked you to develop a Sales Analytics QlikView document for
them.
The document will be used by their top executives, their product managers
and also their salespeople.
119
Requirements
ACME has listed their requirements for the solution as follows:
3
4
They are interested in analyzing their delivery performance how they are
delivering compared to what they have promised.
Note: they count each invoice as a delivery so use the max promised date
and actual delivery date for each invoice. They only are interested to analyze delivery performance for 2008.
Describe how could they could easily distribute this information across
their different user groups. That is to say, different user groups want to
analyze the same data but from different perspectives.
121
Field#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
122
Field Name
Actual Delivery Date
Address Number
CustKey
Description
The Actual date of Delivery
Not used
A unique ID of the Customer.
Used as a key field
Backlog Amount
The backlog amount of an
order
BackOrder Amount
Amount on Backorder
DateKey
Not Used
Discount Amount
Discount Amount
Doc Type
The Document Type
RI = Invoice
RM = Credit Invoice
Family Class
Not Used
Invoice Date
The date of the Invoice
Invoice Number
The unique identifier of an
Invoice, used as a Key field
Item-Branch Key
The Key field to link to the
Item Branch Table
Item Class
Not Used
Item Number
Not Used
Line Desc 1
The Description of the Item
Line Number
The Line Number on the
Invoice
Line Type
Not Used
List Price
The List price for Item
Order Number
The order number
Parent Address Number
Not Used
Payment Terms
The payment Terms
Promised Delivery Date
The date the order was promised for Delivery
Sales Amount
The Actual Sales Line
Amount
Sales Amount Based on List Price The Sale Line Amount based
on the List Price
Sales Cost Amount
The Actual Sales Cost
Amount
26
27
28
29
Sales Price
Sales Quantity
Sales Rep
30
31
32
Unit Price
12.2.2 BUDGET.QVD
This table contains the budget data.
Field#
1
Field
BudgetPeriod
2
3
4
Budget Amount
BudgetYear
Customer Number
textBudgetPeriod
Description
The budget period 1 = January 12 = December
The budget Amount
The Year of the Budget
The CustomerID Used as a
key field
The budget period as text
January, February etc.
12.2.3 CUSTOMERS.QVD
This table contains Customer information.
.
FieldNo
1
Field
Address Number
2
3
4
5
Business Family
Business Unit
Customer
Customer Number
Customer Type
Description
A unique address number for
the customer. Used as a key
field
Not Used
Not Used
The Name of the Customer
The unique Customer ID used
as a Key Field
Not Used
123
Division
8
9
10
Line of Business
Phone
Region Code
11
12
12.2.4 CUSTOMERADDRESS.QVD
This table contains the address information for the customer.
Field#
1
Field
Address Number
2
3
4
City
Country
Customer Address 1
5
6
7
8
Customer Address 2
Customer Address 3
Customer Address 4
State
Zip Code
Description
A unique address number for
the customer. Used as a key
field
The city
The Country
The address of the customers
The address of the customers
The address of the customers
The address of the customers
The State / Province code
used as a key field
The Zip Code / Postal Code
of the customer
12.2.5 STATEDESCRIPTION.QVD
This table contains the state descriptions.
124
Field#
1
Field
State
2
3
state_abbr
state_name
Description
The State / Province code
used as a key field
Abbriviated state name (code)
The name of the state/province
12.2.6 REGION.QVD
This table contains the Region descriptions.
Field#
1
2
Field
Region Name
Region Code
Description
The Name of the Region
The region code. Used as a
Key Field
12.2.7 DIVISION.QVD
This table contains the Division descriptions.
$Field#
1
2
$Field
Division Name
Division
Description
The Name of the Division
The Division code. Used as a
KeyField
12.2.8 SALESREP.QVD
This table contains information about SalesReps.
FieldNo
1
Field
Sales Rep
Description
The salesrep code. Used as a
key field
The Name of the Sales Rep
12.2.9 ITEMBRANCH.QVD
This table contains Item Branch information. Main purpose is to link to the
ItemMaster table
FieldNo
1
2
3
Field
Item Branch 2nd Item Number
Item Branch Category Code 6
Item Branch G/L Category
Item-Branch Key
Short Name
Description
Not used
Not used
May be used by Finance
department
Item Branch key. Used as a
Key field
The reorder point at the
branch
Used a Key Field (unique Item
Identifier)
125
12.2.10 ITEMMASTER.QVD
This table is the Item Master table.
$Field#
1
$Field
Master Planning Family
2
3
Product Department
Product Group
Product Line
5
6
7
8
Product Type
Short Name
Description
The planning family for the
item
Not Used
The product group code.
Used as a key Field
The product Line code. Used
as a key Field
Not Used
The product sub group code.
Used as a key Field
Not Used
Used a Key Field (unique Item
Identifier)
12.2.11 PRODUCTGROUP.TXT
This table contains the Product group descriptions.
.
$FieldN
#
1
$Field
Description
Product Group
Field
Product Line
Description
The product Line code. Used
as a key Field
The Description of the product line
$Field#
126
$Field
Description
127