How to conver this SQL server Function into a CTE in snowflake - function

I have two functions in SQL server that i'm trying to recreate in snowflake. i want to make it a CTE instead as i'm having many issues with it being a function (very picky about date parameters passed through)
I'm not quite thinking of it in the right way. So we pass two paramters through, a date and an int. and the function returns an INT value for us. I'm kind of "stuck".
--Function 1: Straight from SQL server
ALTER FUNCTION [dbo].[cfn_GetShiftIDFromDateTime] (
#dateTime datetime,
#shiftCalendarID int
)
RETURNS int
AS
BEGIN
DECLARE
#time time = CONVERT( time, #dateTime ),
#curDay int,
#prvDay int,
#shiftID int;
SELECT TOP 1
#shiftCalendarID = ID,
#curDay = DATEDIFF( dd, BeginDate, #dateTime ) % PeriodInDays + 1,
#prvDay = ( #curDay + PeriodInDays - 2 ) % PeriodInDays + 1
FROM ShiftCalendar
WHERE ID = #shiftCalendarID
OR ( #shiftCalendarID IS NULL
AND Name = 'Factory'
AND BeginDate <= #dateTime )
ORDER BY BeginDate DESC;
SELECT #shiftID = ID
FROM Shift
WHERE ShiftCalendarID = #shiftCalendarID
AND ( ( FromDay = #curDay AND FromTimeOfDay <= #time AND TillTimeOfDay > #time )
OR ( FromDay = #curDay AND FromTimeOfDay >= TillTimeOfDay AND FromTimeOfDay <= #time )
OR ( FromDay = #prvDay AND FromTimeOfDay >= TillTimeOfDay AND TillTimeOfDay > #time )
);
RETURN #shiftID;
END
--GO
I had help from a user writing this and was able to get this function written in snowflake and seems to be working properly. here it is below
--Function 1 -- Snowflake syntax, currently working
CREATE OR REPLACE FUNCTION DB_BI_DEV.RAW_CPMS_AAR.cfn_GetShiftIDFromDateTime (dateTime TIMESTAMP_NTZ(9), shiftCalendarID int)
RETURNS table (shiftID int)
AS
$$
WITH T0 (ShiftCalendarID, CurDay, PrvDay)
AS (
SELECT TOP 1
ID AS ShiftCalendarID,
DATEDIFF( day, BeginDate, dateTime ) % PeriodInDays + 1 AS CurDay,
( CurDay + PeriodInDays - 2 ) % PeriodInDays + 1 AS PrvDay
FROM RAW_CPMS_AAR.ShiftCalendar
WHERE ID = shiftCalendarID
OR ( shiftCalendarID IS NULL
AND Name = 'Factory'
AND BeginDate <= dateTime )
ORDER BY BeginDate DESC
),
T1 (TimeValue)
AS (
SELECT TIME_FROM_PARTS(
EXTRACT(HOUR FROM dateTime),
EXTRACT(MINUTE FROM dateTime),
EXTRACT(SECOND FROM dateTime))
)
SELECT ID as shiftID
FROM RAW_CPMS_AAR.Shift, T0, T1
WHERE Shift.ShiftCalendarID = T0.ShiftCalendarID
AND ( ( FromDay = T0.CurDay AND FromTimeOfDay <= T1.TimeValue AND TillTimeOfDay > T1.TimeValue )
OR ( FromDay = T0.CurDay AND FromTimeOfDay >= TillTimeOfDay AND FromTimeOfDay <= T1.TimeValue )
OR ( FromDay = T0.PrvDay AND FromTimeOfDay >= TillTimeOfDay AND TillTimeOfDay > T1.TimeValue )
)
$$
;
here is function 2:
--Function 2: Straight from SQL server
ALTER FUNCTION [dbo].[cfn_GetEquipmentShiftCalendarID] ( #equipmentID int, #date datetime )
RETURNS int
AS
BEGIN
IF #date IS NULL SET #date = GETDATE();
DECLARE
#shiftCalendarID int,
#endDate date;
WITH cte ( ID, ParentEquipmentID, ShiftCalendarEntityNumber ) AS (
SELECT ID, ParentEquipmentID, ShiftCalendarEntityNumber
FROM Equipment
WHERE ID = #equipmentID
UNION ALL
SELECT p.ID, p.ParentEquipmentID, p.ShiftCalendarEntityNumber
FROM cte
INNER JOIN Equipment p ON p.ID = cte.ParentEquipmentID AND cte.ShiftCalendarEntityNumber IS NULL
)
SELECT TOP 1 #shiftCalendarID = sc.ID, #endDate = sc.EndDate
FROM cte
INNER JOIN ShiftCalendar sc ON sc.EntityNumber = cte.ShiftCalendarEntityNumber
WHERE sc.BeginDate <= #date
ORDER BY
CASE WHEN EndDate IS NULL OR EndDate > #date THEN 1 ELSE 2 END, -- Prio on date range
sc.BeginDate DESC;
IF #shiftCalendarID IS NULL
BEGIN
-- Default to the last created calendar we find that started before the given time
SELECT TOP 1 #shiftCalendarID = ID
FROM ShiftCalendar
WHERE BeginDate < #date
ORDER BY BeginDate DESC;
END;
RETURN #shiftCalendarID; -- CASE WHEN #endDate IS NULL OR #endDate > #date THEN #shiftCalendarID END; -- Return NULL when no matching date range found?
END
GO
This one I was able to rewrite in snowflake but the if statement isnt working. I am not sure if snowflake function can use an if statement.
ALTER FUNCTION [dbo].[cfn_GetShiftIDFromDateTime] (
#dateTime datetime,
#shiftCalendarID int
)
RETURNS int
AS
BEGIN
DECLARE
#time time = CONVERT( time, #dateTime ),
#curDay int,
#prvDay int,
#shiftID int;
SELECT TOP 1
#shiftCalendarID = ID,
#curDay = DATEDIFF( dd, BeginDate, #dateTime ) % PeriodInDays + 1,
#prvDay = ( #curDay + PeriodInDays - 2 ) % PeriodInDays + 1
FROM ShiftCalendar
WHERE ID = #shiftCalendarID
OR ( #shiftCalendarID IS NULL
AND Name = 'Factory'
AND BeginDate <= #dateTime )
ORDER BY BeginDate DESC;
SELECT #shiftID = ID
FROM Shift
WHERE ShiftCalendarID = #shiftCalendarID
AND ( ( FromDay = #curDay AND FromTimeOfDay <= #time AND TillTimeOfDay > #time )
OR ( FromDay = #curDay AND FromTimeOfDay >= TillTimeOfDay AND FromTimeOfDay <= #time )
OR ( FromDay = #prvDay AND FromTimeOfDay >= TillTimeOfDay AND TillTimeOfDay > #time )
);
RETURN #shiftID;
END
--GO
so how are these functions used? i have a view which i was able to recreate in snowflake, but is missing the part that calls the function.
ALTER VIEW [proj].[pvw_PowerBI_ActualUnits]
SELECT
e.Name AS ProductionUnit,
temp.DateTime AS DateTime,
s.Reference AS Shift,
CONVERT(TIME, temp.DateTime) AS Time,
CONVERT(DATE, temp.DateTime - ISNULL((SELECT CAST(MIN(s_first.FromTimeOfDay) AS DateTime) FROM [Shift] s_first WHERE s_first.FromDay = s.FromDay AND s_first.ShiftCalendarID = s.ShiftCalendarID), CAST('6:00' AS DateTime))) AS ProductionDate,
'Actual Units' AS ScrapReason,
temp.ScrapQuantity AS ScrapQuantity,
'Auto Registered' AS RegistrationType,
s.ID
FROM
(SELECT
vl.EquipmentID AS ProductionUnit,
DATEADD(MINUTE, 30 * (DATEPART(MINUTE, vl.BeginTime) / 30), DATEADD(HOUR, DATEDIFF(HOUR, 0, vl.BeginTime), 0)) AS DateTime,
SUM(vl.Quantity) AS ScrapQuantity
FROM oee.ValueLog vl WITH (NOLOCK)
INNER JOIN KPIInstance ki ON ki.ID = vl.KPIInstanceID AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY DATEADD(MINUTE, 30 * (DATEPART(MINUTE, vl.BeginTime) / 30), DATEADD(HOUR, DATEDIFF(HOUR, 0, vl.BeginTime), 0)), vl.EquipmentID) temp
INNER JOIN Equipment e ON e.ID = temp.ProductionUnit
INNER JOIN Shift s ON s.ID = dbo.cfn_GetShiftIDFromDateTime(temp.DateTime, dbo.cfn_GetEquipmentShiftCalendarID(temp.ProductionUnit, temp.DateTime)) -- here is where the functions are called
i was able to rewrite this in snowflake for the most part minus calling the function.
SELECT
e.Name AS ProductionUnit,
temp.DateTime AS DateTime,
s.Reference AS Shift,
temp.DateTime::TIME AS Time,
--CONVERT(DATE, temp.DateTime - ISNULL((SELECT CAST(MIN(s_first.FromTimeOfDay) AS DateTime) FROM [Shift] s_first WHERE s_first.FromDay = s.FromDay AND s_first.ShiftCalendarID = s.ShiftCalendarID), CAST('6:00' AS DateTime))) AS ProductionDate,
IFNULL(dateadd(HOUR, - (HOUR(SELECT MIN(s_first.FromTimeOfDay)
FROM RAW_CPMS_AAR.Shift s_first WHERE s_first.FromDay = s.FromDay AND s_first.ShiftCalendarID = s.ShiftCalendarID)), temp.DateTime), (dateadd(HOUR, - 6, temp.DateTime))) AS ProductionDate ,
'Actual Units' AS ScrapReason,
temp.ScrapQuantity AS ScrapQuantity,
'Auto Registered' AS RegistrationType
FROM
(SELECT
vl.EquipmentID AS ProductionUnit,
DATEADD(MIN, 30 * (DATE_PART(MINUTE, vl.BeginTime) / 30), DATEADD(HOUR, DATEDIFF(HOUR, '0', vl.BeginTime), '0')) AS DateTime,
SUM(vl.Quantity) AS ScrapQuantity
FROM RAW_CPMS_AAR.ValueLog vl
INNER JOIN KPIInstance ki ON ki.ID = vl.KPIInstanceID AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY DATEADD(MIN, 30 * (DATE_PART(MINUTE, vl.BeginTime) / 30), DATEADD(HOUR, DATEDIFF(HOUR, '0', vl.BeginTime), '0')), vl.EquipmentID) as temp, shiftcalendar_cte, RAW_CPMS_AAR.Equipment e, RAW_CPMS_AAR.Shift s
WHERE e.ID = temp.ProductionUnit
Now i dont know if there is a better way to do this, maybe a cte is better. i know functions are not resource friendly, i'm simply trying to recreate this. open to any ideas or help.

ok, some small translations:
DATEADD(HOUR, DATEDIFF(HOUR, 0, vl.BeginTime), 0)
is truncating to the hour, so is the same as
date_trunc('HOUR', vl.BeginTime)
that dateadd thus is:
select column1
,date_trunc('hour', column1) as t_hour
,truncate(minute(column1)/30)*30
,timeadd('minute', truncate(minute(column1)/30)*30, date_trunc('hour', column1)) as datetime
from values
('2022-11-14 13:26:01'::timestamp_ntz),
('2022-11-14 12:34:01'::timestamp_ntz);
COLUMN1
T_HOUR
TRUNCATE(MINUTE(COLUMN1)/30)*30
DATETIME
2022-11-14 13:26:01.000
2022-11-14 13:00:00.000
0
2022-11-14 13:00:00.000
2022-11-14 12:34:01.000
2022-11-14 12:00:00.000
30
2022-11-14 12:30:00.000
So moving that sub-select that you alias as temp into a CTE, and get that "working"
WITH table_oee_valuelog(equipmentid, begintime, quantity, kpiinstanceid) as (
select * from values
(1, '2022-11-14 13:26:01'::timestamp_ntz, 10, 100),
(1, '2022-11-14 13:26:01'::timestamp_ntz, 11, 100),
(1, '2022-11-14 13:34:01'::timestamp_ntz, 12, 100)
), table_kpiinstance(id, kpidefinitionid) as (
select * from values
(100, 'COUNT-OUT:extra stuff')
)--, temp_sub_select as (
SELECT
vl.equipmentid as productionunit,
timeadd('minute', truncate(minute(vl.BeginTime)/30)*30, date_trunc('hour', vl.BeginTime)) as datetime,
SUM(vl.quantity) AS scrapquantity
FROM table_oee_valuelog as vl
INNER JOIN table_kpiinstance as ki
ON ki.ID = vl.KPIInstanceID
AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY 1,2
;)
PRODUCTIONUNIT
DATETIME
SCRAPQUANTITY
1
2022-11-14 13:00:00.000
21
1
2022-11-14 13:30:00.000
12
implementing cfn_GetEquipmentShiftCalendarID
so if we extend our table data a bit more we can take a first crack at cfn_GetEquipmentShiftCalendarID like:
WITH table_oee_valuelog(equipmentid, begintime, quantity, kpiinstanceid) as (
select * from values
(1, '2022-11-14 13:26:01'::timestamp_ntz, 10, 100),
(1, '2022-11-14 13:26:01'::timestamp_ntz, 11, 100),
(1, '2022-11-14 13:34:01'::timestamp_ntz, 12, 100)
), table_kpiinstance(id, kpidefinitionid) as (
select * from values
(100, 'COUNT-OUT:extra stuff')
), table_equipment(id, name, parentequipmentid, shiftcalendarentitynumber) as (
select * from values
(1,'equipment one', 2, null),
(2,'equipment two', null, 80),
(3,'equipment three', 4, 81),
(4,'equipment four', null, 82)
), table_shift(id, shiftcalendarid, reference) as (
select * from values
(1001, 9001, 'a')
), table_shiftcalendar(id, entitynumber, begindate, enddate) as (
select * from values
(699, 80, '2021-01-01'::date, '2021-12-31'::date),
(700, 80, '2022-01-01'::date, '2022-12-31'::date),
--(701, 81, '2022-02-01'::date, '2022-11-30'::date),
(702, 82, '2022-10-01'::date, '2022-11-15'::date)
), cte_GetEquipmentShiftCalendarID/*(id, shiftCalendarID)*/ as (
with recursive rec_cte (id, parentequipmentid, shiftcalendarentitynumber) as (
select
ID,
ParentEquipmentID,
ShiftCalendarEntityNumber
FROM table_equipment
UNION ALL
SELECT
r.ID,
p.ParentEquipmentID,
p.ShiftCalendarEntityNumber
FROM rec_cte as r
INNER JOIN table_equipment p
ON p.ID = r.ParentEquipmentID
AND r.ShiftCalendarEntityNumber IS NULL
)
select * from rec_cte as c
left join table_shiftcalendar as sc
on sc.entitynumber = c.ShiftCalendarEntityNumber
where shiftcalendarentitynumber is not null
qualify row_number() over (partition by c.id order by sc.begindate desc ) = 1
)
select * from cte_GetEquipmentShiftCalendarID;
This is missing the #date based filters and the catch all, as to product the "latest" all bit of equipment, cannot be done yet.
ID
PARENTEQUIPMENTID
SHIFTCALENDARENTITYNUMBER
ID_2
ENTITYNUMBER
BEGINDATE
ENDDATE
1
80
700
80
2022-01-01
2022-12-31
2
80
700
80
2022-01-01
2022-12-31
3
4
81
4
82
702
82
2022-10-01
2022-11-15
so we need to weave this current data, with the temp table, how convenient we made it a CTE already...
so the next partial step is:
WITH table_oee_valuelog(equipmentid, begintime, quantity, kpiinstanceid) as (
select * from values
(1, '2022-11-14 13:26:01'::timestamp_ntz, 10, 100),
(1, '2022-11-14 13:26:01'::timestamp_ntz, 11, 100),
(1, '2022-11-14 13:34:01'::timestamp_ntz, 12, 100),
(2, '2022-11-14 13:34:01'::timestamp_ntz, 20, 100),
(3, '2022-11-14 13:34:01'::timestamp_ntz, 30, 100),
(4, '2022-11-14 13:34:01'::timestamp_ntz, 44, 100)
), table_kpiinstance(id, kpidefinitionid) as (
select * from values
(100, 'COUNT-OUT:extra stuff')
), table_equipment(id, name, parentequipmentid, shiftcalendarentitynumber) as (
select * from values
(1,'equipment one', 2, null),
(2,'equipment two', null, 80),
(3,'equipment three', 4, 81),
(4,'equipment four', null, 82)
), table_shift(id, shiftcalendarid, reference) as (
select * from values
(1001, 9001, 'a')
), table_shiftcalendar(id, entitynumber, begindate, enddate) as (
select * from values
(699, 80, '2021-01-01'::date, '2021-12-31'::date),
(700, 80, '2022-01-01'::date, '2022-12-31'::date),
(701, 80, '2023-01-01'::date, '2023-12-31'::date),
--(701, 81, '2022-02-01'::date, '2022-11-30'::date),
(702, 82, '2022-10-01'::date, '2022-11-15'::date)
), temp_sub_select as (
SELECT
vl.equipmentid as productionunit,
timeadd('minute', truncate(minute(vl.BeginTime)/30)*30, date_trunc('hour', vl.BeginTime)) as datetime,
SUM(vl.quantity) AS scrapquantity
FROM table_oee_valuelog as vl
INNER JOIN table_kpiinstance as ki
ON ki.ID = vl.KPIInstanceID
AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY 1,2
), cte_GetEquipmentShiftCalendarID_part_a/*(id, shiftCalendarID)*/ as (
with recursive rec_cte (id, parentequipmentid, shiftcalendarentitynumber) as (
select
ID,
ParentEquipmentID,
ShiftCalendarEntityNumber
FROM table_equipment
UNION ALL
SELECT
r.ID,
p.ParentEquipmentID,
p.ShiftCalendarEntityNumber
FROM rec_cte as r
INNER JOIN table_equipment p
ON p.ID = r.ParentEquipmentID
AND r.ShiftCalendarEntityNumber IS NULL
)
select
c.id,
sc.id as shiftCalendarID,
sc.begindate, sc.enddate
from rec_cte as c
left join table_shiftcalendar as sc
on sc.entitynumber = c.ShiftCalendarEntityNumber
where shiftcalendarentitynumber is not null
)--, last_calendar_per_equipment as (
select *
,iff(c.enddate is null or c.enddate > t.datetime, 1, 2) as order_a
,row_number() over (partition by t.productionunit, t.datetime order by order_a, c.begindate desc) as rn
from temp_sub_select as t
left join cte_GetEquipmentShiftCalendarID_part_a as c
on t.productionunit = c.id
and c.begindate <= t.datetime
;)
this gives:
PRODUCTIONUNIT
DATETIME
SCRAPQUANTITY
ID
SHIFTCALENDARID
BEGINDATE
ENDDATE
ORDER_A
RN
1
2022-11-14 13:00:00.000
21
1
700
2022-01-01
2022-12-31
1
1
1
2022-11-14 13:00:00.000
21
1
699
2021-01-01
2021-12-31
2
2
1
2022-11-14 13:30:00.000
12
1
700
2022-01-01
2022-12-31
1
1
1
2022-11-14 13:30:00.000
12
1
699
2021-01-01
2021-12-31
2
2
2
2022-11-14 13:30:00.000
20
2
700
2022-01-01
2022-12-31
1
1
2
2022-11-14 13:30:00.000
20
2
699
2021-01-01
2021-12-31
2
2
3
2022-11-14 13:30:00.000
30
1
1
4
2022-11-14 13:30:00.000
44
4
702
2022-10-01
2022-11-15
1
1
last step of this function can be handled with this CTE which we can join to the prior results, and take if the prior results are null.
select
t.datetime,
sc.id
from (
select distinct datetime
from temp_sub_select
) as t
join table_shiftcalendar as sc
on sc.begindate <= t.datetime
qualify row_number() over (partition by t.datetime order by sc.begindate desc) = 1
weave those together and a little data change:
WITH table_oee_valuelog(equipmentid, begintime, quantity, kpiinstanceid) as (
select * from values
(1, '2022-11-14 13:26:01'::timestamp_ntz, 10, 100),
(1, '2022-11-14 13:26:01'::timestamp_ntz, 11, 100),
(1, '2022-11-14 13:34:01'::timestamp_ntz, 12, 100),
(2, '2022-11-14 13:34:01'::timestamp_ntz, 20, 100),
(3, '2022-11-14 13:34:01'::timestamp_ntz, 30, 100),
(4, '2022-11-14 13:34:01'::timestamp_ntz, 44, 100)
), table_kpiinstance(id, kpidefinitionid) as (
select * from values
(100, 'COUNT-OUT:extra stuff')
), table_equipment(id, name, parentequipmentid, shiftcalendarentitynumber) as (
select * from values
(1,'equipment one', 2, null),
(2,'equipment two', null, 80),
(3,'equipment three', 4, 81),
(4,'equipment four', null, 82)
), table_shift(id, shiftcalendarid, reference) as (
select * from values
(1001, 9001, 'a')
), table_shiftcalendar(id, entitynumber, begindate, enddate) as (
select * from values
(699, 80, '2021-01-01'::date, '2021-12-31'::date),
(700, 80, '2022-01-01'::date, '2022-12-31'::date),
(701, 80, '2023-01-01'::date, '2023-12-31'::date),
--(701, 81, '2022-02-01'::date, '2022-11-30'::date),
(702, 82, '2022-10-01'::date, '2022-11-15'::date),
(703, 89, '2022-11-01'::date, '2022-11-15'::date)
), temp_sub_select as (
SELECT
vl.equipmentid as productionunit,
timeadd('minute', truncate(minute(vl.BeginTime)/30)*30, date_trunc('hour', vl.BeginTime)) as datetime,
SUM(vl.quantity) AS scrapquantity
FROM table_oee_valuelog as vl
INNER JOIN table_kpiinstance as ki
ON ki.ID = vl.KPIInstanceID
AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY 1,2
), cte_GetEquipmentShiftCalendarID_part_a/*(id, shiftCalendarID)*/ as (
with recursive rec_cte (id, parentequipmentid, shiftcalendarentitynumber) as (
select
ID,
ParentEquipmentID,
ShiftCalendarEntityNumber
FROM table_equipment
UNION ALL
SELECT
r.ID,
p.ParentEquipmentID,
p.ShiftCalendarEntityNumber
FROM rec_cte as r
INNER JOIN table_equipment p
ON p.ID = r.ParentEquipmentID
AND r.ShiftCalendarEntityNumber IS NULL
)
select
c.id,
sc.id as shiftCalendarID,
sc.begindate, sc.enddate
from rec_cte as c
left join table_shiftcalendar as sc
on sc.entitynumber = c.ShiftCalendarEntityNumber
where shiftcalendarentitynumber is not null
), cte_GetEquipmentShiftCalendarID_part_b as (
select t.productionunit,
t.org_datetime,
t.datetime,
c.shiftCalendarID
from (
select
productionunit,
datetime as org_datetime,
nvl(datetime, CURRENT_DATE) as datetime /* handle the null case from the T-SQL */
from temp_sub_select
) as t
left join cte_GetEquipmentShiftCalendarID_part_a as c
on t.productionunit = c.id
and c.begindate <= t.datetime
qualify row_number() over (partition by t.productionunit, t.datetime
order by iff(c.enddate is null or c.enddate > t.datetime, 1, 2), c.begindate desc) = 1
), max_shiftCalendar_per_datetime as (
select
t.datetime,
sc.id
from (
select distinct nvl(datetime, CURRENT_DATE) as datetime
from temp_sub_select
) as t
join table_shiftcalendar as sc
on sc.begindate <= t.datetime
qualify row_number() over (partition by t.datetime order by sc.begindate desc) = 1
)--, last_calendar_per_equipment as (
select
a.productionunit
,a.org_datetime
,a.shiftCalendarID, b.id
,nvl(a.shiftCalendarID, b.id) as shiftCalendarID
from cte_GetEquipmentShiftCalendarID_part_b as a
join max_shiftCalendar_per_datetime as b
on a.datetime = b.datetime
;)
gives:
PRODUCTIONUNIT
ORG_DATETIME
SHIFTCALENDARID
ID
SHIFTCALENDARID_2
1
2022-11-14 13:00:00.000
700
703
700
1
2022-11-14 13:30:00.000
700
703
700
2
2022-11-14 13:30:00.000
700
703
700
3
2022-11-14 13:30:00.000
703
703
4
2022-11-14 13:30:00.000
702
703
702
Mostly Complete answer:
So I striped ProductionDate and the two fixed string from my answer but:
--CREATE VIEW proj.pvw_PowerBI_ActualUnits
WITH table_oee_valuelog(equipmentid, begintime, quantity, kpiinstanceid) as (
select * from values
(1, '2022-11-14 13:26:01'::timestamp_ntz, 10, 100),
(1, '2022-11-14 13:26:01'::timestamp_ntz, 11, 100),
(1, '2022-11-14 13:34:01'::timestamp_ntz, 12, 100),
(2, '2022-11-14 13:34:01'::timestamp_ntz, 20, 100),
(3, '2022-11-14 13:34:01'::timestamp_ntz, 30, 100),
(4, '2022-11-14 13:34:01'::timestamp_ntz, 44, 100)
), table_kpiinstance(id, kpidefinitionid) as (
select * from values
(100, 'COUNT-OUT:extra stuff')
), table_equipment(id, name, parentequipmentid, shiftcalendarentitynumber) as (
select * from values
(1,'equipment one', 2, null),
(2,'equipment two', null, 80),
(3,'equipment three', 4, 81),
(4,'equipment four', null, 82)
), table_shift(id, shiftcalendarid, reference, FromDay, FromTimeOfDay, TillTimeOfDay) as (
select * from values
(1001, 700, 'a', 8, '06:00'::time,'18:00'::time),
(1001, 702, 'a', 5, '06:00'::time,'18:00'::time),
(1001, 703, 'a', 4, '06:00'::time,'18:00'::time)
), table_shiftcalendar(id, entitynumber, begindate, enddate, name, PeriodInDays) as (
select * from values
(699, 80, '2021-01-01'::date, '2021-12-31'::date, 'Factory', 10),
(700, 80, '2022-01-01'::date, '2022-12-31'::date, 'Factory', 10),
(701, 80, '2023-01-01'::date, '2023-12-31'::date, 'Factory', 10),
--(701, 81, '2022-02-01'::date, '2022-11-30'::date, 'Factory', 10),
(702, 82, '2022-10-01'::date, '2022-11-15'::date, 'Factory', 10),
(703, 89, '2022-11-01'::date, '2022-11-15'::date, 'Factory', 10)
), temp_sub_select as (
SELECT
vl.equipmentid as productionunit,
timeadd('minute', truncate(minute(vl.BeginTime)/30)*30, date_trunc('hour', vl.BeginTime)) as datetime,
SUM(vl.quantity) AS scrapquantity
FROM table_oee_valuelog as vl
INNER JOIN table_kpiinstance as ki
ON ki.ID = vl.KPIInstanceID
AND ki.KPIDefinitionID LIKE 'COUNT-OUT:%'
GROUP BY 1,2
), cte_GetEquipmentShiftCalendarID_part_a as (
with recursive rec_cte (id, parentequipmentid, shiftcalendarentitynumber) as (
select
ID,
ParentEquipmentID,
ShiftCalendarEntityNumber
FROM table_equipment
UNION ALL
SELECT
r.ID,
p.ParentEquipmentID,
p.ShiftCalendarEntityNumber
FROM rec_cte as r
INNER JOIN table_equipment p
ON p.ID = r.ParentEquipmentID
AND r.ShiftCalendarEntityNumber IS NULL
)
select
c.id,
sc.id as shiftCalendarID,
sc.begindate, sc.enddate
from rec_cte as c
left join table_shiftcalendar as sc
on sc.entitynumber = c.ShiftCalendarEntityNumber
where shiftcalendarentitynumber is not null
), cte_GetEquipmentShiftCalendarID_part_b as (
select t.productionunit,
t.org_datetime,
t.datetime,
c.shiftCalendarID
from (
select
productionunit,
datetime as org_datetime,
nvl(datetime, CURRENT_DATE) as datetime /* handle the null case from the T-SQL */
from temp_sub_select
) as t
left join cte_GetEquipmentShiftCalendarID_part_a as c
on t.productionunit = c.id
and c.begindate <= t.datetime
qualify row_number() over (partition by t.productionunit, t.datetime
order by iff(c.enddate is null or c.enddate > t.datetime, 1, 2), c.begindate desc) = 1
), max_shiftCalendar_per_datetime as (
select
t.datetime,
sc.id
from (
select distinct nvl(datetime, CURRENT_DATE) as datetime
from temp_sub_select
) as t
join table_shiftcalendar as sc
on sc.begindate <= t.datetime
qualify row_number() over (partition by t.datetime order by sc.begindate desc) = 1
), last_calendar_per_equipment as (
select
a.productionunit
,a.org_datetime
,nvl(a.shiftCalendarID, b.id) as shiftCalendarID
from cte_GetEquipmentShiftCalendarID_part_b as a
join max_shiftCalendar_per_datetime as b
on a.datetime = b.datetime
), cfn_GetShiftIDFromDateTime as (
with t0 as (
select
x.productionunit
,x.org_datetime
,x.org_datetime::time as org_time
,x.ShiftCalendarID
,DATEDIFF( day, BeginDate, x.org_datetime ) % PeriodInDays + 1 AS CurDay
,( CurDay + PeriodInDays - 2 ) % PeriodInDays + 1 AS PrvDay
from last_calendar_per_equipment as x
join table_shiftcalendar as sc
where sc.id = x.shiftCalendarID
OR ( x.shiftCalendarID IS NULL
AND sc.Name = 'Factory'
AND sc.BeginDate <= x.org_datetime )
QUALIFY row_number() over (partition by x.productionunit, x.org_datetime order by sc.begindate desc) = 1
)
SELECT
s.id
,s.reference
,productionunit
,org_datetime
,s.ShiftCalendarID
,s.FromDay, s.FromTimeOfDay, s.TillTimeOfDay, T0.CurDay, t0.org_time
FROM table_shift as s, T0
WHERE s.ShiftCalendarID = T0.ShiftCalendarID
AND ( ( s.FromDay = T0.CurDay AND s.FromTimeOfDay <= t0.org_time AND s.TillTimeOfDay > t0.org_time )
OR ( s.FromDay = T0.CurDay AND s.FromTimeOfDay >= s.TillTimeOfDay AND s.FromTimeOfDay <= t0.org_time )
OR ( s.FromDay = T0.PrvDay AND s.FromTimeOfDay >= s.TillTimeOfDay AND s.TillTimeOfDay > t0.org_time )
)
)
SELECT
e.name AS productionunit,
temp.datetime AS datetime,
s.reference AS shift,
temp.DateTime::time AS Time,
temp.ScrapQuantity AS ScrapQuantity,
s.ID
FROM temp_sub_select as temp
INNER JOIN table_equipment e
ON e.ID = temp.ProductionUnit
INNER JOIN cfn_GetShiftIDFromDateTime s
ON s.productionunit = temp.ProductionUnit
and temp.datetime = s.org_datetime
as far as I can follow, does what your functions do, and unrolls the correlated query, as that would never work in Snowflake.
PRODUCTIONUNIT
DATETIME
SHIFT
TIME
SCRAPQUANTITY
ID
equipment one
2022-11-14 13:00:00.000
a
13:00:00
21
1001
equipment one
2022-11-14 13:30:00.000
a
13:30:00
12
1001
equipment two
2022-11-14 13:30:00.000
a
13:30:00
20
1001
equipment three
2022-11-14 13:30:00.000
a
13:30:00
30
1001
equipment four
2022-11-14 13:30:00.000
a
13:30:00
44
1001
Not so bad for five hours work...

Related

Snowflake Function input is datetime, but when i call the function it's not quite working

with temp AS (
SELECT CAST(SUM(sreg.ScrapQuantity) AS INT) AS Quantity,
sreas.Name AS ScrapReason,
to_date((DATEADD(MINUTE, 30 * (DATE_PART(MINUTE, sreg.ScrapTime) / 30), DATEADD(HOUR, TIMESTAMPDIFF(HOUR, '0', sreg.ScrapTime), '0')))) AS DATETIME,
sreg.EquipmentID AS EquipmentID
FROM ScrapRegistration sreg
INNER JOIN ScrapReason sreas ON sreas.ID = sreg.ScrapReasonID
INNER JOIN WorkRequest wr ON wr.ID = sreg.WorkRequestID
INNER JOIN SegmentRequirementEquipmentRequirement srer ON srer.SegmentRequirementID = wr.SegmentRequirementID
GROUP BY DATEADD(MINUTE, 30 * (DATE_PART(MINUTE, sreg.ScrapTime) / 30), DATEADD(HOUR, TIMESTAMPDIFF(HOUR, '0', sreg.ScrapTime), '0')),
sreg.EquipmentID,
sreas.Name
)
select temp.EquipmentID
from RAW_CPMS_AAR.equipment e, temp
Where e.ID = (select * from table(cfn_GetShiftIDFromDateTime_test(temp.DateTime::DATETIME, 0))) --this works with datetime
when i run this query above, i get Processing aborted due to error 300010:391167117; incident 3245754.. I believe this is an issue with the temp.datetime -- when i run the same codebut hardcoding the function input, i get the desired output.
with temp AS (
SELECT CAST(SUM(sreg.ScrapQuantity) AS INT) AS Quantity,
sreas.Name AS ScrapReason,
to_date((DATEADD(MINUTE, 30 * (DATE_PART(MINUTE, sreg.ScrapTime) / 30), DATEADD(HOUR, TIMESTAMPDIFF(HOUR, '0', sreg.ScrapTime), '0')))) AS DATETIME,
sreg.EquipmentID AS EquipmentID
FROM ScrapRegistration sreg
INNER JOIN ScrapReason sreas ON sreas.ID = sreg.ScrapReasonID
INNER JOIN WorkRequest wr ON wr.ID = sreg.WorkRequestID
INNER JOIN SegmentRequirementEquipmentRequirement srer ON srer.SegmentRequirementID = wr.SegmentRequirementID
GROUP BY DATEADD(MINUTE, 30 * (DATE_PART(MINUTE, sreg.ScrapTime) / 30), DATEADD(HOUR, TIMESTAMPDIFF(HOUR, '0', sreg.ScrapTime), '0')),
sreg.EquipmentID,
sreas.Name
)
select temp.EquipmentID
from RAW_CPMS_AAR.equipment e, temp
Where e.ID = (select * from table(cfn_GetShiftIDFromDateTime_test('2021-12-02 10:03:0.00'::datetime, 0))) --this works with datetime
it seems that that somwehere along the line, it's not liking the date format i put in. it's not returning me an error of not liking the input.
here is the function.
CREATE OR REPLACE FUNCTION DB_BI_DEV.RAW_CPMS_AAR.cfn_GetShiftIDFromDateTime (dateTime TIMESTAMP_NTZ(9), shiftCalendarID int)
RETURNS table (shiftID int)
AS
$$
WITH T0 (ShiftCalendarID, CurDay, PrvDay)
AS (
SELECT TOP 1
ID AS ShiftCalendarID,
DATEDIFF( day, BeginDate, dateTime ) % PeriodInDays + 1 AS CurDay,
( CurDay + PeriodInDays - 2 ) % PeriodInDays + 1 AS PrvDay
FROM RAW_CPMS_AAR.ShiftCalendar
WHERE ID = shiftCalendarID
OR ( shiftCalendarID IS NULL
AND Name = 'Factory'
AND BeginDate <= dateTime )
ORDER BY BeginDate DESC
),
T1 (TimeValue)
AS (
SELECT TIME_FROM_PARTS(
EXTRACT(HOUR FROM dateTime),
EXTRACT(MINUTE FROM dateTime),
EXTRACT(SECOND FROM dateTime))
)
SELECT ID as shiftID
FROM RAW_CPMS_AAR.Shift, T0, T1
WHERE Shift.ShiftCalendarID = T0.ShiftCalendarID
AND ( ( FromDay = T0.CurDay AND FromTimeOfDay <= T1.TimeValue AND TillTimeOfDay > T1.TimeValue )
OR ( FromDay = T0.CurDay AND FromTimeOfDay >= TillTimeOfDay AND FromTimeOfDay <= T1.TimeValue )
OR ( FromDay = T0.PrvDay AND FromTimeOfDay >= TillTimeOfDay AND TillTimeOfDay > T1.TimeValue )
)
$$
;

sql server 2008 running totals between 2 dates

I need to get running totals between 2 dates in my sql server table and update the records simultaneoulsy. My data is as below and ordered by date,voucher_no
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
-------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
13/10/2017 3 105 20 125
Now if i insert a record with voucher_no 4 on 12/10/2017 the output should be like
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
12/10/2017 4 105 4 109
13/10/2017 3 109 20 129
I have seen several examples which find running totals upto a certain date but not between 2 dates or from a particular date to end of file
You should consider changing your database structure. I think it will be better to keep DATE, VOUCHER_NO, DEBITS, CREDITS in one table. And create view to calculate balances. In that case you will not have to update table after each insert. In this case your table will look like
create table myTable (
DATE date
, VOUCHER_NO int
, DEBITS int
, CREDITS int
)
insert into myTable values
('20171010', 1, 10, null),( '20171012', 2, null, 5)
, ('20171013', 3, 20, null), ('20171012', 4, 4, null)
And view will be
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
select
a.DATE, a.VOUCHER_NO, a.DEBITS, a.CREDITS
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal, a.DEBITS, a.CREDITS
Here's another solution if you can not change your db structure. In this case you must run update statement each time after inserts. In both cases I assume that initial balance is 100 while recalculation
create table myTable (
DATE date
, VOUCHER_NO int
, OPEN_BAL int
, DEBITS int
, CREDITS int
, CLOS_BAL int
)
insert into myTable values
('20171010', 1, 100, 10, null, 110)
,( '20171012', 2, 110, null, 5, 105)
, ('20171013', 3, 105, 20, null, 125)
, ('20171012', 4, null, 4, null, null)
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
, cte2 as (
select
a.DATE, a.VOUCHER_NO
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal
)
update a
set a.OPEN_BAL = b.OPEN_BAL, a.CLOS_BAL = b.CLOS_BAL
from
myTable a
join cte2 b on a.DATE = b.DATE and a.VOUCHER_NO = b.VOUCHER_NO

Return one role per ID

I want to return only one role per employee(EmpID), that being the last employee to work on a specific task(FixEndDate). Below are mockup tables.
DECLARE #Problem TABLE
(
ProblemID INT
, ProblemDate DATETIME
, LogBy NVARCHAR(50)
)
INSERT INTO #Problem(ProblemID,ProblemDate,LogBy)
VALUES (1,CAST('2015-01-29 10:53:46.000'AS DATETIME),'Carl')
, (2,CAST('2015-01-21 10:53:46.000'AS DATETIME),'Paul')
, (3,CAST('2015-01-21 13:53:46.000'AS DATETIME),'Paul')
, (4,CAST('2015-01-21 15:53:46.000'AS DATETIME),'Paul')
DECLARE #Fix TABLE
(
FixID INT
, ProblemID INT
, EmpID INT
, FixStartDate DATETIME
, FixEndDate DATETIME
)
INSERT INTO #Fix(ProblemID, EmpID, FixStartDate, FixEndDate)
VALUES (1, 12, CAST('2015-02-02 10:53:46.000'AS DATETIME),CAST('2015-02-02 12:50:46.000'AS DATETIME))
, (1, 14, CAST('2015-02-03 10:53:46.000'AS DATETIME),CAST('2015-02-03 12:50:46.000'AS DATETIME))
, (2, 11, CAST('2015-02-04 10:53:46.000'AS DATETIME),CAST('2015-02-04 01:55:46.000'AS DATETIME))
, (2, 12, CAST('2015-02-04 05:56:46.000'AS DATETIME),CAST('2015-02-03 08:50:46.000'AS DATETIME))
, (3, 10, CAST('2015-02-04 07:53:46.000'AS DATETIME),CAST('2015-02-04 18:10:46.000'AS DATETIME))
, (3, 15, CAST('2015-02-05 10:53:46.000'AS DATETIME),CAST('2015-02-05 12:10:46.000'AS DATETIME))
, (3, 18, CAST('2015-02-05 11:53:46.000'AS DATETIME),CAST('2015-02-05 01:10:46.000'AS DATETIME))
, (4, 20, CAST('2015-02-07 12:53:46.000'AS DATETIME),CAST('2015-02-08 03:10:46.000'AS DATETIME))
, (4, 23, CAST('2015-02-08 11:53:46.000'AS DATETIME),CAST('2015-02-09 18:10:46.000'AS DATETIME))
, (4, 13, CAST('2015-02-10 16:53:46.000'AS DATETIME),CAST('2015-02-11 16:10:46.000'AS DATETIME))
I tried with:
SELECT f.EmpID
, p.ProblemID
, p.ProblemDate
, f.FixStartDate
, f.FixEndDate
FROM #Problem AS p
INNER JOIN #Fix AS f
ON p.ProblemID = f.ProblemID
INNER JOIN (
SELECT f.EmpID
, p.ProblemID
, p.ProblemDate
, f.FixStartDate
, MAX(f.FixEndDate) AS Fixed
FROM #Problem AS p
INNER JOIN #Fix AS f
ON p.ProblemID = f.ProblemID
GROUP BY f.EmpID
, p.ProblemID
, p.ProblemDate
, f.FixStartDate
) AS d
ON d.EmpID = f.EmpID
AND d.Fixed = f.FixEndDate
ORDER BY FixEndDate DESC
Which is the way forward with this?
try
SELECT f.EmpID
, p.ProblemID
, p.ProblemDate
, f.FixStartDate
, f.FixEndDate
FROM #Problem AS p
INNER JOIN (select * from (select * ,row_number() over (partition by ProblemID order by fixenddate desc) as rno from #Fix) t where rno=1) AS f
ON p.ProblemID = f.ProblemID

altering a table using sql

I have this table
**Original Table**
year month duration amount per month
2012 5 3 2000
and I want to get this
**Result table**
year month duration amount per month
2012 5 1 2000
2012 6 1 2000
2012 7 1 2000
Note how the duration of a project (this is a project) is 3 and the "amount per month" is 2000, so I added two more rows to show that the next months (6 and 7) will have an "amount per month" as well. How do I do that with sql/tsql?
try this for SQL SERVER, i included my test temp table:
declare #temp as table
(
[year] int
, [month] int
, [duration] int
, [amount] int
)
insert into #temp
(
[year]
, [month]
, [duration]
, [amount]
)
VALUES(
2012
,5
,3
,2000
)
SELECT
[year]
,[month] + n.number
,1
,[amount]
, '1' + SUBSTRING(CAST([duration] AS varchar(10)), 2, 1000) AS Items
FROM #temp
JOIN master..spt_values n
ON n.type = 'P'
AND n.number < CONVERT(int, [duration])
Please see the script below that may work for your requirement. I have also compensated for calender year and month increment. Please test and let me know.
DECLARE #temp AS TABLE([Year] INT,[Month] INT,Duration INT,Amount INT)
INSERT INTO #temp([year], [month], Duration, Amount)
VALUES (2011, 5, 3, 2000),(2012, 11, 3, 3000),(2013, 9, 12, 1000);
;WITH cte_datefix
AS (
SELECT [Year],
[Month],
Duration,
Amount,
CAST(CAST([Year] AS VARCHAR(4)) + RIGHT('00' + CAST([Month] AS VARCHAR(2)), 2) + '01' AS DATE) AS [Date]
FROM #temp
),
cte_Reslut
AS (SELECT [Year],
[Month],
Duration,
Amount,
[Date],
1 AS Months
FROM cte_datefix
UNION ALL
SELECT t.[Year],
t.[Month],
t.Duration,
t.Amount,
DATEADD(M, Months, t.[Date]) AS [Date],
cr.Months + 1 AS Months
FROM cte_Reslut AS cr
INNER JOIN cte_datefix AS t
ON t.[Year] = cr.[Year]
WHERE cr.Months < cr.Duration
)
SELECT YEAR([Date]) AS [Year],
MONTH([Date]) AS [Month],
1 AS Duration,
Amount
FROM cte_Reslut
ORDER BY [Date]
For those that are wondering how to increment the year if needed, here is an example building on Suing response (really easy, just include two case statements):
select
2012 as [year]
,11 as [month]
,5 as [duration]
,2000 as [amount]
into #temp
select * from #temp
SELECT
case
when [month] + n.number > 12
then [year] + 1
else [year]
end as [year]
,case
when [month] + n.number > 12
then [month] + n.number - 12
else [month] + n.number
end as newYear
,1 as newDuration
,[amount]
, '1' + SUBSTRING(CAST([duration] AS varchar(10)), 2, 1000) AS Items
FROM #temp
JOIN master..spt_values n
ON n.type = 'P'
AND n.number < CONVERT(int, [duration])
drop table #temp

Group by, with rank and sum - not getting correct output

I'm trying to sum a column with rank function and group by month, my code is
select dbo.UpCase( REPLACE( p.Agent_name,'.',' '))as Agent_name, SUM(convert ( float ,
p.Amount))as amount,
RANK() over( order by SUM(convert ( float ,Amount )) desc ) as arank
from dbo.T_Client_Pc_Reg p
group by p.Agent_name ,p.Sale_status ,MONTH(Reg_date)
having [p].Sale_status='Activated'
Currently I'm getting all total value of that column not month wise
Name amount rank
a 100 1
b 80 2
c 50 3
for a amount 100 is total amount till now but , i want get current month total amount not last months..
Maybe you just need to add a WHERE clause? Here is a minor re-write that I think works generally better. Some setup in tempdb:
USE tempdb;
GO
CREATE TABLE dbo.T_Client_Pc_Reg
(
Agent_name VARCHAR(32),
Amount INT,
Sale_Status VARCHAR(32),
Reg_date DATETIME
);
INSERT dbo.T_Client_Pc_Reg
SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'NotActivated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()-40;
Then the query:
SELECT
Agent_name = UPPER(REPLACE(Agent_name, '.', '')),
Amount = SUM(CONVERT(FLOAT, Amount)),
arank = RANK() OVER (ORDER BY SUM(CONVERT(FLOAT, Amount)) DESC)
FROM dbo.T_Client_Pc_Reg
WHERE Reg_date >= DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
AND Reg_date < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) + 1, 0)
AND Sale_status = 'Activated'
GROUP BY UPPER(REPLACE(Agent_name, '.', ''))
ORDER BY arank;
Now cleanup:
USE tempdb;
GO
DROP TABLE dbo.T_Client_Pc_Reg;