Expand table rows based on column value - sql

I have a table with two columns; EVENT_DATE date and RANG number the first column holds a date for an event while the second column is for the period of that event. here is a sample of data
| EVENT_DATE | RANG |
|------------|------|
| 03/01/2015 | 1 |
| 09/04/2015 | 3 |
| 15/10/2015 | 2 |
is there any way to expand the EVENT_DATE by increment it based on the RANG value, so the output will be like,
| EVENT_DATE |
|------------|
| 03/01/2015 |
| 04/01/2015 |
| 09/04/2015 |
| 10/04/2015 |
| 11/04/2015 |
| 12/04/2015 |
| 15/10/2015 |
| 16/10/2015 |
| 17/10/2015 |

Here you go.
select to_char(event_date + (l - 1),'dd/mm/yyyy') from tab1 t
left outer join (
select level l from dual
connect by level <= (select max(rang) + 1 from tab1)
) on l <= rang + 1
order by event_date, 1;
SQL Fiddle

This should work:
select (t.event_date + t2.value) as event_date
from t, (select rownum -1 as value from all_objects) t2
where t2.value <= t.rang
order by 1 asc;

Another possible answer. Assume Event1 is your table
with tmp as
(select MAX(RANG) s FROM Event1)
,rec as (
select 0 num
union all
select num+1 from rec where num < (select s from tmp))
SELECT DATEADD(DAY, r.num, e.Event_Date) Result from rec r join Event1 e on r.num <= e.RANG
ORDER BY DATEADD(DAY, r.num, e.Event_Date)

Related

how to get individual-clinic-month that are excluded from SQL query

I have the following dataset:
individual | clinic_1 | clinic_2 | month | address_recorded | address_code
1 | A | B | 01-01-2016 | 01-02-1999 | C01
1 | A | A | 01-01-2016 | 01-02-2003 | C02
1 | A | A | 01-01-2016 | 01-02-2001 | C06
1 | A | X | 01-01-2016 | 01-02-2000 | C03
2 | C | B | 01-04-2016 | 01-02-1999 | D04
2 | C | A | 01-04-2016 | 01-02-2001 | D05
2 | C | X | 01-04-2016 | 01-02-2000 | D06
I would like to get:
individual | clinic_1 | month | address_code
1 | A | 01-01-2016 | C02
2 | C | 01-04-2016 | D05
Criteria:
For unique set of individual-clinic_1-month with clinic_1 = clinic_2, select the most
recent date in which address was recorded within clinic_1
For unique set of individual-clinic_1-month with NO instances where clinic_1 = clinic_2,
select the most recent date in which address was recorded across
clinics
I thought about doing:
with cte_1
as
(
select * from table
where clinic_1 = clinic_2
)
,cte_2
as
(
select row_number () over (Partition by clinic_1, individual, month order by clinic_1, individual, month, address_recorded desc) as number, *
from cte_1
)
select individual, clinic_1, month, address_code from cte_2 where number = 1
But I don't know how to get those individual-clinic_1-month for which there are no instances where clinic_1=clinic_2, any ideas?
You can Union two select queries; one to select all records where clinic_1=clinic_2 and another one to select all records where clinic_1<>clinic_2
and clinic_1 not in the results set of the first query.
Both queries are grouped by [individual],[clinic_1], [clinic_2], [mnth] to find all of the required data rows for each [clinic_1] - [mnth] entry. Noting that for the 2nd query [clinic_2] is selected as ''.
Check the following:
with cte as
(SELECT [individual] ,[clinic_1],[clinic_2],[mnth],max([address_recorded]) as m
FROM [MyData] where [clinic_1]=[clinic_2]
group by [individual],[clinic_1],[clinic_2] ,[mnth]
),
cte2 as
(SELECT [MyData].[individual] ,[MyData].[clinic_1],'' as [clinic_2],[MyData].[mnth],max([MyData].[address_recorded]) as m
FROM [MyData]
Left Join cte on cte.individual=MyData.individual
and cte.mnth=MyData.mnth
where [MyData].[clinic_1]<>[MyData].[clinic_2] and cte.individual IS NULL
group by [MyData].[individual],[MyData].[clinic_1], [MyData].[mnth]
),
D as
(SELECT * FROM cte
UNION
SELECT * FROM cte2)
,
LastQr as(
select [MyData].individual, [MyData].clinic_1,[MyData].mnth,[MyData].address_code,
row_number() OVER(PARTITION BY [MyData].individual, [MyData].clinic_1,[MyData].mnth ORDER BY [MyData].individual, [MyData].clinic_1,[MyData].mnth)
as rn from D
INNER JOIN [MyData]
ON D.individual=MyData.individual and D.clinic_1=MyData.clinic_1 and D.mnth=MyData.mnth and D.m=MyData.address_recorded
and (D.clinic_2=MyData.clinic_2 or D.clinic_2='')
)
select * from LastQr where rn=1
See the results from dbfiddle.uk.

Repeat rows cumulative

I have this table
| date | id | number |
|------------|----|--------|
| 2021/05/01 | 1 | 10 |
| 2021/05/02 | 2 | 20 |
| 2021/05/03 | 3 | 30 |
| 2021/05/04 | 1 | 20 |
I am trying to write a query to have this other table
| date | id | number |
|------------|----|--------|
| 2021/05/01 | 1 | 10 |
| 2021/05/02 | 1 | 10 |
| 2021/05/02 | 2 | 20 |
| 2021/05/03 | 1 | 10 |
| 2021/05/03 | 2 | 20 |
| 2021/05/03 | 3 | 30 |
| 2021/05/04 | 1 | 20 |
| 2021/05/04 | 2 | 20 |
| 2021/05/04 | 3 | 30 |
The idea is that each date should have all the previus different ids with its number, and if an id is repeated then only the last value should be considered.
One way is to expand out all the rows for each date. Then take the most recent value using qualify:
with t as (
select date '2021-05-01' as date, 1 as id, 10 as number union all
select date '2021-05-02' as date, 2 as id, 20 as number union all
select date '2021-05-03' as date, 3 as id, 30 as number union all
select date '2021-05-04' as date, 1 as id, 20 as number
)
select d.date, t.id, t.number
from t join
(select date
from (select min(date) as min_date, max(date) as max_date
from t
) tt cross join
unnest(generate_date_array(min_date, max_date, interval 1 day)) date
) d
on t.date <= d.date
where 1=1
qualify row_number() over (partition by d.date, t.id order by t.date desc) = 1
order by 1, 2, 3;
A more efficient method doesn't generate all the rows and then filter them. Instead, it just generates the rows that are needed by generating the appropriate dates within each row. That requires a couple of window functions to get the "next" date for each id and the maximum date in the data:
with t as (
select date '2021-05-01' as date, 1 as id, 10 as number union all
select date '2021-05-02' as date, 2 as id, 20 as number union all
select date '2021-05-03' as date, 3 as id, 30 as number union all
select date '2021-05-04' as date, 1 as id, 20 as number
)
select date, t.id, t.number
from (select t.*,
date_add(lead(date) over (partition by id order by date), interval -1 day) as next_date,
max(date) over () as max_date
from t
) t cross join
unnest(generate_date_array(date, coalesce(next_date, max_date))) date
order by 1, 2, 3;
Consider below [less verbose] approach
select t1.date, t2.id, t2.number
from (
select *, array_agg(struct(date, id,number)) over(order by date) arr
from `project.dataset.table`
) t1, unnest(arr) t2
where true
qualify row_number() over (partition by t1.date, t2.id order by t2.date desc) = 1
# order by date, id
if applied to sample data in your question - output is

SQL query For Latest date/time Stamp record for each ID

Please help to sort below list TABLE,
ID NAME DATE TIME STATUS
ID is unique, Name, Date, Time, Status keeps changing in database.
I need output list, having Latest STATUS, DATE AND TIME stamps for each user ID
I would use window functions for this:
select t.*
from (select t.*,
row_number() over (partition by id order by date desc, time desc) as seqnum
from t
) t
where seqnum = 1;
Alternatively, if you have a table with one row per customer, then apply might be best:
select t.*
from customers c cross apply
(select top (1) t.*
from t
where t.id = c.id
order by date desc, time desc
) t;
How about
SELECT T1.*
FROM T T1 INNER JOIN
(
SELECT ID,
CName,
MAX(CDate) CDate,
MAX(CTime) CTime
FROM T
GROUP BY ID,
CName
) T2
ON T1.CDate = T2.CDate
AND
T1.CTime = T2.CTime
AND T1.CName = T2.CName;
Which will return
+---------------------+----------+--------+----+-------+
| CDate | CTime | Status | ID | CName |
+---------------------+----------+--------+----+-------+
| 22/12/2018 00:00:00 | 16:27:57 | 1 | 1 | A |
| 21/12/2018 00:00:00 | 15:41:13 | 4 | 2 | B |
| 20/12/2018 00:00:00 | 12:35:27 | 3 | 2 | C |
| 21/12/2018 00:00:00 | 15:29:46 | 4 | 3 | D |
+---------------------+----------+--------+----+-------+
OR
SELECT T1.*
FROM T T1 INNER JOIN
(
SELECT ID,
MAX(CDate) CDate,
MAX(CTime) CTime
FROM T
GROUP BY ID
) T2
ON T1.CDate = T2.CDate
AND
T1.CTime = T2.CTime;
Which will return
+---------------------+----------+--------+----+-------+
| CDate | CTime | Status | ID | CName |
+---------------------+----------+--------+----+-------+
| 22/12/2018 00:00:00 | 16:27:57 | 1 | 1 | A |
| 21/12/2018 00:00:00 | 15:41:13 | 4 | 2 | B |
| 21/12/2018 00:00:00 | 15:29:46 | 4 | 3 | D |
+---------------------+----------+--------+----+-------+
Demo
SELECT * FROM table
WHERE C_Time =
(SELECT max(C_Time) FROM table t1 WHERE C_Date =
(SELECT max(C_Date) FROM table t2 WHERE t1.ID = t2.ID)
);
This gives you the entries for the highest C_Date and C_Time values for every ID

Date-based rows generation in Oracle

Good day,
I have data in following form
ID Start Date End Date
1 01-Nov-2018 01-Nov-2018
2 04-Nov-2018 07-Nov-2018
3 09-Nov-2018 09-Nov-2018
4 11-Nov-2018 12-Nov-2018
I want to generate the following output
ID Date
1 01-Nov-2018
2 04-Nov-2018
2 05-Nov-2018
2 06-Nov-2018
2 07-Nov-2018
3 09-Nov-2018
4 11-Nov-2018
4 12-Nov-2018
I know how to do it if i want to process it for single ID
SELECT
,d.ID
, dv.date_start start_date
, dv.date_end End_Date
, dv.date_start + Level - 1 the_date
From (SELECT *
FROM table_name d
WHERE d.id = <some_id>) dv
Where (dv.date_start + Level - 1) <= dv.date_end
Connect By Level <= dv.date_end - dv.date_start + 1;
But as soon as i give multiple Ids it just goes haywire and give multiple duplicate dates. Appreciate if anyone can help in how I can generate the desired data.
Try this.
SELECT id,
start_date + LEVEL - 1
FROM t
CONNECT BY LEVEL <= ( end_date - start_date + 1 )
AND PRIOR id = id
AND PRIOR sys_guid() IS NOT NULL;
Read :Sys_Guid() in connect by level
Demo
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( ID, Start_Date, End_Date ) AS
SELECT 1, DATE '2018-11-01', DATE '2018-11-01' FROM DUAL UNION ALL
SELECT 2, DATE '2018-11-04', DATE '2018-11-07' FROM DUAL UNION ALL
SELECT 3, DATE '2018-11-09', DATE '2018-11-09' FROM DUAL UNION ALL
SELECT 4, DATE '2018-11-11', DATE '2018-11-12' FROM DUAL;
Query 1:
SELECT t.*,
c.COLUMN_VALUE AS the_date
FROM table_name t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT t.start_date + LEVEL - 1
FROM DUAL
CONNECT BY t.start_date + LEVEL - 1 <= t.end_date
)
AS SYS.ODCIDATELIST
)
) c
Results:
| ID | START_DATE | END_DATE | THE_DATE |
|----|----------------------|----------------------|----------------------|
| 1 | 2018-11-01T00:00:00Z | 2018-11-01T00:00:00Z | 2018-11-01T00:00:00Z |
| 2 | 2018-11-04T00:00:00Z | 2018-11-07T00:00:00Z | 2018-11-04T00:00:00Z |
| 2 | 2018-11-04T00:00:00Z | 2018-11-07T00:00:00Z | 2018-11-05T00:00:00Z |
| 2 | 2018-11-04T00:00:00Z | 2018-11-07T00:00:00Z | 2018-11-06T00:00:00Z |
| 2 | 2018-11-04T00:00:00Z | 2018-11-07T00:00:00Z | 2018-11-07T00:00:00Z |
| 3 | 2018-11-09T00:00:00Z | 2018-11-09T00:00:00Z | 2018-11-09T00:00:00Z |
| 4 | 2018-11-11T00:00:00Z | 2018-11-12T00:00:00Z | 2018-11-11T00:00:00Z |
| 4 | 2018-11-11T00:00:00Z | 2018-11-12T00:00:00Z | 2018-11-12T00:00:00Z |

In MySQL: fetching rows distinct by year

I have a MySQL table similar to this:
| id | name | create_date |
---------------------------
| 1 | foo | 2003-03-11 |
| 2 | goo | 2003-04-27 |
| 3 | woo | 2004-10-07 |
| 4 | too | 2004-12-01 |
| 5 | hoo | 2005-04-20 |
| 6 | koo | 2006-01-12 |
| 7 | boo | 2006-04-17 |
| 8 | moo | 2006-08-19 |
I want to fetch all the latest yearly rows - one per year. So in the example above I'll get 2, 4, 5 and 8.
What's the right syntax?
Some of the other answers may work for you but this simple query does not require any joins
SELECT YEAR(create_date),
(SELECT id ORDER BY create_date DESC LIMIT 1)
FROM mytable
group by YEAR(create_date)
you can do something like
select * from table_name
where create_date in (
select max(create_date)
from table_name
group by year(create_date))
SELECT id FROM foo JOIN
(SELECT YEAR(create_date),MAX(create_date) AS md
FROM foo
GROUP BY YEAR(create_date)) as maxes
ON (create_date=md);
If you put an index on create_date, this will be fairly fast.
SELECT mi.*
FROM (
SELECT DISTINCT YEAR(created_date) AS dyear
FROM mytable
) md
JOIN mytable mi
ON mi.id =
(
SELECT id
FROM mytable ml
WHERE ml.create_date < CAST(CONCAT_WS('.', dyear + 1, 1, 1)) AS DATETIME)
ORDER BY
ml.create_date DESC
LIMIT 1
)
select id
from mytable
where not exists (
select * from mytable as T2
where T2.id = mytable.id
and T2.id >= year(created_date) + 1
)