Do not input private or sensitive data. View Qlik Privacy & Cookie Policy.
Skip to main content

Announcements
Qlik Open Lakehouse is Now Generally Available! Discover the key highlights and partner resources here.
cancel
Showing results for 
Search instead for 
Did you mean: 
Not applicable

Looping through dates with specific intervals

Hello,

I have a table of dates that looks like this:

Cust_No   Order_Date

---------------------------------

1               01/01/2013

1               02/01/2013

1               05/01/2013

1               07/01/2013

1               09/01/2013

I want to loop through these dates so that only dates that are at least 3 months from the index date are loaded. In addition, once a date is found that is at least 3 months from the index date, I want that date to become the new index date. So the final table in the example above would look like below:

Cust_No   Order_Date

---------------------------------

1               01/01/2013

1               05/01/2013

1               09/01/2013

In this case 01/01/2013 is the index date. 02/01/2013 is not at least 3 months after 01/01/2013, so it isn't loaded. 05/01/2013 is at least 3 months from 01/01/2013, so that is loaded and becomes the new index date. 07/01/2013 is not at least 3 months away from the new index date of 05/01/2013, so it is not loaded. 09/01/2013 is, so it is loaded.

I know that Peek() can be used to loop, but I don't know how to loop in such a way that the index date can be continuously reset. I would honestly be find with a flag that can identify the appropriate dates (which I can filter later), but I don't know how to create a loop that does this. Is this possible?

1 Solution

Accepted Solutions
petter
Partner - Champion III
Partner - Champion III

Still without resorting to loops by doing FOR or WHILE it is possible to handle multiple customer no's and unsorted input. Have a look at the following two approaches where the first presumes pre-sorted by customer no and date and the second one has to use a resident load to perform sort (less efficient):

Adjusted to handle multiple customer #:

2015-11-08 #1.PNG

Adjusted to handle multiple customer # and unsorted source data:

2015-11-08 #2.PNG

View solution in original post

10 Replies
ronnywaage
Contributor III
Contributor III

Hi

I am working with an example. But is the order date always the first of a month?

E.g. if you have an orderdate the 15.01.2013 will 02.04.2013 be a new index date or will the first order date that can be an index date be 16.04.2013?

sunny_talwar

I took the liberty to add some more rows of data. See if this script help:

Table:

LOAD *,

  RowNo() as RowNum

Inline [

Cust_No,  Order_Date

1,        01/01/2013

1,        02/01/2013

1,        05/01/2013

1,        07/01/2013

1,        09/01/2013

1,  10/01/2013

1,        02/01/2014

1,  09/01/2014

1,  10/01/2014

1,  11/01/2014

1,  02/01/2015

];

LET vRowCount = Peek('RowNum', -1);

NewTable:

NoConcatenate

LOAD Cust_No,

  Order_Date

Resident Table

Where RowNum = 1;

FOR i = 1 to $(vRowCount)

  LET vDate = Num(AddMonths(Peek('Order_Date', -1, NewTable), 3));

  Concatenate (NewTable)

  LOAD FirstSortedValue(Cust_No, RowNum) as Cust_No,

  FirstSortedValue(Order_Date, RowNum) as Order_Date,

  Min(RowNum) as RowNum

  Resident Table

  Where Num(Order_Date) > $(vDate);

NEXT

DROP Table Table;

Output:

Capture.PNG

petter
Partner - Champion III
Partner - Champion III

Actually it doesn't require any looping as far as I can see. A couple of preceding loads is all you need and that makes it very efficient:

2015-11-07 #1.PNG

maxgro
MVP
MVP

for more than 1 customer


SET DateFormat='MM/DD/YYYY';

source:

load rowno() as id, * inline [

Cust_No ,  Order_Date

1  ,            01/01/2013

1  ,            02/01/2013

1    ,          05/01/2013

1    ,          07/01/2013

1      ,        09/01/2013

2  ,            01/01/2014

2  ,            02/01/2014

2    ,          05/01/2014

2    ,          07/01/2014

2      ,        09/01/2014

3  ,            03/01/2014

3  ,            02/01/2014

3    ,          05/01/2014

3    ,          07/01/2014

3      ,        09/01/2014

3      ,        09/01/2014

3      ,        10/01/2014

3      ,        11/01/2014

3      ,        11/01/2014

];

final:

load *, if(Order_Date > AddMonths(Index_Date,3) or Order_Date=Index_Date,1,0) as Flag;

load

  if(Peek('Cust_No')=Cust_No,

  if(AddMonths(Order_Date,-3) > Peek('Index_Date'), Order_Date, Peek('Index_Date')),

  Order_Date) as Index_Date,

  Cust_No ,  Order_Date, id

Resident source

Order by

  //Cust_No, id,                           

  Cust_No ,  Order_Date

  ;

DROP Table source;

ronnywaage
Contributor III
Contributor III

Hi

I have created a small algorithmn to solve this issue.

The starting point was a new list of orders (for two customers) as I assume that you want an index date per customer.

Here is the list that I used:

 

[Customer_IDCustomer_Date
101.01.2014
110.01.2014
125.01.2014
123.02.2014
104.03.2014
119.03.2014
115.04.2014
129.05.2014
108.08.2014
109.08.2014
115.08.2014
112.12.2014
110.01.2015
201.01.2014
201.05.2014
210.06.2014
212.12.2014];

The dates in bold is the dates I expect to find as index dates. And here is the result from the run:

Customer_IDIndex_Date
101.01.2014
115.04.2014
108.08.2014
112.12.2014
201.01.2014
201.05.2014
212.12.2014

Here is the algorithmn:

LET v_NUM_ROWS = NoOfRows('TEST_DATES');

Do while v_NUM_ROWS>0

  // Find new index dates (per customer) in the remaining orders

  INDEX_DATES:

  LOAD Customer_ID, Min(Customer_Date) as Index_Date

  Resident TEST_DATES

  Group by Customer_ID;

  // Add the currently latest index dates per customer

  Left Join (TEST_DATES)

  LOAD Customer_ID, Max(Index_Date) as Max_Index_Date

  Resident INDEX_DATES

  Group by Customer_ID;

  // Prepare to remove all orders that will not set a new index date

  RENAME Table TEST_DATES to old_TEST_DATES;

  // Remove all orders that will not set a new index date

  TEST_DATES:

  NoConcatenate

  LOAD Customer_ID, Customer_Date

  Resident old_TEST_DATES

  Where Customer_Date > AddMonths(Max_Index_Date,3);

  DROP Table old_TEST_DATES;

  // Check if there are more orders left to set new index dates

  LET v_NUM_ROWS = NoOfRows('TEST_DATES');

Loop

  DROP Table TEST_DATES;

petter
Partner - Champion III
Partner - Champion III

Still without resorting to loops by doing FOR or WHILE it is possible to handle multiple customer no's and unsorted input. Have a look at the following two approaches where the first presumes pre-sorted by customer no and date and the second one has to use a resident load to perform sort (less efficient):

Adjusted to handle multiple customer #:

2015-11-08 #1.PNG

Adjusted to handle multiple customer # and unsorted source data:

2015-11-08 #2.PNG

ronnywaage
Contributor III
Contributor III

Hi Petter

Thanks for sharing your solution. Interresting to read about the use of preceeding loads.

It solves the issue I tried to solve. I can see the result is the same as for my looping solution.

I had to test it

Not applicable
Author

This is exactly what I needed and very simple. Thank you!

Not applicable
Author

This is an interesting approach that was great to see. Thanks!