#loops #date #qliksense
Вопрос:
Может ли кто-нибудь помочь мне найти решение для приведенного ниже сценария? (Формат даты «ММ/ДД/ГГГГ»)
Дата Cr — это дата создания учетной записи, Дата окончания-дата окончания учетной записи, количество месяцев означает месяцы между датой создания и датой окончания, Дата оплаты означает номер дня каждого месяца, в который должен быть произведен платеж.
Мне нужно сгенерировать это поле Даты выполнения в виде даты, как показано ниже, которая является непрерывными сроками выполнения между датой создания и датой окончания.
1/8/2020
2/8/2020
3/8/2020
4/8/2020
5/8/2020
6/8/2020
Не могли бы вы, пожалуйста, помочь мне найти решение этой задачи?
Ответ №1:
Одним из возможных решений является перебор всех значений и автогенерация дат.
Пример аннотированного сценария загрузки:
// Sample data
RawData:
Load * Inline [
Id, Cr Date , End Date
1 , 12/08/2019, 06/08/2020
2 , 05/07/2019, 16/11/2020
];
// temp table to keep distinct values
// of the concatenation of id, cr date and end date
// example record:
// 1^12/08/2019^06/08/2020
TempTable:
Load
distinct
Id amp; '^' amp; [Cr Date] amp; '^' amp; [End Date] as Id_Dates
Resident
RawData
;
// for each record in Id_Dates field
for i = 1 to FieldValueCount('Id_Dates')
// get the current iteration value
let value = FieldValue('Id_Dates', $(i));
// extract the Id
let currentId = SubField('$(value)', '^', 1);
// extract cr date
let currentCrDate = Num(SubField('$(value)', '^', 2));
// extract end date
let currentEndDate = Num(SubField('$(value)', '^', 3));
// autogenerate all dates between the currentCrDate and currentEndDate
// add the current Id value (this will link to the RawData table
DueDates:
LOAD
'$(currentId)' as Id,
date($(currentCrDate) IterNo() - 1, 'DD/MM/YYYY') AS DueDate
AUTOGENERATE (1)
WHILE
$(currentCrDate) IterNo() -1 <= $(currentEndDate)
;
next
// we dont need this table anymore
Drop Table TempTable;
Как только сценарий будет завершен, он будет содержать две таблицы:
И DueDates
таблица будет содержать такие значения, как это:
P.S. Это решение может быть не очень эффективным, если исходные данные имеют много различных Id
значений. Дайте мне знать, если это так, и я придумаю какое-нибудь другое решение
Обновление (06/10/2021)
Другой подход, позволяющий избежать зацикливания всех строк по отдельности, заключается в создании перекрестного соединения (декартова соединения) между исходными данными и таблицей календаря, содержащей все возможные даты. Как только у нас будет эта таблица, мы сможем отфильтровать строки, которые не требуются
Этот подход будет быстрее, но, скорее всего, он будет потреблять больше оперативной памяти во время перезагрузки. Как только перезагрузка будет завершена, результирующее приложение должно иметь тот же объем памяти, что и подход «цикл по каждой строке».
RawData:
Load * Inline [
Id, Cr Date , End Date
1 , 12/08/2019, 06/08/2020
2 , 05/07/2019, 16/11/2020
];
// Get min and max dates from [Cr Date] and [End Date] fields
TempTable1:
Load
min([Cr Date]) as MinDate,
max([Cr Date]) as MaxDate
Resident
RawData
;
concatenate
Load
min([End Date]) as MinDate,
max([End Date]) as MaxDate
Resident
RawData
;
// Get the overall min and max dates
NoConcatenate
TempTable2:
Load
min(MinDate) as MinDate,
max(MaxDate) as MaxDate
Resident
TempTable1
;
Drop Table TempTable1;
// gnerate all possible dates between the min and max dates
// once the dates are generated join the result table to RawData
// since there is no common fields between both tables
// the result table will be many to many join (cartesian join)
// as a result at this point RawData will be quite large table
// No of rows in RawData (initially) * No of rows in the Calendar table
// for example if RawData has 10 rows and calendar have 1000 the result table
// will have 10 000 rows
// We will reduce the rows a bit in the next step
let vMinDate = peek('MinDate');
let vMaxDate = peek('MaxDate');
join (RawData)
Calendar:
Load
Date($(vMinDate) IterNo() - 1, 'DD/MM/YYYY') as DueDate
Autogenerate 1
While
$(vMinDate) IterNo() - 1 <= $(vMaxDate)
;
Drop Table TempTable2;
// Load resident modified RawData table and while loading we'll create new field
// This field will be used a flag and we'll filter on it at the end
// The logic in the field is:
// if [Cr Date] >= DueDate <= [End Date] then set it to 1 else 0
// The final step is to keep only records with TempFlag == 1
NoConcatenate
RawData_Final:
Load
Id,
[Cr Date],
[End Date],
DueDate
Where
TempFlag = 1
;
Load
Id,
[Cr Date],
[End Date],
DueDate,
if(DueDate >= [Cr Date] and DueDate <= [End Date], 1, 0) as TempFlag
Resident
RawData
;
Drop Table RawData;
Комментарии:
1. Большое вам спасибо, Стефан Стойчей. Я нашел решение. Большое спасибо.
Ответ №2:
@Стефан Стойчев
Спасибо за ваш отличный код…тем не менее, моя таблица значений идентификаторов содержит около 200000 entries…it делает загрузку скрипта довольно медленной. Вы думали о другом методе ?
Спасибо, вот адаптация вашего кода, который я использую
[ZSQ042_TEMP]:
LOAD
key //commande amp; poste
,[COMMANDE]
,[POSTE]
, YEAR_WEEK_REF_TAUX_SERVICE
, WEEK_SM_REELLE_ZSQ
,[ORIGINE]
,[Business unit]
,[Sector]
,TYPE_cde
resident [SALES]
where num(WEEK_SM_REELLE_ZSQ) > num(YEAR_WEEK_REF_TAUX_SERVICE)
;
//on duplique toutes les lignes qui ne sont pas livrées à la semaine annoncée
TempTable:
Load
distinct
key amp; '^' amp; COMMANDE amp; '^' amp; POSTE amp; '^' amp; YEAR_WEEK_REF_TAUX_SERVICE amp; '^' amp; WEEK_SM_REELLE_ZSQ amp; '^' amp;
[ORIGINE] amp; '^' amp; [Business unit] amp; '^' amp; [Sector] amp; '^' amp; TYPE_cde
as Id_Weeks
Resident
[ZSQ042_TEMP]
;
// for each record in Id_Dates field
for i = 2 to FieldValueCount('Id_Weeks')
// get the current iteration value
let value = FieldValue('Id_Weeks', $(i));
// extract the Id
let currentId = SubField('$(value)', '^', 1);
//Extract Order Number
let currentOrder = SubField('$(value)', '^', 2);
//Extract Order Line Number
let currentLine = SubField('$(value)', '^', 3);
// extract week to be delivered
let currentWeekStart = Num(SubField('$(value)', '^', 4));
// extract week of delivery
let currentWeekEnd = Num(SubField('$(value)', '^', 5));
// extract origin
let currentOrigin = SubField('$(value)', '^', 6);
// extract BU
let currentBU = SubField('$(value)', '^', 7);
// extract Sector
let currentSector = SubField('$(value)', '^', 8);
// extract Type de commande
let currentType = SubField('$(value)', '^', 9);
// autogenerate all weeks between the week to deliver and week of delivery
// add the current Id value (this will link to the RawData table)
Concatenate([SALES])
LOAD
'$(currentId)' as key,
'$(currentOrder)' as COMMANDE,
'$(currentLine)' as POSTE,
'$(currentWeekStart)' as INITIAL_YEAR_WEEK_REF_TAUX_SERVICE,
$(currentWeekStart) IterNo() as YEAR_WEEK_REF_TAUX_SERVICE, //IterNo()-1
'$(currentWeekEnd)' as WEEK_SM_REELLE_ZSQ,
IterNo() as Nb_Weeks_Late,
'$(currentOrigin)' as ORIGINE,
'$(currentBU)' as Business_unit,
'$(currentSector)' as Sector,
'$(currentType)' as TYPE_cde
AUTOGENERATE (1)
WHILE
$(currentWeekStart) IterNo() <= $(currentWeekEnd) //IterNo()-1
;
next
// we dont need this table anymore
Drop Table TempTable;
Drop Table ZSQ042_TEMP;
Комментарии:
1. я обновил свой ответ и предоставил другое решение. Второе решение должно работать быстрее, но должно потреблять больше памяти во время перезагрузки. Как только перезагрузка будет завершена, данные о результатах будут такими же, как и в первом решении