Sunday 6 December 2015

Issues with Order By, Joins and Previous Function

Key Point 1: If you apply a Join to a table you have previously Sorted using ORDER BY, the table no longer retains the order by, even if for example it's a left join which doesn't add any extra rows, just an extra field at the end.

It makes sense in hindsight, but did cause me confusion and a lot of time to resolve.

Key Point 2: Previous function looks at the previous record that was passed to the Load statement from the source - even if the previous function failed a Where clause and was not processed.



For one of my applications we have customer forecasts which look something like this:

So Customer, Date and Quantity.  In the Qlikview application the forecast needs to be spread across the days between each forecast.  So the 1st Nov for Customer abc should be spread evenly across the 2nd, 3rd, 4th, 5th and 6th November (as 1st and 7th) are weekends.

I've had a QVW script which does this by first sorting the table by Customer and Forecast Date descending (last forecast to first).

Then I have a for loop which runs 0 to 30.  For each record it checks if the Forecast Date + Counter is less than the Previous(Forecast Date).  If it is a "Forecast Spread Date" is set to be Forecast Date + Counter, otherwise Null.  The Nulls are later deleted.  There is special rule for first (ie. last forecast date) record per customer which spreads over 7 days.

So for customer abc, the first date to process is 6/12/2015.  This gets spread over a week.  2nd record is 29/11/2015.  It checks that 29/11/2015 + 0 = 29/11/2015 < 6/12/2015 so keeps that date.  29/11/2015 + 1 also till 29/11/2015 + 7 is not < 6/12/2015 so doesn't keep that date.

This all worked fine, although probably a better way to do it.

Recently a user asked for something extra.  They wanted to count how many forecasts exist in the month for the customer, and use that in some formulas.

To solve between the Order By load and the for loop, I added a [Forecast Month] field to the table and an extra step:

FC_In_Month:
LOAD
  Customer,
  Forecast_Month
  Count(Distinct Month(Forecast Date)) as FC_In_Month
RESIDENT Forecast
GROUP BY Customer, Forecast Month;

LEFT JOIN (Forecast)
  Customer,
  Forecast_Month
  FC_In_Month
RESIDENT FC_In_Month;

This adds an extra field (no new rows though) FC_In_Month.

But after doing this the for loop and date logic didn't work.  I eventually worked out that the Left Join back to the Forecast table had corrupted the sort order that was put in.  To resolve I just resorted again after the join and all okay.  But something I didn't think of at the time when added the extra step.


The other problem came about because there is actually an extra bit of info in the table.

Forecasts can be Firm or NonFirm and can have same date.  The For Loop described above is only interested in NonFirm records - the Firm records are handled elsewhere.

The Load statement that runs within the Loop has a clause Where Type = "NonFirm".

As mentioned above the test is that the Forecast Date + Counter < Previous(Forecast Date).  Problem I had was with the 15th.  Working backwards by date, the first record it came across was the Firm.  The Where clause excluded this record.  It then moved to the Non-Firm record.  The Previous(Forecast Date) was the 15th - the excluded Firm record and so the test failed for the NonFirm record as well as 15/11/2015 + 0 is not < 15/11/2015.

Solution was to create a separate pre-filtered table so that what I fed into the Load statement in the For Loop only has NonFirm.  This worked, but it's not a great solution.

Both of these make sense when I think about them - even obvious.  But it was hard to see why it went wrong and hard to find.  The Previous issue had been in the code and although not common in the data was actually presenting incorrect data to end users.  Was only when testing my before and after that I noticed.


FINAL NOTE: "order by is a clause used for sorting the records of a resident table before they are processed by the load statement".  Never chased down that rule that will the input table to the load statement before processing.  Previously I've often sorted the table earlier or created an interim table_SORTED version.  Too lazy to check, but it is okay to rely on it in the load statement (where clause notwithstanding).


Wednesday 18 November 2015

Qlikview Management Console - Folder and Document list sorting

Our instance of qlikview has a lot of Documents and folders containing them.  We have setup a structure where each report has a sub folder of it's own with an application, data, config folder etc loosely based on the Qlikview Deployment Framework.

In the management console where setup Qlikview Server to find the folders we list out each report folder as a separate Mounted Folder rather than pointing to a top level.  This allows more customisation of structure in Qlikview.  eg.

We try to name the folders in a meaningful way, eg. Admin_...., Beverage_....., Procurement_.....  A problem we came across when setting this up was that as you add new folders, the simply go to the bottom of the list, rather than being sorted alphabetically.  Finding a relevant folder, either here or in the "Documents" tab is difficult when they run to 150+ reports.

I've not been able to find a way in Qlikview to get it to sort the folder names - at least not a standard method.  But have found the following process to allow it.  NOTE: Only done this on our test server as a check - haven't validated if there are any adverse consequences.  This blog is my instruction manual if / when decide to replicate to production.

a) Folder listings:
Management Console -> System -> Setup -> Qlikview Servers -> our Server -> Folders.  This is the list of folders on disk that contain .qvw files to be referenced.  Our Root Folder is a dummy folder as we are doing everything through Mounted folders.

To re-sort the mounted folder listing I found in this file:

"C:\ProgramData\QlikTech\QlikViewServer\Settings.ini" which controls the settings for Qlikview Server including the Mounted folder list.

Within that list is a row for "Document Mounts" that looks like this:


Each Mounted folder has three fields separated by comma: Mounted Name, the folder on disk and a Browserable (1) or not (0) flag.  A semi-colon seperates each Mounted folder.

I (after taking copies of the file as backup) copied the DocumentMounts= row out to Excel ( which is good for this type of thing ), split into multiple rows and fields.  I then sorted the rows and recreated the string based on the format that came out.

I pasted the string back into the settings.ini file and restarted services.  And it worked, the folders had re-aligned themselves.  Of course when I add the next one, will have to do it all again.


b) Document Listings.

Similar problem with the Documents -> User Documents tab.  I had hoped changing the folders list would copy across to here, but didn't appear to happen - or I didn't wait long enough for it to take effect?

To resort this one went to the QVPR dataset at

"C:\ProgramData\QlikTech\ManagementService\QVPR" folder.  There are a number of xml files here that control the settings (although you can do in a SQL server apparently).  The one of interest was "SourceDocumentFolderResource.xml".

Apart from some settings it lists a series of SourceDocumentFolderResource rows, one for each folder listed.  So within a text editor I simply sorted the rows alphabetically, restarted the Services (twice as first time didn't go so well), and voila, sorted list.

There should be minimal risk with these things as if you break the files, you can simply delete them (or return you previously stored copies) and Qlikview should re-generate them from a default.  The QVPR keeps .bak versions also of the xml.


Tuesday 8 October 2013

My UNIX Cheat Sheet

Our SAP BW and also some ERP systems sit on Unix platform. Occasionally I have to work with files directly on these and this is my cheatsheet for using Unix.

Commands:

dir and ls
dir gives detailed directory list. ls just lists the files / folders.

cd (change directory)
folder delimiter in Unix is "/" not "\" as with windows.

cd ../ returns one folder up.
cd /xxx/xxx will go to that folder.  If start with the / thats the root directory (I think)
cd xxx will move down tree to folder xxx from current directory (if exists).

mv (move)
moves files and/or renames them.
mv [src] [target]
src is the source file, or wildcard.  eg. * is all in current directory, *.csv is all csv folders
target

rm (remove)
removes files.
rm [tgt]
removes the tgt file(s).  Will be in current directory unless otherwise specified.

gzip / gunzip
gzip / qunzip [tgt]  will zip or unzip the tgt.  tgt can be wildcard.
Note sure if can put multiple files into same .gz file.  Presume so, but not sure syntax.

chmod (security)
files / folders have various security levels in Unix.  chmod allows you (if can) to modify the settings for file(s).
eg. chmod 666 [tgt] will give read / write access to the file.

head and tail (show top / bottom lines of file)
head [filename]  will show first 10 lines of file.
head -n 20 [filename].  will show first 20 lines of file.
tail will do from bottom.

sed (  (s)tream (ed)itor - special editor for modifying files)
Lots of sub commands: this page looks useful.
http://www.grymoire.com/Unix/Sed.html

deleteing with sed.
to simply delete a particular line use sed 2d where 2 is the line to delete.

od (octal data)
Display's filestring in octal characters.  Useful to see if have non-printable characters in a file.
eg. "od -b xxx.csv | more"
Came in handy when looking at Qlikview generated csv files, prepends non-readable characters which was throwing out reads.



Thursday 20 June 2013

Qlikview Actions - Launch another application (eg. Open Excel and file)

Neat little trick in Qlikview.

A Qlikview report reads from an Excel file to get some data.  User asked if could add a link to open that Excel file (so they can check source / update).

Created a Button and added External Action -> Launch.

Application = "Excel".
FileName = "\\.....xlsx".

Was pleasantly surprised that simply putting in Excel was sufficient to identify the application.  Thought it may have needed a full path to an .exe which may have been problematic as Excel.exe might not be in the same place on all users computer.

Sunday 16 June 2013

Qlikview Lookup Function

A quick note on the Lookup Function

From the help screen

lookup( fieldname, matchfieldname, matchfieldvalue [, tablename] )

Returns the value of fieldname corresponding to the first occurrence of the value matchfieldvalue in the field matchfieldname.
Fieldname, matchfieldname and tablename must be given as strings (e.g. quoted literals).
The search order is load order unless the table is the result of complex operations such as joins, in which case the order is not well defined.
Both fieldname and matchfieldname must be fields in the same table, specified by tablename. If tablename is omitted the current table is assumed.
If no match is found, null is returned.

Example:


lookup('Price', 'ProductID', InvoicedProd, 'pricelist')


I occasionally have issues using this function - mainly as I try to avoid it anyway.  Couple of things from trial and error:

  • The field names with the ' ' literal wrapper.  DO NOT wrap them with [ ] inside the ' ' even if the field usually needs them.  It will confuse Qlikview and won't find your match.
  • As it says, should always use the tablename, as usually not trying to use the current table name.

Thursday 13 June 2013

Qlikview Chart kept flashing and going back to top of scroll - use of Now() function

I have a chart which worked okay, except for it kept flashing and whenever scrolled down to see more values, it jumped back to the top.  Couldn't work out why.  The data model it uses is a bit messy with fields from three different tables getting mixed together, plus set analysis and other calculations.

Did several changes, including to data model, but still couldn't resolve, until checked out something.

The chart lists customers won in last few months.  By default the document has selected month being only the last month, so the chart uses set analysis to extend.  It uses a variable to determine the start month which originally was MonthStart(Min(Date_Month),-3) to get the starting date for set analysis.

Then user wanted instead of last 3 months, back to start of the fin year (or possibly other customised fields).  I setup another variable to calculate how many months were needed to go back to include the start of the year.  For that variable I used Month(Now()) to work out which month we were in and work back from there.

That was the problem.  By including Now() in the variable, Qlikview constantly recalculated that variable - once every second I guess - and the set analysis calculation in the chart.  Hence the flashing and the scrolling issue.  I've replaced Now() with Today() so now will only cause a problem once a day, and only if user is up late.

Probably better methods to get the same result, but important takeaway was using Now() has risks.

Monday 3 June 2013

Some SAP BW Examples

I work with SAP BW as the datawarehouse feeding our Qlikview reporting.  I've picked up a fair bit of SAP BW and ABAP over time, but struggle to quickly remember things that I have to do oonly occasionally - always have to go back to searching out methods.  This is how I will remember - by including some examples.


   Get CALWEEK from CALDAY or

Using RST_TOBJ_TO_DERIVED_TOBJ function.
 

REPORT Z_MICHAEL.
constants: rs_c_true           TYPE rs_bool      VALUE 'X'.
data: lv_date type d.
data: l_chavl(60)        TYPE c,
      l_chatt(
60type c"rsd_chavl,
      lv_week 
type /bi0/oicalweek.

lv_date = sy-datum.

DO 31 times.
  lv_date = lv_date + 
1.
  
write / lv_date.
  
CLEAR l_chavl.
  l_chavl = lv_date.

  
CALL FUNCTION 'RST_TOBJ_TO_DERIVED_TOBJ'
    
EXPORTING
      I_TIMNM_FROM = 
'0CALDAY'
      I_TIMNM_TO = 
'0CALWEEK'
      i_timvl   = l_chavl
      i_fiscvarnt = 
'Z6'
      i_buffer           = rs_c_true
    
importing
      e_timvl = l_chatt
    
EXCEPTIONS
      incompatible_tobjs = 
1
      no_input_value     = 
2
      fiscvarnt_missing  = 
3
      input_not_numeric  = 
4
      wrong_date         = 
5
      wrong_fiscper      = 
6
      x_message          = 
7
      
OTHERS             = 8.


  
write l_chatt.
ENDDO.