Max size in a connected by prior Oracle - 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
Related
Oracle Subquery while using count and max with join
Table COMPUTER: Table SUPPLIER: how to display the building location that has the most computers? i Have been trying several ways include subquery, joins, max, count but all not working and error keeps happending The result i pursueing is SUPPID SNAME SADDRESS MAKE COUNT(*) 125 Apple Sdn.Bhd 18 Jalan Duta Apple 3
For example (where sample data is in lines #1 - 12; query you might be interested in begins at line #13): SQL> with 2 -- sample data 3 computer (compid, make, suppid, locid) as 4 (select 13323, 'IBM' , 124, 333 from dual union all 5 select 13324, 'Apple', 125, 444 from dual union all 6 select 13325, 'Apple', 125, 444 from dual union all 7 select 13326, 'Apple', 125, 444 from dual 8 ), 9 supplier (suppid, sname, saddress) as 10 (select 124, 'IBM Sdn.Bhd' , '15 Jalan Duta' from dual union all 11 select 125, 'Apple Sdn.Bhd', '18 Jalan Duta' from dual 12 ), 13 comp_loc as 14 -- number of computers per location; RNK = 1 shows location with most computers 15 (select locid, 16 rank() over (order by count(*) desc) rnk, 17 count(*) cnt 18 from computer 19 group by locid 20 ) 21 select distinct s.suppid, s.sname, s.saddress, c.make, l.cnt 22 from supplier s join computer c on c.suppid = s.suppid 23 join comp_loc l on l.locid = c.locid 24 where l.rnk = 1; SUPPID SNAME SADDRESS MAKE CNT ---------- ------------- ------------- ----- ---------- 125 Apple Sdn.Bhd 18 Jalan Duta Apple 3 SQL>
On Oracle 12 and newer select s.suppid, s.sname, s.saddress, c.make, count(1) from COMPUTER c join SUPPLIER s on c.suppid = s.suppid group by s.suppid, s.sname, s.saddress, c.make order by count(1) desc fetch first 1 row only <-- this line will fetch you the top 1 line only You might also use "fetch first 1 row with ties" to output all the top manufacturers if there are many of them having same "count". E.g If IBM and Appl were having same amount of lines On Oracle version before 12 do the following: select * from (select s.suppid, s.sname, s.saddress, c.make, count(1) from comps c join suppls s on c.suppid = s.suppid group by s.suppid, s.sname, s.saddress, c.make order by count(1) desc) where rownum = 1; <-- this line will get you the top 1 manufacturer only PS. version of the oracle database can be obtained for example using: select version from v$instance;
count returns <<air>> when there's nothing to group by
Here's my SQL: select count(*) from sysdba.dw_cmap_arf_tmp left join SYSDBA.TABLE1 rrc on rrc.part_work_order = pwo left join sysdba.TABLE2 R on rrc.run_number = R.RUN_NUMBER where upper(run_type) like '%FEE%' group by pwo; When there's nothing to group by, count returns --air--. It's null nor is it blank. I have modified the above SQL as the following to prove. select '>>' || count(*) || '<<' as blah from sysdba.dw_cmap_arf_tmp left join SYSDBA.TABLE1 rrc on rrc.part_work_order = pwo left join sysdba.TABLE2 R on rrc.run_number = R.RUN_NUMBER where upper(run_type) like '%ANNEAL%' group by pwo; When I, however, write a statement above to perform an update, I got null. So tried coalesce, but got the same thing. Does anyone know what I can do to replace --air-- with null or 0? Thanks! P.S. I have did something research, but couldn't find anything...apologize in advance, if there's already a similar question out there. Thanks!
This is what you currently have (based on Scott's EMP table): as there's no department 50, you get no rows selected (which is the air you're talking about, I presume). SQL> with your_current_query as 2 (select count(*) cnt 3 from emp 4 where deptno = &deptno 5 group by job 6 ) 7 select cnt 8 from your_current_query; Enter value for deptno: 50 no rows selected Just to show that it actually returns something if there are some data there: SQL> / Enter value for deptno: 30 CNT ---------- 4 1 1 SQL> OK; now, to do something with the situation where there are no rows selected, use union with a "dummy" row selected from the DUAL table: SQL> with your_current_query as 2 (select count(*) cnt 3 from emp 4 where deptno = &deptno 5 group by job 6 ) 7 select cnt 8 from your_current_query 9 -- add this: if YOUR_CURRENT_QUERY doesn't return anything, union it with 10 -- a select from dual 11 union all 12 select 0 13 from dual 14 where 0 = (select count(*) from your_current_query); Enter value for deptno: 50 CNT ---------- 0 SQL> So: even though there are no employees in department 50, you got 0 as the result. Again, to show what happens when there are some rows: SQL> / Enter value for deptno: 30 CNT ---------- 4 1 1 SQL> Finally, your query - rewritten - would look like this: with your_current_query as (select count(*) cnt from sysdba.dw_cmap_arf_tmp left join SYSDBA.TABLE1 rrc on rrc.part_work_order = pwo left join sysdba.TABLE2 R on rrc.run_number = R.RUN_NUMBER where upper(run_type) like '%FEE%' group by pwo ) select cnt from your_current_query union all select 0 from dual where 0 = (select count(*) from your_Current_query); See if it helps.
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
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
3rd highest salary in oracle
I had been looking for the query to find the 3rd highest salary from the database (using Oracle database). I found the below query - SELECT * FROM ( SELECT e.*, row_number() over (order by sal DESC) rn FROM emp e ) WHERE rn = 3; I do not have oracle installed in my system, so I'm not try it out. But I want to know if the below query will work or not. If not, then why ? WITH Sal_sort AS (SELECT DISTINCT sal FROM salary ORDER BY sal DESC ) SELECT * FROM Salary S, Sal_sort SS WHERE S.Sal = SS.Sal AND SS.rownum = 3;
Input Data emp_no emp_fname emp_lname salary 1 aa bb 30 2 ee yy 31 3 rr uu 32 4 tt ii 33 5 tt ii 33 6 tt ii 33 7 tt ii 33 8 tt ii 30 9 tt ii 31 Example: select * from ee; select emp_no,salary ,dense_rank() over (order by salary ) dr from ee Output emp_no salary dr 1 30 1 8 30 1 9 31 2 2 31 2 3 32 3 4 33 4 5 33 4 6 33 4 7 33 4
So much easier in version 12 of the database and higher now. SELECT * FROM employees ORDER BY salary DESC OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY Tim talks about this feature here. And if you take a look at the plan, you can see it's not magic, the optimizer is using analytic functions to derive the results.
JUST ONE LINE select * from ( select salary,dense_rank() over (order by salary desc) rank from employees) where rank=3; or select * from ( select a.*,dense_rank() over (order by a.salary desc) rank from employees a) where rank=3;
Without Dense_rank() SELECT salary FROM employees ORDER BY salary DESC OFFSET 2 FETCH 1 NEXT ONE ROWS ONLY; With Dense_rank() SELECT salary FROM ( SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) as rank from employees ) WHERE rank = 3;