get particular day between a data range from giving table - oracle
I read many articles which introduce the using of connect by, but all of them get the particular days from a giving parameter(almost the begin date and end date)
What I want to know is how could I get split the rows from a certain table?
Example
Table T1
StartDate EndDate T_ID
2017-06-01 2017-06-15 01
2017-06-05 2017-06-06 02
The result I want is
TargetDate T_ID
2017-06-01 01
2017-06-02 01
2017-06-03 01
2017-06-04 01
2017-06-05 01
.
.
.
.
2017-06-15 01
2017-06-06 01
2017-06-06 02
I tried
SELECT T_ID, T1.StartDate+ LEVEL - 1 DD, LEVEL
FROM T1
WHERE T1.T_ID in = '01'
CONNECT BY LEVEL <= (TO_DATE(TRUNC(T1.EndDate)) - T1.StartDate + 1 ) ;
Waiting for your solution. Thanks.
Test Data:
CREATE TABLE t1 ( t_id, startdate, enddate ) AS
SELECT 1, DATE '2017-06-01', DATE '2017-06-15' FROM DUAL UNION ALL
SELECT 2, DATE '2017-06-05', DATE '2017-06-06' FROM DUAL;
Query:
SELECT T_ID,
COLUMN_VALUE AS dt,
ROW_NUMBER() OVER ( PARTITION BY t1.ROWID
ORDER BY Column_value ) AS lvl
FROM T1
CROSS JOIN
TABLE(
CAST(
MUTLTSET(
SELECT t1.Startdate + LEVEL - 1
FROM DUAL
CONNECT BY t1.Startdate + LEVEL - 1 <= t1.EndDate
) AS SYS.ODCIDATELIST
)
);
Output:
T_ID DT LVL
---- ---------- ---
1 2017-06-01 1
1 2017-06-02 2
1 2017-06-03 3
1 2017-06-04 4
1 2017-06-05 5
1 2017-06-06 6
1 2017-06-07 7
1 2017-06-08 8
1 2017-06-09 9
1 2017-06-10 10
1 2017-06-11 11
1 2017-06-12 12
1 2017-06-13 13
1 2017-06-14 14
1 2017-06-15 15
2 2017-06-05 1
2 2017-06-06 2
Here is the query in standard SQL (with a recursive cte) which also works in Oracle:
with all_dates(targetdate, t_id, enddate) as
(
select startdate as targetdate, t_id, enddate from t1
union all
select targetdate + 1, t_id, enddate from all_dates where targetdate < enddate
)
select targetdate, t_id
from all_dates
order by t_id, targetdate;
SELECT DISTINCT T_ID
, T1.StartDate+ LEVEL - 1 DD
, LEVEL
FROM T1
WHERE T1.T_ID IN( 1,2)
CONNECT BY LEVEL <= T1.EndDate - T1.StartDate + 1
But I'm not sure about performances (At moment I didn't find a way to limit without DISTINCT but using CONNECT BY clauses).
As an alternative you can use a CTE like this (you can remove RN column, I left it as a check):
with all_dates(targetdate, t_id, enddate, RN) as
(
select startdate as targetdate, t_id, enddate, 1 AS RN from t1
union all
select T1.startdate + all_dates.RN, T1.t_id, T1.enddate, all_dates.RN+1 AS RN
from t1
inner JOIN all_dates ON T1.startdate+all_dates.RN<=all_dates.enddate
AND T1.T_ID = all_dates.T_ID
)
select targetdate, t_id , RN
from all_dates
order by t_id, targetdate;
Sample data:
CREATE TABLE T1 (StartDate DATE, EndDate DATE, T_ID NUMBER(10,0));
INSERT INTO T1 VALUES ('20170601','20170615', 1);
INSERT INTO T1 VALUES ('20170605','20170606', 2);
INSERT INTO T1 VALUES ('20170701','20170703', 3);
Output:
20170601 1 1
20170602 1 2
20170603 1 3
20170604 1 4
20170605 1 5
20170606 1 6
20170607 1 7
20170608 1 8
20170609 1 9
20170610 1 10
20170611 1 11
20170612 1 12
20170613 1 13
20170614 1 14
20170615 1 15
20170605 2 1
20170606 2 2
20170701 3 1
20170702 3 2
20170703 3 3
If you're wanting to use connect by to achieve this, you will need to add a couple of additional clauses in order to get it to work with multiple rows:
WITH t1 AS (SELECT to_date('01/06/2017', 'dd/mm/yyyy') startdate, to_date('15/06/2017', 'dd/mm/yyyy') enddate, 1 t_id FROM dual UNION ALL
SELECT to_date('05/06/2017', 'dd/mm/yyyy') startdate, to_date('06/06/2017', 'dd/mm/yyyy') enddate, 2 t_id FROM dual)
SELECT t_id,
startdate + LEVEL -1 dd
FROM t1
CONNECT BY LEVEL <= enddate - startdate + 1
AND PRIOR t_id = t_id
AND PRIOR sys_guid() IS NOT NULL
ORDER BY t_id, dd;
T_ID DD
---------- -----------
1 01/06/2017
1 02/06/2017
1 03/06/2017
1 04/06/2017
1 05/06/2017
1 06/06/2017
1 07/06/2017
1 08/06/2017
1 09/06/2017
1 10/06/2017
1 11/06/2017
1 12/06/2017
1 13/06/2017
1 14/06/2017
1 15/06/2017
2 05/06/2017
2 06/06/2017
Related
I need to add a column that assigns row numbers to each record
I am trying to add a column that assigns row numbers to each record I made changes to the code to add the desired column But I was getting duplicated row numbers. How do I get unique row numbers? . **Note: This code can be executed in the SQL editor like that. It needs no sample data** select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date, trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby from ( select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20 from (select from_dt ,to_dt ,to_dt - from_dt + 1 as days_between from (select to_date('03-Jan-2021') as from_dt , to_date('30-Jan-2021') as to_dt from dual)) connect by (level - 1) <= days_between ) order by claim_eff_date I made these changes to the code to add the desired column **Note: This code can be executed in the SQL editor like that. It needs no sample data** select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date, trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby, row_number() over (PARTITION BY trunc(GenerateTimeBy1Day,'day'), trunc(GenerateTimeBy1DayPlus20,'day') ORDER BY trunc(GenerateTimeBy1Day,'day')) as row_number from ( select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20 from (select from_dt ,to_dt ,to_dt - from_dt + 1 as days_between from (select to_date('03-Jan-2021') as from_dt , to_date('30-Jan-2021') as to_dt from dual)) connect by (level - 1) <= days_between ) order by claim_eff_date But I am getting Row_Number ---------- 1 1 2 3 4 5 6 1 1 2 3 How do I get unique row numbers? Row_Number ---------- 1 2 3 4 5 6 7 8 9 10
I'm not sure whet those dates represent, but - if query you initially wrote does the job, then use it as source for the final query which calculates row number: SQL> alter session set nls_date_format = 'dd.mm.yyyy'; Session altered. SQL> with your_query as ( 2 select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date, 3 trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby 4 from 5 ( 6 select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20 7 from (select from_dt 8 ,to_dt 9 ,to_dt - from_dt + 1 as days_between 10 from (select date '2021-01-03' as from_dt 11 , date '2021-01-30' as to_dt 12 from dual)) 13 connect by (level - 1) <= days_between 14 )) 15 select claim_eff_date, 16 bwe_to_completeby, 17 row_number() over (order by claim_eff_date) rn 18 from your_query 19 order by claim_eff_date; CLAIM_EFF_ BWE_TO_COM RN ---------- ---------- ---------- 28.12.2020 18.01.2021 1 04.01.2021 25.01.2021 2 04.01.2021 18.01.2021 3 11.01.2021 01.02.2021 4 11.01.2021 25.01.2021 5 18.01.2021 08.02.2021 6 18.01.2021 01.02.2021 7 25.01.2021 08.02.2021 8 25.01.2021 15.02.2021 9 9 rows selected. SQL> By the way, this is what you wrote: to_date('03-Jan-2021'). That's wrong. TO_DATE applied to a string without date format mask relies on Oracle's capabilities to guess what you meant to say. Besides, that won't even work in my database, although you said This code can be executed in the SQL editor My database speaks Croatian and there's no month like Jan. Safer option is to apply format mask: to_date('03-Jan-2021', 'dd-mon-yyyy', 'nls_date_language = english') or, use date literal (like I did): date '2021-01-03' which ALWAYS has date keyword and date in yyyy-mm-dd format, so there's no ambiguity
rownum in Oracle sql with group by
I need to build a query to retrieve information group by Members and an expiration Date but I need to have a sequence number for every Member.. So for example: If Member "A" has 3 records to expire, "B" has only 1 and "C" has 2, I need a result like this: Number Member ExpDate 1 A 01/01/2020 2 A 02/01/2020 3 A 03/01/2020 1 B 01/01/2020 1 C 01/01/2020 2 C 02/01/2020 My query now is: SELECT ROW_NUMBER() OVER(ORDER BY TRUNC(EXPIRATION_DT) ASC) AS SEQUENCE, MEMBER_ID AS MEMBER, SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) AS POINTS, trunc(EXPIRATION_DT) AS EXPDATE FROM TABLE1 WHERE EXPIRATION_DT > SYSDATE AND EXPIRATION_DT < SYSDATE + 90 GROUP BY MEMBER_ID, TRUNC(EXPIRATION_DT) HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0 ORDER BY 4 ASC; But I cant' "group" the sequence number.... The result now is: Seq Mem Points Date 1 1-O 188 2018-03-01 00:00:00 2 1-C 472 2018-03-01 00:00:00 3 1-A 485 2018-03-01 00:00:00 4 1-1 267 2018-03-01 00:00:00 5 1-E 500 2018-03-01 00:00:00 6 1-P 55 2018-03-01 00:00:00 7 1-E 14 2018-03-01 00:00:00
I think you need a DENSE_RANK window function. try this - SELECT DENSE_RANK() OVER (PARTITION BY MEMBER ORDER BY TRUNC(EXPIRATION_DT) ASC) AS SEQUENCE ,MEMBER_ID AS MEMBER ,SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) AS POINTS ,trunc(EXPIRATION_DT) AS EXPDATE FROM TABLE1 WHERE EXPIRATION_DT > SYSDATE AND EXPIRATION_DT < SYSDATE + 90 GROUP BY MEMBER_ID ,TRUNC(EXPIRATION_DT) HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0 ORDER BY 4 ASC;
with g as ( select * From TABLE1 g group by MEMBER_ID ,TRUNC(EXPIRATION_DT) HAVING SUM(ACCRUALED_VALUE) - SUM(USED_VALUE) > 0 ---- etc ) select rownum, g.* From g this select return first column with sequence number
Trouble with Oracle Connect By and Date Ranges
Let's say I have a table with data ranges create table ranges (id number, date_from date, date_to date); insert into ranges values (1, to_date('01.01.2017', 'dd.mm.rrrr'), to_date('03.01.2017', 'dd.mm.rrrr')); insert into ranges values (2, to_date('05.02.2017', 'dd.mm.rrrr'), to_date('08.02.2017', 'dd.mm.rrrr')); and my output should by one row for every date in these ranges id | the_date ---------------- 1 | 01.01.2017 1 | 02.01.2017 1 | 03.01.2017 2 | 05.02.2017 2 | 06.02.2017 2 | 07.02.2017 2 | 08.02.2017 But connect by gives me ORA-01436 Connect by Loop SELECT connect_by_root(id), Trunc(date_from, 'dd') + LEVEL - 1 AS the_date FROM ranges CONNECT BY PRIOR id = id AND Trunc(date_from, 'dd') + LEVEL - 1 <= Trunc(date_to, 'dd') ORDER BY id, the_date What's wrong?
You can add a call to a non-deterministic function, e.g. AND PRIOR dbms_random.value IS NOT NULL So it becomes: SELECT connect_by_root(id), Trunc(date_from, 'dd') + LEVEL - 1 AS the_date FROM ranges CONNECT BY PRIOR id = id AND PRIOR dbms_random.value IS NOT NULL AND Trunc(date_from, 'dd') + LEVEL - 1 <= Trunc(date_to, 'dd') ORDER BY id, the_date; CONNECT_BY_ROOT(ID) THE_DATE ------------------- --------- 1 01-JAN-17 1 02-JAN-17 1 03-JAN-17 2 05-FEB-17 2 06-FEB-17 2 07-FEB-17 2 08-FEB-17 7 rows selected. There's an explanation of why it is necessary in this Oracle Community post; that uses sys_guid() instead of dbms_random.value, but the principle is the same. If you're on 11gR2 or higher you could use recursive subquery factoring instead: WITH rcte (root_id, the_date, date_to) AS ( SELECT id, date_from, date_to FROM ranges UNION ALL SELECT root_id, the_date + 1, date_to FROM rcte WHERE the_date < date_to ) SELECT root_id, the_date FROM rcte ORDER BY root_id, the_date; ROOT_ID THE_DATE ---------- --------- 1 01-JAN-17 1 02-JAN-17 1 03-JAN-17 2 05-FEB-17 2 06-FEB-17 2 07-FEB-17 2 08-FEB-17 7 rows selected.
How to combine 2 select statements in oracle
i have table test2.it contains ID 1 4 5 10 now i found missing numbers in this sequence.with this query SELECT min_ID - 1 + level mn FROM ( SELECT MIN(ID) min_ID , MAX(ID) max_ID FROM test2 ) CONNECT BY level <= max_ID - min_ID + 1 minus SELECT ID FROM test2 output is: MN --- 2 3 6 7 8 9 now i want to combine these 2 columns.I am unable to do this please help me. i want output like 1 2 4 3 7 5 10 6 8 9
Oracle Setup: CREATE TABLE test2 (id) AS SELECT 1 FROM DUAL UNION ALL SELECT 4 FROM DUAL UNION ALL SELECT 5 FROM DUAL UNION ALL SELECT 10 FROM DUAL; Query: WITH bounds ( mn, mx ) AS ( SELECT MIN( id ), MAX( id ) FROM test2 ), missing (id, rn) AS ( SELECT id, ROWNUM FROM ( SELECT mn + LEVEL AS id FROM bounds CONNECT BY LEVEL < MX - MN MINUS SELECT id FROM test2 ) ), existing ( id, rn ) AS ( SELECT id, ROWNUM FROM test2 ) SELECT e.id, m.id FROM existing e FULL OUTER JOIN missing m ON ( e.rn = m.rn ); Output ID ID ---------- ---------- 1 2 4 3 5 6 10 7 9 8
Max size in a connected by prior Oracle
I've got some help turning my table of the sort: Col 23 25 15 53 ... into something like 23,25,15,53... The query that does it is SELECT max(ltrim(sys_connect_by_path(flow_run_id, ','), ',')) FROM (select flow_run_id, rownum rn from table where CREATED_DATE < sysdate - 32 and flow_id = 3 order by 1 desc) START WITH rn = 1 CONNECT BY PRIOR rn = rn - 1 (this beaulty was given by Michael in here) My current problem is that the result is too long (ORA-01489 over the 4k chars from varchar2). I'm still learning about these sys_connected_by_path so I'd need some help sorting this. How could I make this query return me multiple rows instead of one super long line? i.e.: Instead of 419,1,2,3,411,418,4,415,887,413,414,201,888,890,401,417,610,412,416,5,6,922,1080,1422,1423,1411,1412,1413,1414,1415,1416,1417,1418,1419,1964,2217,1636,2037,1988,1970,2038,1989,2000,2040,1993,2043,1994,2001,2044,1658,1995,2045,2224,1996,2019,1678,1997,2022,2201,1680,2219,2024,2207,1677,2209,2220,1959,2211,1961,2026,2212,1962,2028,2215,1675,1676,2035,2216,1986,1963,2017,1983,1935,2002,2018,1985,1936,2003,2020,2032,1937,2004,2021,2033,1938,1943,2023,2034,1939,1944,2025,2225,1941,1950,2027,2036,1942,1955,2029,2041,1945,1956,2030,2227,1946,1957,2031,2039,1947,2005,1974,2042,1948,2006,1976,2228,1949,2007,1978,1951,2009,1979,1929,1952,2012,1980,1931,1953,2013,1981,1933,1954,2015,2334,2350,2311,2239,2240,2241,2242,2245,2246,2249,2250,2336,2312,2008,2010,2011,2014,2251,2253,2016,2243,2244,2247,2351,2248,(...) get 419,1,2,3,411,418,4,415,887,413,414,201,888,890,401,417,610,412,416,5,6,922,1080 1423,1411,1412,1413,1414,1415,1416,1417,1418,1419,1964,2217,1636,2037,1988,1970,2038 2000,2040,1993,2043,1994,2001,2044,1658,1995,2045,2224,1996,2019,1678,1997,2022,2201 (...) Any tips? Thanks! f.
the following query will cut your big string in parts: SQL> SELECT root_rn, MAX(concat) 2 FROM (SELECT connect_by_root(rn) root_rn, 3 ltrim(sys_connect_by_path(flow_run_id, ','), ',') concat 4 FROM (SELECT flow_run_id, rownum rn 5 FROM (SELECT round(dbms_random.VALUE(1, 10000)) 6 AS flow_run_id 7 FROM dual 8 CONNECT BY ROWNUM <= 2000) 9 ORDER BY 1 DESC) 10 START WITH MOD(rn, 10) = 1 11 CONNECT BY PRIOR rn = rn - 1 12 AND MOD(rn, 10) != 1) 13 GROUP BY root_rn 14 ORDER BY root_rn; ROOT_RN MAX(CONCAT) ---------- ------------------------------------------------------------------- 1 654,6710,5297,5481,5085,2793,7646,9170,1051,2387 11 1882,8285,5430,4928,267,3779,3843,1151,3085,1446 21 4721,6087,6755,9904,805,2776,4633,2772,7785,5818 31 5189,5307,6481,2099,3832,9788,5970,8068,6605,3904 41 53,7013,1314,7717,9320,7069,907,5367,5013,7637 51 3903,2318,2611,7954,5751,5598,6148,6555,9724,984 [...] You can replace "10" with a bigger number if you want more elements on each row.
Some little modifications to keep order SELECT 10*frn+1 root,ltrim(sys_connect_by_path(flow_run_id,','),',') FROM (SELECT flow_run_id,mod(rn,10) mrn,floor(rn/10) frn,count(*)over(partition by floor(rn/10))-1 crn FROM (SELECT flow_run_id, row_number()over(order by flow_run_id)-1 rn FROM (SELECT round(dbms_random.VALUE(1, 10000)) AS flow_run_id FROM dual CONNECT BY ROWNUM <= 2000 ) ) ) WHERE crn = mrn START WITH mrn = 0 CONNECT BY PRIOR mrn = mrn-1 AND PRIOR frn = frn