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

Resources