rows after a specific event in time FOR A KEY - snowflake-cloud-data-platform

I have a table like below,
and I am trying to get all rows after the first occurrence of a specific event for a key + the row that contains the event, like below for 'xn' (column1),

Have you seen the QUALIFY() function? Works nicely here combined with MIN() and windowing over your required keys.
Also if you'd like quicker answers sometimes supplying the cte's used to test and run your question might speed up answers.
Copy|Paste|Run
WITH CTE AS
(SELECT TO_TIMESTAMP('9/1/2021 10:10','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY ,'XA' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:11','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XN' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:12','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XN' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:13','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XD' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:14','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XT' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:15','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XM' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:16','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'A' KEY,'XJ' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:12','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'B' KEY,'XR' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:13','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'B' KEY,'XU' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:14','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'B' KEY,'XN' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:15','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'B' KEY,'XI' AS COLUMN_1
UNION
SELECT TO_TIMESTAMP('9/1/2021 10:16','MM/DD/YYYY HH24:MI') AS TIMESTAMP , 'B' KEY,'XH' AS COLUMN_1 )
SELECT
*
FROM
CTE
QUALIFY
TIMESTAMP >= MIN(DECODE(COLUMN_1,'XN', TIMESTAMP ,NULL) ) OVER
(PARTITION BY KEY ORDER BY TIMESTAMP)

Related

Is there a way to join a calendar table to start and end dates of another query?

I have one table with Date Keys and another Calendar table. I want to join the two such that all of the dates between the start and end get the data. Is that possible?
See the tables below for clarification:
Activity Table
Well Activity DateKey
A Drill 20190101
A Drill 20190102
A Drill 20190106
A Drill 20190107
A Drill 20190108
B Complete 20190107
B Complete 20190108
B Complete 20190111
B Complete 20190115
Calendar Table
Date Key CalendarDate
20190101 1/1/2019
20190102 1/2/2019
20190103 1/3/2019
...
Result Table
Calendar Date Well Activity
1/1/2019 A Drill
1/2/2019 A Drill
1/3/2019 A Drill
1/4/2019 A Drill
1/5/2019 A Drill
1/6/2019 A Drill
1/7/2019 A Drill
1/7/2019 B Complete
1/8/2019 A Drill
1/8/2019 B Complete
1/9/2019 B Complete
1/10/2019 B Complete
1/11/2019 B Complete
1/12/2019 B Complete
1/13/2019 B Complete
1/14/2019 B Complete
1/15/2019 B Complete
1/16/2019 Null Null
1/17/2019 Null Null
1/18/2019 Null Null
1/19/2019 Null Null
I've tried a few different joins but nothing fills in the gaps.
SELECT
JA.[WellId]
,Well.[WellName]
,JA.[JobKey]
,Job.[JobType]
-- ,MIN(JA.DateKey)
-- ,MAX(JA.DateKey)
,Calendar.DisplayDate
FROM [WELLEZ].[PLY_WELLEZ_PRD].[rpt].[JobActivity] JA
Left Outer Join [WELLEZ].[PLY_WELLEZ_PRD].[rpt].[Well] Well on JA.[WellId] = Well.[WellId]
Left Outer Join [WELLEZ].[PLY_WELLEZ_PRD].[rpt].[Job] Job on JA.[JobId] = Job.[JobId]
Left Outer Join [PRODUCTION].[PLY_FV_PRD].[mdm].[Calendar] Calendar on Calendar.CalendarKey = JA.DateKey
WHERE
(well.[IsCurrentRecord] = 1 or well.[IsCurrentRecord] is null)
and (well.[WellHasBeenDeleted] = 0 or Well.WellHasBeenDeleted is null)
and (Job.[IsCurrentRecord] = 1 OR Job.[IsCurrentRecord] is Null)
and JA.WellID = 104935.00
Group by
Calendar.DisplayDate
,Well.[WellName]
,Job.[JobType]
,JA.[WellId]
,JA.[JobKey]
Order by
Well.[WellName]
,MIN(Calendar.CalendarDay)
You need to find the start and end date key first, then use a left join from your calendar table in order to have all dates.
WITH Activity AS
(
SELECT 'A' AS Well, 'Drill' AS Activity, 20190101 AS DateKey UNION
SELECT 'A' AS Well, 'Drill' AS Activity, 20190102 AS DateKey UNION
SELECT 'A' AS Well, 'Drill' AS Activity, 20190106 AS DateKey UNION
SELECT 'A' AS Well, 'Drill' AS Activity, 20190107 AS DateKey UNION
SELECT 'A' AS Well, 'Drill' AS Activity, 20190108 AS DateKey UNION
SELECT 'B' AS Well, 'Complete' AS Activity, 20190107 AS DateKey UNION
SELECT 'B' AS Well, 'Complete' AS Activity, 20190108 AS DateKey UNION
SELECT 'B' AS Well, 'Complete' AS Activity, 20190111 AS DateKey UNION
SELECT 'B' AS Well, 'Complete' AS Activity, 20190115 AS DateKey
) ,
Calendar AS (
SELECT 20190101 AS [DateKey], CAST('1/1/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190102 AS [DateKey], CAST('1/2/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190103 AS [DateKey], CAST('1/3/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190104 AS [DateKey], CAST('1/4/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190105 AS [DateKey], CAST('1/5/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190106 AS [DateKey], CAST('1/6/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190107 AS [DateKey], CAST('1/7/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190107 AS [DateKey], CAST('1/7/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190108 AS [DateKey], CAST('1/8/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190108 AS [DateKey], CAST('1/8/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190109 AS [DateKey], CAST('1/9/2019 ' AS DATE) AS CalendarDate UNION
SELECT 20190110 AS [DateKey], CAST('1/10/2019' AS DATE) AS CalendarDate UNION
SELECT 20190111 AS [DateKey], CAST('1/11/2019' AS DATE) AS CalendarDate UNION
SELECT 20190112 AS [DateKey], CAST('1/12/2019' AS DATE) AS CalendarDate UNION
SELECT 20190113 AS [DateKey], CAST('1/13/2019' AS DATE) AS CalendarDate UNION
SELECT 20190114 AS [DateKey], CAST('1/14/2019' AS DATE) AS CalendarDate UNION
SELECT 20190115 AS [DateKey], CAST('1/15/2019' AS DATE) AS CalendarDate UNION
SELECT 20190116 AS [DateKey], CAST('1/16/2019' AS DATE) AS CalendarDate UNION
SELECT 20190117 AS [DateKey], CAST('1/17/2019' AS DATE) AS CalendarDate UNION
SELECT 20190118 AS [DateKey], CAST('1/18/2019' AS DATE) AS CalendarDate UNION
SELECT 20190119 AS [DateKey], CAST('1/19/2019' AS DATE) AS CalendarDate
)
, CTE_START_END_ACTIVITY AS (
SELECT
Well
,Activity
,MIN(DateKey) AS start_Datekey
,MAX(DateKey) AS end_Datekey
FROM Activity
GROUP BY
Well
,Activity
)
SELECT
d.CalendarDate
,a.Well
,a.Activity
FROM Calendar d
LEFT JOIN CTE_START_END_ACTIVITY a
ON d.DateKey BETWEEN a.start_Datekey AND end_Datekey
ORDER BY
D.CalendarDate
,a.Well
,a.Activity

order by clause in union statement

I have some records coming from 4 tables and date is not the common field in all of them but I use 'as'. Now I need to order by date
select id,convert(varchar(20), SoldDate,3) as Date from sale
union
select id,convert(varchar(20), PaymentDate,3) as Date from purchase
union
select id,convert(varchar(20), PaymentClearedDate,3) as Date from payments
union
select id,convert(varchar(20), PaymentClearedDate,3) as Date from orders
order by Date desc
I need order by Date
You can use CTE or subquery :
SELECT t.*
FROM ( <Query>
) t
ORDER BY r.Date DESC;
However, i would argue on date conversations, if you want just date then use cast(SoldDate as date) & latter convert it to dd\MM\yy.
So, your updated query would be :
SELECT t.id, CONVERT(VARCHAR(10), t.[Date], 3) AS [Date]
FROM (SELECT id, CAST(SoldDate AS DATE) AS [Date]
FROM sale
UNION
SELECT id, CAST(PaymentDate AS DATE)
FROM purchase
UNION
. . .
) t
ORDER BY t.[Date] DESC;
What goes wrong with your code? You are ordering by Date there as far as I can see. If you want to order by the Dates in date order, create another column which is the date, as a date type, e.g.
select id,convert(varchar(20), SoldDate,3) as Date,SoldDate as d2 from sale
union
select id,convert(varchar(20), PaymentDate,3) as Date, PaymentDate as d2 from purchase
union
select id,convert(varchar(20), PaymentClearedDate,3) as Date, PaymentClearedDate as d2 from payments
union
select id,convert(varchar(20), PaymentClearedDate,3) as Date, PaymentClearedDate as d2 from orders
order by d2 desc
you might need to cast your dates to type date if they are stored in some other text format
e.g.
select id,convert(varchar(20), SoldDate,3) as Date,CAST(SoldDate as datetime2) as d2 from sale
union
...etc
order by d2 desc

Repeat the first date withing a group

I Would like the first date of each group to repeat for the rest of the rows withing each group
You could use window expressions and grouping;
FIRST_VALUE (Transact-SQL)
You would need to partition by your first column. to get the split of A and B.
For example;
with cteTempData
(
[Code]
, [Date]
)
as
(
select 'A',cast('2015-9-4' as date)
union all select 'A','2015-9-4'
union all select 'A','2015-9-4'
union all select 'A','2015-9-16'
union all select 'B','2015-9-16'
union all select 'B','2015-9-22'
union all select 'B','2015-9-22'
union all select 'B','2015-10-26'
union all select 'B','2015-10-30'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
from cteTempData
Using the first_value syntax also allows you to work with other columns in that ordered record....
with cteTempData
(
[Code]
, [Date]
, [Comment]
)
as
(
select 'A',cast('2015-9-4' as date),'One'
union all select 'A','2015-9-4','Two'
union all select 'A','2015-9-4','Three'
union all select 'A','2015-9-16','Four'
union all select 'B','2015-9-16','Five'
union all select 'B','2015-9-22','Six'
union all select 'B','2015-9-22','Seven'
union all select 'B','2015-10-26','Eight'
union all select 'B','2015-10-30','Nine'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
, FIRST_VALUE([Comment]) over (partition by [Code] order by [Date]) as [First_Comment]
from cteTempData
Use MIN() Over ()
Declare #Table table (Grp varchar(25),Date date)
Insert into #Table values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
Select *
,GrpDate = min(Date) over (Partition By Grp)
From #Table
Returns
Grp Date GrpDate
A 2015-09-04 2015-09-04
A 2015-09-05 2015-09-04
A 2015-09-10 2015-09-04
B 2015-10-04 2015-10-04
B 2015-10-05 2015-10-04
B 2015-10-10 2015-10-04
You could use MIN with the OVER-clause
SELECT t.ColumnA,
DateCol = MIN( t.DateCol ) OVER ( PARTITION BY t.ColumnA ),
OtherColumns
FROM dbo.TableName t
you can go with a CROSS JOIN or FIRST_VALUE.
Declare #Yourtable table (groupCol varchar(25),firstDate date)
Insert into #Yourtable values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
SELECT a.*,b.firstDate
FROM #Yourtable a
CROSS JOIN (SELECT groupCol,MIN(firstDate) firstDate
FROM #Yourtable b
GROUP BY groupCol)b
WHERE a.groupCol =b.groupCol
OR
SELECT a.*,FIRST_VALUE(a.firstDate) OVER (PARTITION BY groupCol ORDER BY groupCol ASC) AS firstDate
FROM #Yourtable a

Calculating Subtotals using rollup in SQL Server

I have a table(#mytable) that contains basic financial info about companies.
CREATE TABLE #mytable
(
Companyid varchar2(50),
DataDescription varchar2(100),
Value DECIMAL(23,6),
Department varchar2(100),
CurrencyIS03 varchar2(5),
DateofData datetime
)
INSERT INTO #mytable (Companyid, DataDescription, Value, Department, CurrencyIS03, DateofData)
SELECT
'A100', 'Revenue', '1000.00', 'Corporate', 'USD', '2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','2000.00','Banking','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','2500.00','Corporate','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A100','Operating Income','10000.00','Corporate','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Operating Income','1000.00','Banking','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A200','Revenue','1100.00','Corporate','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A200','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A200','Operating Income','5500.00','Corporate','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A200','Operating Income','10000.00','Banking','USD','2014-12-31 00:00:00'
I have to find sub totals based on Companyid, DataDescription, Department, CurrencyIS03, DateofData. I am not sure how to do that. I tried doing the following
select
Companyid, DataDescription,
sum(Value) as total,
Department, CurrencyIS03, DateofData
from
#mytable
group by
rollup(CompanyID, Datadescription, Department, CurrencyIS03, DateofData)
This is not returning the correct answer.
Below is what I expect.
CREATE TABLE #outputtable
(
Companyid varchar2(50),
DataDescription varchar2(100),
TotalValue DECIMAL(23,6),
Department varchar2(100),
CurrencyIS03 varchar2(5),
DateofData datetime
)
INSERT INTO #outputtable (Companyid, DataDescription, TotalValue, Department, CurrencyIS03, DateofData)
SELECT 'A100','Revenue','1000.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','2000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','3000.00','Total','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','2500.00','Corporate','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','5500.00','Total','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','10000.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','1000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','11000.00','Total','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','1100.00','Corporate','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','4100.00','Total','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','5500.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','10000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','15500.00','Total','USD','2014-12-31 00:00:00'
Any help is appreciated.
Thanks
It looks like you're only rolling up Department within the grouping so you just have use ROLLUP(Department) and group by the rest normally.
SELECT Companyid,
DataDescription,
SUM(Value) AS total,
COALESCE(Department,'Total') Department,
CurrencyIS03,
DateofData
FROM #mytable
GROUP BY CompanyID,
Datadescription,
ROLLUP(Department),
CurrencyIS03,
DateofData
ORDER BY CompanyID ASC,
Datadescription DESC,
DateofData DESC,
Department ASC
I'm not sure why you would want to use ROLLUP for this.
I would just do:
select * from
(select Companyid,DataDescription,sum(Value) as Value ,'Total' as Department,CurrencyIS03,DateofData
from mytable
group by CompanyID, Datadescription, CurrencyIS03,DateofData
union all
select * from mytable) a
order by CompanyID asc, Datadescription desc, DateofData desc, Department asc
There is a fiddle

select top 1 record with two grouping keys

id date value
1001 2015-06-01 A
1001 2015-06-30 B
1001 2015-07-10 C
1001 2015-07-11 D
1001 2015-08-01 E
1001 2015-08-15 F
1001 2015-08-20 G
Desired output will be
1001 2015-06-30 6 B
1001 2015-07-11 7 D
1001 2015-08-20 8 G
I would like to subset one observation only for each month for each id.
e.g. I have three records for id 1001 in July ('2015-07-01', '2015-07-10', '2015-07-20'). Therefore I just need '2015-07-20' record only. similar for other month.
SELECT
s.id,
s.value,
max(s.date) as [MaxDate],
month(s.date) as [DateMonth]
FROM
name s
WHERE
s.date between '2015-06-01' and '2015-09-01'
GROUP BY
s.id, month(s.date)
With error message
Is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
It seems there is problem when I am using the function Month()
The problem is not with the Month function. Its with the value column. Use value in GROUP BY clause too.
If you really need the value column without GROUP BY, use a CTE first get the max in the select with ID and month. Then Join on ID and MONTH with Name Table to get the value.
Something like this:
;WITH DateCTE AS
(SELECT s.id,
max(s.date) AS [MaxDate],
month(s.date) AS [DateMonth]
FROM name AS s
WHERE s.date BETWEEN '2015-06-01' AND '2015-09-01'
GROUP BY s.id, month(s.date))
SELECT s.id,
K.value,
[MaxDate],
[DateMonth]
FROM DateCTE AS S
INNER JOIN
name AS K
ON s.id = K.ID
AND [MaxDate] = K.date;
Let me know if it works
Try this:
select
res.id,
res.date,
month(res.date) as [DateMonth],
res.value
from name res
INNER JOIN
(SELECT
s.id,
max(s.date) as [MaxDate],
month(s.date) as [DateMonth]
FROM
name s
WHERE
s.date between '2015-06-01' and '2015-09-01'
GROUP BY
s.id, month(s.date)) a
ON a.id = res.id AND a.MaxDate = res.date
If your version of Sql Server supports the row_number function, you can try this:
declare #a table (
id int not null,
dt datetime not null,
val varchar(10) not null
)
insert into #a values (1001, '2015-06-01', 'A')
insert into #a values (1001, '2015-06-30', 'B')
insert into #a values (1001, '2015-07-10', 'C')
insert into #a values (1001, '2015-07-11', 'D')
insert into #a values (1001, '2015-08-01', 'E')
insert into #a values (1001, '2015-08-15', 'F')
insert into #a values (1001, '2015-08-20', 'G')
insert into #a values (1001, '2015-08-20', 'H')
insert into #a values (1002, '2015-08-20', 'I')
insert into #a values (1002, '2015-08-20', 'J')
select sub.id, sub.dt, sub.val, month(sub.dt) as [month]
from (
select id, dt, val, row_number() over (partition by id, month(dt) order by dt desc, val) as rn from #a
) as sub
where sub.rn=1
The error you are receiving because you have not added s.value in your Group by clause.

Resources