I need to add a column that assigns row numbers to each record - oracle
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
Related
Create a product base table by date
I want to create a query that returns number of active products by colander date. I don’t have calendar dim table in database. Current table Product_name|prod_id|start_date|end_date P1|1234|02/01/2020|30/05/2020 P1|2345|02/01/2020|31/12/9999 P1|3456|03/01/2020|31/12/9999 Expected Result Calander_date|product_name|active_base 01/01/2020|P1|0 02/01/2020|P1|2 03/01/2020|P1|3 01/06/2020|P1|2
Create your own calendar, then - either in the database, as a "real" table (row generator technique helps here), or as a CTE (as I did in the following example): SQL> with 2 test (product_name, prod_id, start_date, end_date) as 3 -- you have that table; don't type that 4 (select 'P1', 1234, date '2020-01-02', date '2020-05-30' from dual union all 5 select 'P1', 2345, date '2020-01-02', date '9999-12-31' from dual union all 6 select 'P1', 3456, date '2020-01-03', date '9999-12-31' from dual 7 ), 8 calendar (datum) as 9 -- create your own calendar table 10 (select date '2020-01-01' + level - 1 11 from dual 12 connect by level <= 10000 --> number of days you want in calendar 13 ) 14 -- final query - join! 15 select c.datum, 16 t.product_name, 17 count(*) active_base 18 from calendar c join test t on c.datum between t.start_date and t.end_date 19 group by c.datum, t.product_name 20 order by c.datum; DATUM PR ACTIVE_BASE ---------- -- ----------- 02/01/2020 P1 2 03/01/2020 P1 3 04/01/2020 P1 3 05/01/2020 P1 3 06/01/2020 P1 3 <snip> 28/05/2020 P1 3 29/05/2020 P1 3 30/05/2020 P1 3 31/05/2020 P1 2 01/06/2020 P1 2 02/06/2020 P1 2 <snip>
Oracle - How to know the load volume by times of a table?
I have some tables in Oracle and I would like to know the variations of the table by sections of time. I explain, I need a query/script to know how often data is loaded / updated in the table. example Can anyone give me ideas on how to do something like that or similar? Thanks!
Split a day to hours (CTEs times and periods) and apply aggregates to values stored in your_table (which is joined to "fabricated" hours). For example: SQL> with times as 2 (select trunc(sysdate) + (level - 1)/24 val 3 from dual 4 connect by level <= 25 5 ), 6 periods as 7 (select val val_from, 8 lead(val) over (order by val) val_to 9 from times 10 ), 11 your_table (date_column, ins, upd) as 12 (select trunc(sysdate) + 13/24 + 25/(24*60), 100, 18 from dual union all 13 select trunc(sysdate) + 13/24 + 25/(24*60), 225, null from dual union all 14 select trunc(sysdate) + 14/24 + 33/(24*60), 203, 112 from dual union all 15 select trunc(sysdate) + 15/24 + 15/(24*60), null, 687 from dual union all 16 select trunc(sysdate) + 15/24 + 18/(24*60), null, 987 from dual 17 ) 18 select to_char(p.val_from, 'hh24:mi') ||' - '|| 19 to_char(p.val_to , 'hh24:mi') period, 20 count(ins) cnt_insert, 21 count(upd) cnt_update 22 from periods p join your_table t on t.date_column >= p.val_from 23 and t.date_column < p.val_to 24 where p.val_to is not null 25 group by p.val_from, p.val_to 26 order by p.val_From; PERIOD CNT_INSERT CNT_UPDATE ------------- ---------- ---------- 13:00 - 14:00 2 1 14:00 - 15:00 1 1 15:00 - 16:00 0 2 SQL>
get particular day between a data range from giving table
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
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.
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