NEEDS
I want to create a table like the following in Oracle SQL:
COUNTRY NAME WMWHSE_ID DATE US CRD2 1 040620 GR WAZ 2 040620 CN KOL 3 040620 FR DEL 4 040620 ... ... US CRD2 1 030620 GR WAZ 2 030620 CN KOL 3 030620 FR DEL 4 030620 ... ...
Every warehouse in WMWHSE_ID will be printed with a DATE column for today’s date, and this will be repeated for DATE = yesterday, the day before yesterday, the day before that…etc until exactly 1 week ago. I have 124 warehouses total, so 124*7 = 868 rows total.
===
QUERY BLOCK
The following is my query. It uses the CONNECT BY LEVEL <= to achieve the 7 dates (in theory)
select SUBSTR(db_alias, 1, 2) AS COUNTRY, db_alias as NAME, To_Number(Regexp_Replace(Db_Logid, '[^0-9]', '')) As Wmwhse_Id, to_char(sysdate, 'yyyyMMdd')+1-level as ACTDATE from wmsadmin.pl_db, dual where db_alias not like '%BPV' and db_alias not like 'PRDO%' and db_alias not like 'ENTERPRISE' connect by level <=7 order by ACTDATE desc, WMWHSE_ID asc
(The GROUP BY is needed, because without it the table looks like:)
COUNTRY NAME WMWHSE_ID DATE US CRD2 1 040620 GR WAZ 2 040620 CN KOL 3 040620 FR DEL 4 040620 ... ... US CRD2 1 030620 US CRD2 1 030620 US CRD2 1 030620 US CRD2 1 030620 ... ...
===
THE ISSUE
The query time seems to grow exponentially with n in CONNECT BY LEVEL <= n. I ran some testing and got the following:
CONNECT BY LEVEL <= n ROWS SECONDS 1 124 2-6 2 248 10+? 3 372 110
Anything n=4 and up seems to just hang sqldeveloper completely. When n=7, I left the computer running for 30+ minutes and the query was still running.
What is causing this slowness? Is there a better way to go about implementing my table? Thanks for your time.
Advertisement
Answer
Why is query so slow? Because each iteration connects each row with 124 new rows. So at second level it is 124 * 124 rows, at fourth level you get 236421376 rows, at seventh level 450766669594624 rows. This is why you get duplicates. Also join with dual has no impact.
The solution is to correct connect by
part, add condition wmwhse_id = prior wmwhse_id
:
select country, name, wmwhse_id, trunc(sysdate) - level + 1 dt from pl_db connect by level <= 7 and prior wmwhse_id = wmwhse_id and prior sys_guid() is not null order by dt desc, wmwhse_id
It is even easier with recursive CTE, which is standard in most databases:
with r(country, name, wmwhse_id, dt, lvl) as ( select country, name, wmwhse_id, trunc(sysdate), 1 from pl_db union all select country, name, wmwhse_id, trunc(sysdate) - lvl, lvl + 1 from r where lvl < 7) select country, name, wmwhse_id, dt from r;
But simplest is to make cross join with 7 numbers, doesn’t matter how you generate them:
select country, name, wmwhse_id, trunc(sysdate) - trim(column_value) + 1 dt from pl_db cross join xmltable('1 to 7')
dbfiddle containing all three queries.