There is a number of ways to establish bi-directional integration between QlikView and external systems. Some of them employ web-services. In this case a web-service is a helper application which sits between QlikView and external application (or is part of that external application) and communicates using web-protocols. Web-services can be written in any popular programming language (PHP, Java, C#, Python, Ruby, Go etc.) and hosted using popular web servers like Apache, IIS or nginx or any other of your choice.
Let's consider several architectures:
Showing posts with label hints and tips. Show all posts
Showing posts with label hints and tips. Show all posts
January 19, 2013
December 26, 2012
Using Dynamic Updates in QlikView 11
Dynamic Updates is another one feature that makes difference between QlikView and other popular BI tools even bigger (see also "Really, is QlikView a BI tool?"). If you haven't heard about it -- it allows adding new or modifying existing data right in the in-memory data model of a QlikView application without re-running loading script. While this feature can be used (to some extent) for real-time updates, in my opinion the main benefit from it lies in a different area -- it enables creating closed-loop workflow with operational applications. It means that not only can you take data from operational applications (e.g. Salesforce or SAP), but return some user-generated output from QlikView into these applications as part of a single workflow. For example, in one of my applications a user can get a list of pending orders from logistic system, create batches from these orders based on some filters and selections in QlikView and then return these batches for delivery into the logistic application.
Dynamic Updates can be performed in two ways -- using VB macros (starting from version 10) and using Actions (starting from version 11). Using actions is a more preferable way (as VB macros are losing more and more favor with QlikTech). Unfortunately, documentation about Dynamic Update Actions is very scarce and there are lots of limitations and side effects, so I tried to collect here some practical hints and tips how it can be used:
Actions utilize SQL-like syntax (however very very limited). E.g.:
INSERT INTO * ('Batch ID', 'Order ID') VALUES (1, 'ABC123');
UPDATE Batches SET WrongBatch = 1 WHERE "Order ID" = null();
DELETE FROM Batches WHERE -1;
These commands look like SQL but here similarities end. Some of limitations:
$(=concat(DISTINCT 'INSERT INTO * (OrderID) VALUES ($(vNewBatch),'&[Order ID]&')', ';'))
On QlikCommunity there is excellent demo application made by Matthias Dix that shows basic Dynamic Update commands and their equivalent using VB macros (full thread here).
You should also keep in mind that there are some significant side effects from using Dynamic Updates which often lead to unpredictable behavior. This article explains very well these side effects and how to deal with some of them. I struggled with failed variable assignments (Set Variable action) in the same action batch with Dynamic Update, unexpected freezes, and that any Dynamic Update command for some reason triggers all OnSelect actions. Finally I came to this workaround:
UPDATE
Keep in mind that for server-hosted applications all changes made by Dynamic Update apply immediately for all users that are working with the applications. It means that you need to make sure that actions of different users working simultaneously won't interfere. It can be achieved, for instance, by adding user name for each record created by Dynamic Update and then using set analysis expressions to limit records to only those which are required for the user. But in any case, extra care should be taken to manage correctly all possible concurrency cases.
Dynamic Updates can be performed in two ways -- using VB macros (starting from version 10) and using Actions (starting from version 11). Using actions is a more preferable way (as VB macros are losing more and more favor with QlikTech). Unfortunately, documentation about Dynamic Update Actions is very scarce and there are lots of limitations and side effects, so I tried to collect here some practical hints and tips how it can be used:
Actions utilize SQL-like syntax (however very very limited). E.g.:
INSERT INTO * ('Batch ID', 'Order ID') VALUES (1, 'ABC123');
UPDATE Batches SET WrongBatch = 1 WHERE "Order ID" = null();
DELETE FROM Batches WHERE -1;
These commands look like SQL but here similarities end. Some of limitations:
- You can use equal sign (=) in WHERE clause. You cannot use greater (>) or less (<) signs or not equal (<>)
- You can combine conditions using AND or OR in WHERE clause. You cannot use NOT
- Check for nulls is done differently than in ANSI SQL (see example above)
- Notice different syntax for field names with spaces
$(=concat(DISTINCT 'INSERT INTO * (OrderID) VALUES ($(vNewBatch),'&[Order ID]&')', ';'))
On QlikCommunity there is excellent demo application made by Matthias Dix that shows basic Dynamic Update commands and their equivalent using VB macros (full thread here).
You should also keep in mind that there are some significant side effects from using Dynamic Updates which often lead to unpredictable behavior. This article explains very well these side effects and how to deal with some of them. I struggled with failed variable assignments (Set Variable action) in the same action batch with Dynamic Update, unexpected freezes, and that any Dynamic Update command for some reason triggers all OnSelect actions. Finally I came to this workaround:
- Put all variable assignments (Set Variable action) before Dynamic Update command otherwise new values of these variable won't be visible in expressions.
- Use some dummy command right after Dynamic Update to fight freezes, e.g. Set Variable for dummy (i.e. not used) variable, or even better ...
- ... use Selection -> Back action right after Dynamic Update command to compensate triggering all OnSelect events (won't have effect on VB Macros tied to OnSelect).
UPDATE
Keep in mind that for server-hosted applications all changes made by Dynamic Update apply immediately for all users that are working with the applications. It means that you need to make sure that actions of different users working simultaneously won't interfere. It can be achieved, for instance, by adding user name for each record created by Dynamic Update and then using set analysis expressions to limit records to only those which are required for the user. But in any case, extra care should be taken to manage correctly all possible concurrency cases.
Labels:
hints and tips,
QlikView
December 15, 2012
How to look inside resident tables at any point of loading script
What do you usually do when you need to look inside a resident table somewhere in the middle of loading script? I guess you store it as QVD, then load this QVD in a separate temporary QlikView application, then create table box or something else to browse the table.
Here is small trick how you can make your life easier using QViewer:
Insert in the beginning of the loading script this short snippet:
SUB INSPECT (T)
LET vPathToQviewer = 'C:\User\Dmitry\AppData\Local\EasyQlik\QViewer\';
STORE $(T) into [$(vPathToQviewer)temp_qvd.qvd] (qvd);
EXECUTE "$(vPathToQviewer)qviewer.exe" "$(vPathToQviewer)temp_qvd.qvd";
ENDSUB
And then simply insert calls to this function in any place of the loading script where you need to inspect table in its current state. For instance:
CALL Inspect('Orders');
When execution of the loading script will come to this line it will open QViewer showing the table. Script will continue running as soon as you close QViewer.
This is convenient technique that allows inspecting resident tables after every transformation like joins, concatenate loads, etc. You can have as many inspection points as you need during a single run.
A few more tips:
You may receive Security Alert from QlikView because of EXECUTE statement as depicted below. Just press Override Security button. Or you can change security settings in the bottom of the Script Editor window.
You can put this snipped into some file (e.g. debug.qvs) and then include this file when you need to do debugging of some application. Also you might want to save inspected tables under their own names in some temporary folder -- just modify the snippet accordingly or add a new similar function, e.g. InspectSave.
Any more ideas?
UPDATE 1
+Donald Hutchins made a good point -- he offered to store temporary QVD file not in QViewer's folder but in the working folder of the appliction which loading script is being executed. I think it makes a lot of sense because of less problems with access rights and better fit for parallel use. Here is his variant with my minor changes:
SUB INSPECT (T)
STORE $(T) into [$(QvWorkPath)\~$(T).qvd] (qvd);
EXECUTE "C:\.......\QViewer.exe" "$(QvWorkPath)\~$(T).qvd";
EXECUTE cmd /c del "$(QvWorkPath)\~$(T).qvd";
ENDSUB
UPDATE 2
+Matthew Fryer proposes one more variant of the Inspect sub in his blog QlikView Addict.
UPDATE 3
You can use EasyMorph for exactly the same purpose -- inspecting resident tables. Pros: with EasyMorph you can do more than just viewing a QVD -- you can filter, aggregate, calculate new columns, join other tables, etc. Also the free EasyMorph doesn't have the limit of 100K rows as the free QViewer does. Cons: EasyMorph opens QVD slower than QViewer as it needs to convert them to its internal format. Also it's less memory efficient when it comes to large QVDs.
The only difference in script would be additional /load key:
SUB INSPECT (T)
STORE $(T) into [$(QvWorkPath)\~$(T).qvd] (qvd);
EXECUTE "C:\.......\morph.exe" /load "$(QvWorkPath)\~$(T).qvd";
EXECUTE cmd /c del "$(QvWorkPath)\~$(T).qvd";
ENDSUB
Here is small trick how you can make your life easier using QViewer:
Insert in the beginning of the loading script this short snippet:
SUB INSPECT (T)
LET vPathToQviewer = 'C:\User\Dmitry\AppData\Local\EasyQlik\QViewer\';
STORE $(T) into [$(vPathToQviewer)temp_qvd.qvd] (qvd);
EXECUTE "$(vPathToQviewer)qviewer.exe" "$(vPathToQviewer)temp_qvd.qvd";
ENDSUB
And then simply insert calls to this function in any place of the loading script where you need to inspect table in its current state. For instance:
CALL Inspect('Orders');
When execution of the loading script will come to this line it will open QViewer showing the table. Script will continue running as soon as you close QViewer.
This is convenient technique that allows inspecting resident tables after every transformation like joins, concatenate loads, etc. You can have as many inspection points as you need during a single run.
A few more tips:
You may receive Security Alert from QlikView because of EXECUTE statement as depicted below. Just press Override Security button. Or you can change security settings in the bottom of the Script Editor window.
You can put this snipped into some file (e.g. debug.qvs) and then include this file when you need to do debugging of some application. Also you might want to save inspected tables under their own names in some temporary folder -- just modify the snippet accordingly or add a new similar function, e.g. InspectSave.
Any more ideas?
UPDATE 1
+Donald Hutchins made a good point -- he offered to store temporary QVD file not in QViewer's folder but in the working folder of the appliction which loading script is being executed. I think it makes a lot of sense because of less problems with access rights and better fit for parallel use. Here is his variant with my minor changes:
SUB INSPECT (T)
STORE $(T) into [$(QvWorkPath)\~$(T).qvd] (qvd);
EXECUTE "C:\.......\QViewer.exe" "$(QvWorkPath)\~$(T).qvd";
EXECUTE cmd /c del "$(QvWorkPath)\~$(T).qvd";
ENDSUB
UPDATE 2
+Matthew Fryer proposes one more variant of the Inspect sub in his blog QlikView Addict.
UPDATE 3
You can use EasyMorph for exactly the same purpose -- inspecting resident tables. Pros: with EasyMorph you can do more than just viewing a QVD -- you can filter, aggregate, calculate new columns, join other tables, etc. Also the free EasyMorph doesn't have the limit of 100K rows as the free QViewer does. Cons: EasyMorph opens QVD slower than QViewer as it needs to convert them to its internal format. Also it's less memory efficient when it comes to large QVDs.
The only difference in script would be additional /load key:
SUB INSPECT (T)
STORE $(T) into [$(QvWorkPath)\~$(T).qvd] (qvd);
EXECUTE "C:\.......\morph.exe" /load "$(QvWorkPath)\~$(T).qvd";
EXECUTE cmd /c del "$(QvWorkPath)\~$(T).qvd";
ENDSUB
Labels:
hints and tips,
QlikView,
QViewer
May 13, 2012
How to write reusable and expandable expressions in QlikView
One of the good practices in QlikView development is using reusable expressions. The most common case is when a repeating expression is replaced with dollar-sign expansion through the application. E.g.
$(eAmount)
where eAmount is defined like, for instance
LET eAmount = 'sum(Amount)';
If you don't use reusable expressions in QlikView you definitely should start doing it because of 2 reasons:
While this a generally good practice there are a couple of tricks to get even more value from it.
$(eAmount)
where eAmount is defined like, for instance
LET eAmount = 'sum(Amount)';
If you don't use reusable expressions in QlikView you definitely should start doing it because of 2 reasons:
- Improved maintainability -- you don't have to edit all occurrences of an expression in an application, but only once
- Better readability -- you have much better readability of your expressions because their (sometimes complex) logic is actually replaced with its brief explanation.
While this a generally good practice there are a couple of tricks to get even more value from it.
Labels:
hints and tips,
QlikView
February 13, 2012
How to draw circles and ellipses in QlikView
That's simple -- create new text object and set up its layout properties as below and voila!
| (clickable) |
Labels:
hints and tips,
QlikView
September 30, 2011
Using configuration files for QlikView applications
Often, development of QlikView applications requires (as any other IT project) several environments -- e.g. development, UAT, production, backup, etc. Usually, it means that QlikView application should connect to different databases, read/write QVD files to/from different directories, have different sets of users, etc. depending on environment. Changing or commenting/uncommenting parts of loading script every time is not so convenient way of doing things. Much more efficient approach is using custom configurations files. The general idea is to have different config files but the same QV app in each environment and read environment-specific settings from these config files into QV application. In this case it is possible just to copy application from one place to another place, without changing it. Also it helps to not mess up with version control, as you always have one version of application.
A configuration file can be a simple txt file, for example like this one:
Create it in Notepad, name it, say, myapp.cfg and put to the same directory with your QlikView application.
In the application load parameters from configuration file. Sample script:
Then you can use variables vDBName, vDBUser and vXPassword in database connection strings and vQBDPath in paths to QVD files.
Parameter Description can be used just for, well, description of a configuration file to distinguish one environment settings from another.
A configuration file can be a simple txt file, for example like this one:
Description=TEST environment
DBName=db_test
DBUser=db_user
XPassword=xxXpppAasssWwwOoorRrddDd
QVDPath='C:\QVD'
Create it in Notepad, name it, say, myapp.cfg and put to the same directory with your QlikView application.
In the application load parameters from configuration file. Sample script:
//Load configuration settings from config file which should be in the same directory as application
//Get application path
LET vAppFullPath = DocumentPath();
LET vAppName = DocumentName();
LET vAppPath = left('$(vAppFullPath)',index('$(vAppFullPath)','$(vAppName)')-2);
//Load config table
Config:
LOAD @1 as Parameter,
@2 as Value
FROM $(vAppPath)\myapp.cfg (txt, codepage is 1252, no labels, delimiter is '=', msq);
//Assign variables
LET QVDPath = lookup('Value','Parameter','QVDPath','Config');
LET vDBName = lookup('Value','Parameter',DBName','Config');
LET vDBUser = lookup('Value','Parameter',DBUser','Config');
LET vXPassword = lookup('Value','Parameter','XPassword','Config');
Drop table Config;
Then you can use variables vDBName, vDBUser and vXPassword in database connection strings and vQBDPath in paths to QVD files.
Parameter Description can be used just for, well, description of a configuration file to distinguish one environment settings from another.
Labels:
hints and tips,
QlikView
Subscribe to:
Posts (Atom)
