SQL: Query for getting date wise increment - sql

Need to count the number of user being added every day, given a date range from date and to date, an e.g is shown below:
select
'2017-06-01' as myDate
, count(distinct user_id)
from tbl_stats
where date(dateTime)<='2017-06-01'
union all
select
'2017-06-02' as myDate
, count(distinct user_id)
from tbl_stats
where date(dateTime)<='2017-06-02'
The output would be like:
reportDate | count
------------+-------
2017-06-01 | 2467
2017-06-02 | 2470
So, I will just have fromDate and toDate and i would need date wise distinct user count in the table. I will not be using any procedures or loops.

SELECT DATE(ts.dateTime) AS reportDate
, COUNT(distinct ts.user_id) AS userCount
FROM tbl_stats AS ts
WHERE ts.dateTime >= #lowerBoundDate
AND ts.dateTime < TIMESTAMPADD('DAY', 1, #upperBoundDate)
GROUP BY DATE(ts.dateTime)

To get cumulative (distinct) users count per day, use following, replace custom dates given in following example with your start and end dates.
WITH test_data AS (
SELECT '2017-01-01'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-01'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-02'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-03'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-03'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 1::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 2::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-04'::date as event_date, 6::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 3::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 4::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 5::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 6::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 7::int as user_id
UNION
SELECT '2017-01-05'::date as event_date, 8::int as user_id
UNION
SELECT '2017-01-06'::date as event_date, 7::int as user_id
UNION
SELECT '2017-01-06'::date as event_date, 9::int as user_id
)
SELECT event_date,
COUNT(distinct user_id) AS distinct_user_per_day,
SUM(COUNT(distinct user_id)) OVER (ORDER BY event_date) AS cumulative_user_count
FROM test_data
WHERE event_date >= '2017-01-01'
AND
event_date <= '2017-01-06'
GROUP BY
event_date

Related

Get a rolling order count into session data

I have the following table
One client has two purchases in one session.
My goal is to assign a order counter to each row of the table.
To reach this goal I am using the lag function to call the last order_id and the last order_timestamp:
SELECT
lag(event_timestamp) over (partition by session_id order by
ecom_data.order_id) as prev_order_timestamp,
lag(ecom_data.order_id)
over (partition by session_id order by event_timestamp) as
prev_order_number
From table
My desired output is this:
Problem, I do not get the previous order time. Instead I get the event_timestamp from the previous event.
My second challenge is that I do not know how to assign a order_count. My desired output is this:
Ideally, this order count should be rolling as in the real data set I dont know how many orders in total each session had. There can be 0 - infinite orders.
Can you help ?
Thank you!
### create sample table (helps to introduce these in your questions)
WITH
base AS (
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:17:41") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'ts' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:17:42") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:27:14") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'atc' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:27:15") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'p' AS event_name,
123 AS order_id,
DATETIME("2022-05-12 10:30:47") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:30:50") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:01") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'atc' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:20") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'ts' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:22") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'rv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:32") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:35") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:32:49") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'p' AS event_name,
456 AS order_id,
DATETIME("2022-05-12 10:33:35") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:33:48") AS event_timestamp
UNION ALL
SELECT
'A' AS client_id,
1 AS session_id,
'tv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:33:50") AS event_timestamp
UNION ALL
SELECT
'B' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 10:31:50") AS event_timestame
UNION ALL
SELECT
'B' AS client_id,
1 AS session_id,
'p' AS event_name,
123 AS order_id,
DATETIME("2022-05-12 10:33:50") AS event_timestame
UNION ALL
SELECT
'C' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 11:13:50") AS event_timestame
UNION ALL
SELECT
'C' AS client_id,
1 AS session_id,
'pv' AS event_name,
NULL AS order_id,
DATETIME("2022-05-12 11:33:50") AS event_timestame),
prev_order1 AS (
SELECT
*,
LAG(order_id) OVER (PARTITION BY client_id ORDER BY event_timestamp) AS prev_order_number1
FROM
base),
### filling in order number using your requested output
prev_order2 AS (
SELECT
*,
MAX(prev_order_number1) OVER(partition by client_id ORDER BY event_timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS prev_order_number2
FROM
prev_order1
ORDER BY
event_timestamp )
### inserting order_counter logic
SELECT
*,
DENSE_RANK() OVER(partition by client_id ORDER BY prev_order_number2) - 1 AS order_counter
FROM
prev_order2
Think about edge cases and perhaps if you want to partition by other dimensions such as client_id vs total table (as you have it now). I included client_id = B as an example.

create time range with 2 columns date_time

The problem I am facing is how to find distinct time periods from multiple time periods with overlap in Teradata ANSI SQL.
For example, the attached tables contain multiple overlapping time periods, how can I combine those time periods into 3 unique time periods in Teradata SQL???
I think I can do it in python with the loop function, but not sure how to do it in SQL
ID
Start Date
End Date
001
2005-01-01
2006-01-01
001
2005-01-01
2007-01-01
001
2008-01-01
2008-06-01
001
2008-04-01
2008-12-01
001
2010-01-01
2010-05-01
001
2010-04-01
2010-12-01
001
2010-11-01
2012-01-01
My expected result is:
ID
start_Date
end_date
001
2005-01-01
2007-01-01
001
2008-01-01
2008-12-01
001
2010-01-01
2012-01-01
From Oracle 12, you can use MATCH_RECOGNIZE to perform a row-by-row comparison:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY start_date
MEASURES
FIRST(start_date) AS start_date,
MAX(end_date) AS end_date
ONE ROW PER MATCH
PATTERN (overlapping_ranges* last_range)
DEFINE overlapping_ranges AS NEXT(start_date) <= MAX(end_date)
)
Which, for the sample data:
CREATE TABLE table_name (ID, Start_Date, End_Date) AS
SELECT '001', DATE '2005-01-01', DATE '2006-01-01' FROM DUAL UNION ALL
SELECT '001', DATE '2005-01-01', DATE '2007-01-01' FROM DUAL UNION ALL
SELECT '001', DATE '2008-01-01', DATE '2008-06-01' FROM DUAL UNION ALL
SELECT '001', DATE '2008-04-01', DATE '2008-12-01' FROM DUAL UNION ALL
SELECT '001', DATE '2010-01-01', DATE '2010-05-01' FROM DUAL UNION ALL
SELECT '001', DATE '2010-04-01', DATE '2010-12-01' FROM DUAL UNION ALL
SELECT '001', DATE '2010-11-01', DATE '2012-01-01' FROM DUAL;
Outputs:
ID
START_DATE
END_DATE
001
2005-01-01 00:00:00
2007-01-01 00:00:00
001
2008-01-01 00:00:00
2008-12-01 00:00:00
001
2010-01-01 00:00:00
2012-01-01 00:00:00
db<>fiddle here
Update: Alternative query
SELECT id,
start_date,
end_date
FROM (
SELECT id,
dt,
SUM(cnt) OVER (PARTITION BY id ORDER BY dt) AS grp,
cnt
FROM (
SELECT ID,
dt,
SUM(type) OVER (PARTITION BY id ORDER BY dt, ROWNUM) * type AS cnt
FROM table_name
UNPIVOT (dt FOR type IN (start_date AS 1, end_date AS -1))
)
WHERE cnt IN (1,0)
)
PIVOT (MAX(dt) FOR cnt IN (1 AS start_date, 0 AS end_date))
Or, an equivalent that does not use UNPIVOT, PIVOT or ROWNUM and works in both Oracle and PostgreSQL:
SELECT id,
MAX(CASE cnt WHEN 1 THEN dt END) AS start_date,
MAX(CASE cnt WHEN 0 THEN dt END) AS end_date
FROM (
SELECT id,
dt,
SUM(cnt) OVER (PARTITION BY id ORDER BY dt) AS grp,
cnt
FROM (
SELECT ID,
dt,
SUM(type) OVER (PARTITION BY id ORDER BY dt, rn) * type AS cnt
FROM (
SELECT r.*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY dt ASC, type DESC) AS rn
FROM (
SELECT id, 1 AS type, start_date AS dt FROM table_name
UNION ALL
SELECT id, -1 AS type, end_date AS dt FROM table_name
) r
) p
) s
WHERE cnt IN (1,0)
) t
GROUP BY id, grp
Update 2: Another Alternative
SELECT id,
MIN(start_date) AS start_date,
MAX(end_Date) AS end_date
FROM (
SELECT t.*,
SUM(CASE WHEN start_date <= prev_max THEN 0 ELSE 1 END)
OVER (PARTITION BY id ORDER BY start_date) AS grp
FROM (
SELECT t.*,
MAX(end_date) OVER (
PARTITION BY id ORDER BY start_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
) AS prev_max
FROM table_name t
) t
) t
GROUP BY id, grp
db<>fiddle Oracle PostgreSQL
This is a gaps and islands problem. Try this:
with u as
(select ID, start_date, end_date,
case
when start_date <= lag(end_date) over(partition by ID order by start_date, end_date) then 0
else 1 end as grp
from table_name),
v as
(select ID, start_date, end_date,
sum(grp) over(partition by ID order by start_date, end_date) as island
from u)
select ID, min(start_date) as start_Date, max(end_date) as end_date
from v
group by ID, island;
Fiddle
Basically you can identify "islands" by comparing start_date of current row to end_date of previous row (ordered by start_date, end_date), if it precedes it then it's the same island. Then you can do a rolling sum() to get the island numbers. Finally select min(start_date) and max(end_date) from each island to get the desired output.
This may work ,with little bit of change in function , I tried it in Dbeaver :
select ID,Start_Date,End_Date
from
(
select t.*,
dense_rank () over(partition by extract (year from Start_Date) order BY End_Date desc) drnk
from testing_123 t
) temp
where temp.drnk = 1
ORDER BY Start_Date;
Try this
WITH a as (
SELECT
ID,
LEFT(Start_Date, 4) as Year,
MIN(Start_Date) as New_Start_Date
FROM
TAB1
GROUP BY
ID,
LEFT(Start_Date, 4)
), b as (
SELECT
a.ID,
Year,
New_Start_Date,
End_Date
FROM
a
LEFT JOIN
TAB1
ON LEFT(a.New_Start_Date, 4) = LEFT(TAB1.Start_Date, 4)
)
select
ID,
New_Start_Date as Start_Date,
MAX(End_Date)
from
b
GROUP BY
ID,
New_Start_Date;
Example: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=97f91b68c635aebfb752538cdd752ace

sql oracle goup by on dates with possibilities of null values

I have a table with emplid and end_date columns. I want from all emplids the max end_dates. If at least one end_date is null, I want to have the null value as max. So in this example:
emplid end_date
1 05/04/2019
1 05/10/2019
1 null
2 05/04/2019
2 05/10/2019
I want as result:
emplid end_date
1 null
2 05/10/2019
I tried something like
select emplid,
CASE
WHEN MAX(NVL(end_Date,'01/01/3000'))='01/01/3000' THEN null
ELSE end_date
END as end_dt
from people
group by emplid
then I get a group-by error.
Maybe it is very easy, but I don't figure out how to get properly what I want.
with s(id, dt) as (
select 1, to_date('05/04/2019', 'dd/mm/yyyy') from dual union all
select 1, to_date('05/10/2019', 'dd/mm/yyyy') from dual union all
select 1, null from dual union all
select 2, to_date('05/04/2019', 'dd/mm/yyyy') from dual union all
select 2, to_date('05/10/2019', 'dd/mm/yyyy') from dual)
select id, decode(count(dt), count(*), max(dt)) max_dt
from s
group by id;
ID MAX_DT
---------- -----------------------------
1
2 2019-10-05 00:00:00
I would simply do:
select emplid,
(case when count(*) = count(end_date)
then max(end_date)
end) as max_end_date
from t
group by emplid;
There is no reason to introduce a "magic" maximum value (even if it is correct).
The first expression in the case is simply asking "do the number of non-NULL end-date values match the number of rows".
Try this
SELECT
EMPLID,
CASE WHEN END_DATE='01/01/3000' THEN NULL ELSE END_DATE END AS END_DT
FROM
(
SELECT EMPLID, MAX(END_DATE) AS END_DATE FROM
(
SELECT EMPLID, NVL(END_DATE,'01/01/3000') AS END_DATE FROM PEOPLE
)
GROUP BY EMPLID
);
Case does not go with group by , you have to get the max value using group by first then evaluate the null values. Try below.
select empid, CASE WHEN NVL(eDate,'01-DEC-3000')='01-DEC-3000' THEN null ELSE edate end end_dt from (
select empid, MAX(NVL(eDate,'01-DEC-3000')) eDate
from
(select 1 empid, sysdate-100 edate from dual union all
select 1 empid, sysdate-10 edate from dual union all
select 1 empid, null edate from dual union all
select 2 empid, sysdate-105 edate from dual union all
select 2 empid, sysdate-1 edate from dual ) datad
group by empid);

Get data from last year

I am trying to get data from 1 year ago from the given date in bigquery.
date(DATE_ADD(date(DATE_ADD(timestamp(New_date),(DATEDIFF(timestamp(New_date), CURRENT_DATE())-1), "DAY")), -354, "DAY"))
The query above works, but when I try to put it in a case statement I get null.
sum(case when date(New_day) = date(DATE_ADD(date(DATE_ADD(timestamp(New_date),(DATEDIFF(timestamp(New_date), CURRENT_DATE())-1), "DAY")), -354, "DAY")) then 'this is working' else null end) as lastyeardata
How do I get data for 1 year ago on the same day to do a YoY comparison?
Edit #Mikhail's suggestions:
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-14' AS New_date, '1234' AS volume UNION ALL
SELECT 2 AS id, '2017-08-13' AS New_date, '2345' AS volume UNION ALL
SELECT 3 AS id, '2017-08-14' AS New_date, '3456' AS volume UNION ALL
SELECT 4 AS id, '2017-08-14' AS New_date, '4567' AS volume UNION ALL
SELECT 5 AS id, '2016-08-14' AS New_date, '5678' AS volume UNION ALL
SELECT 6 AS id, '2016-08-13' AS New_date, '6789' AS volume UNION ALL
SELECT 7 AS id, '2016-08-12' AS New_date, '6789' AS volume UNION ALL
SELECT 8 AS id, '2016-08-11' AS New_date, '1011' AS volume
)
select
New_date
,volume as thisyeardata
,Case
WHEN PARSE_DATE('%Y-%m-%d', New_date) = DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR)
THEN volume
ELSE NULL end as lastyearvolume
,DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR) as lastyear
FROM `yourTable`
For some reason lastyearvolume is giving me null.
Below is for BigQuery Standard SQL (which is really recommended by BigQuery team to use versus Legacy one)
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-14' AS New_date, '2016-08-14' AS New_day UNION ALL
SELECT 2 AS id, '2017-08-13' AS New_date, '2016-08-13' AS New_day UNION ALL
SELECT 3 AS id, '2017-08-14' AS New_date, '2016-08-12' AS New_day UNION ALL
SELECT 4 AS id, '2017-08-14' AS New_date, '2016-08-11' AS New_day
)
SELECT
COUNT(
CASE
WHEN PARSE_DATE('%Y-%m-%d', New_day) = DATE_ADD(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL -1 YEAR)
THEN 'this is working'
ELSE NULL
END
) AS lastyeardata
FROM `yourTable`
As you would expect two rows are just counted as they have New_date and New_day year apart, the rest are not
Above example assumes your dates fields are of STRING type
If they are of Date Type - you just omit PARSE_DATE function
DATE_ADD has counterpart - DATE_SUB - so it can be used instead as DATE_SUB(PARSE_DATE('%Y-%m-%d', New_date), INTERVAL 1 YEAR)
Update based on your updated example:
simple and fast way to adjust your query t work:
#standardSQl
WITH `yourTable` AS (
SELECT 1 AS id, '2017-08-13' AS New_date, '1234' AS volume UNION ALL
SELECT 2 AS id, '2017-08-14' AS New_date, '2345' AS volume UNION ALL
SELECT 3 AS id, '2017-08-15' AS New_date, '3456' AS volume UNION ALL
SELECT 4 AS id, '2017-08-16' AS New_date, '4567' AS volume UNION ALL
SELECT 5 AS id, '2016-08-13' AS New_date, '5678' AS volume UNION ALL
SELECT 6 AS id, '2016-08-14' AS New_date, '6789' AS volume UNION ALL
SELECT 7 AS id, '2016-08-15' AS New_date, '6789' AS volume UNION ALL
SELECT 8 AS id, '2016-08-16' AS New_date, '1011' AS volume
)
SELECT
this_year.New_date
,this_year.volume AS thisyeardata
,last_year.volume AS lastyeardata
FROM `yourTable` AS this_year
JOIN `yourTable` AS last_year
ON PARSE_DATE('%Y-%m-%d', last_year.New_date) = DATE_ADD(PARSE_DATE('%Y-%m-%d', this_year.New_date), INTERVAL -1 YEAR)

Identify contiguous and discontinuous date ranges

I have a table named x . The data is as follows.
Acccount_num start_dt end_dt
A111326 02/01/2016 02/11/2016
A111326 02/12/2016 03/05/2016
A111326 03/02/2016 03/16/2016
A111331 02/28/2016 02/29/2016
A111331 02/29/2016 03/29/2016
A999999 08/25/2015 08/25/2015
A999999 12/19/2015 12/22/2015
A222222 11/06/2015 11/10/2015
A222222 05/16/2016 05/17/2016
Both A111326 and A111331 should be identified as contiguous data and A999999 and
A222222 should be identified as discontinuous data.In my code I currently use the following query to identify discontinuous data. The A111326 is also erroneously identified as discontinuous data. Please help to modify the below code so that A111326 is not identified as discontinuous data.Thanks in advance for your help.
(SELECT account_num
FROM (SELECT account_num,
(MAX (
END_DT)
OVER (PARTITION BY account_num
ORDER BY START_DT))
START_DT,
(LEAD (
START_DT)
OVER (PARTITION BY account_num
ORDER BY START_DT))
END_DT
FROM x
WHERE (START_DT + 1) <=
(END_DT - 1))
WHERE START_DT < END_DT);
Oracle Setup:
CREATE TABLE accounts ( Account_num, start_dt, end_dt ) AS
SELECT 'A', DATE '2016-02-01', DATE '2016-02-11' FROM DUAL UNION ALL
SELECT 'A', DATE '2016-02-12', DATE '2016-03-05' FROM DUAL UNION ALL
SELECT 'A', DATE '2016-03-02', DATE '2016-03-16' FROM DUAL UNION ALL
SELECT 'B', DATE '2016-02-28', DATE '2016-02-29' FROM DUAL UNION ALL
SELECT 'B', DATE '2016-02-29', DATE '2016-03-29' FROM DUAL UNION ALL
SELECT 'C', DATE '2015-08-25', DATE '2015-08-25' FROM DUAL UNION ALL
SELECT 'C', DATE '2015-12-19', DATE '2015-12-22' FROM DUAL UNION ALL
SELECT 'D', DATE '2015-11-06', DATE '2015-11-10' FROM DUAL UNION ALL
SELECT 'D', DATE '2016-05-16', DATE '2016-05-17' FROM DUAL UNION ALL
SELECT 'E', DATE '2016-01-01', DATE '2016-01-02' FROM DUAL UNION ALL
SELECT 'E', DATE '2016-01-05', DATE '2016-01-06' FROM DUAL UNION ALL
SELECT 'E', DATE '2016-01-03', DATE '2016-01-07' FROM DUAL;
Query:
WITH times ( account_num, dt, lvl ) AS (
SELECT Account_num, start_dt - 1, 1 FROM accounts
UNION ALL
SELECT Account_num, end_dt, -1 FROM accounts
)
, totals ( account_num, dt, total ) AS (
SELECT account_num,
dt,
SUM( lvl ) OVER ( PARTITION BY Account_num ORDER BY dt, lvl DESC )
FROM times
)
SELECT Account_num,
CASE WHEN COUNT( CASE total WHEN 0 THEN 1 END ) > 1
THEN 'N'
ELSE 'Y'
END AS is_contiguous
FROM totals
GROUP BY Account_Num
ORDER BY Account_Num;
Output:
ACCOUNT_NUM IS_CONTIGUOUS
----------- -------------
A Y
B Y
C N
D N
E Y
Alternative Query:
(It's exactly the same method just using UNPIVOT rather than UNION ALL.)
SELECT Account_num,
CASE WHEN COUNT( CASE total WHEN 0 THEN 1 END ) > 1
THEN 'N'
ELSE 'Y'
END AS is_contiguous
FROM (
SELECT Account_num,
SUM( lvl ) OVER ( PARTITION BY Account_Num
ORDER BY CASE lvl WHEN 1 THEN dt - 1 ELSE dt END,
lvl DESC
) AS total
FROM accounts
UNPIVOT ( dt FOR lvl IN ( start_dt AS 1, end_dt AS -1 ) )
)
GROUP BY Account_Num
ORDER BY Account_Num;
WITH cte AS (
SELECT
AccountNumber
,CASE
WHEN
LAG(End_Dt) OVER (PARTITION BY AccountNumber ORDER BY End_Dt) IS NULL THEN 0
WHEN
LAG(End_Dt) OVER (PARTITION BY AccountNumber ORDER BY End_Dt) >= Start_Dt - 1 THEN 0
ELSE 1
END as discontiguous
FROM
#Table
)
SELECT
AccountNumber
,CASE WHEN SUM(discontiguous) > 0 THEN 'discontiguous' ELSE 'contiguous' END
FROM
cte
GROUP BY
AccountNumber;
One of your problems is that your contiguous desired result also includes overlapping date ranges in your example data set. Example A111326 Starts on 3/2/2016 but ends the row before on 3/5/2015 meaning it overlaps by 3 days.