|
задачка с запросом
#39586941
Ссылка:
Ссылка на сообщение:
Ссылка с названием темы:
Ссылка на профиль пользователя:
|
|
|
|
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
drop table aaa;
create table aaa (date_from date, date_to date, c1 integer, c2 integer, c3 integer);
drop table bbb;
create table bbb (date_from date, date_to date, c4 integer, c5 integer, c6 integer);
insert into aaa values (to_date('20010101','YYYYMMDD'),to_date('20010331','YYYYMMDD'),1,0,0);
insert into aaa values (to_date('20030601','YYYYMMDD'),to_date('20041231','YYYYMMDD'),7,0,3);
insert into aaa values (to_date('20080101','YYYYMMDD'),null,1,8,0);
insert into bbb values (to_date('20020101','YYYYMMDD'),to_date('20020731','YYYYMMDD'),1,0,0);
insert into bbb values (to_date('20030101','YYYYMMDD'),to_date('20040131','YYYYMMDD'),1,0,7);
insert into bbb values (to_date('20060101','YYYYMMDD'),null,1,0,4);
commit;
1. 2. 3. 4. 5. 6. 7. 8. 9.
Data from aaa
1. 01/01/2001 31/03/2001 1 0 0
2. 01/06/2003 31/12/2004 7 0 3
3. 01/01/2008 1 8 0
Data from bbb
1. 01/01/2002 31/07/2002 1 0 0
2. 01/01/2003 31/01/2004 1 0 7
3. 01/01/2006 1 0 4
Хочется получить следующее
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
Periods to get
/*
01/01/2001 31/03/2001 - record 1 from aaa
01/01/2002 31/07/2002 - record 1 from bbb
01/01/2003 31/05/2003 - date_from record 2 from bbb, date_to is calculated based on merging periods from table aaa and bbb. Since date_to for record 2 from bbb is 31/01/2004, but there are another records from aaa/bbb that are in that period, we need to split the records, that why date_to is 31/05/2003, that's day before 01/06/2003 - record 2 table aaa
01/06/2003 31/01/2004 - same as above logic, 01/06/2003 record 2 table aaa till 31/01/2004
01/02/2004 31/12/2004 - day after previous record (31/01/2004) 01/02/2004 till the last period 31/12/2004 - record 2 table aaa
01/01/2006 31/12/2007 - record 3 bbb till day before 01/01/2008(record 3 table aaa) 31/12/2007
01/01/2008 - last one, record 3 table aaa
*/
Same with business data :
/*
01/01/2001 31/03/2001 1 0 0
01/01/2002 31/07/2002 1 0 0
01/01/2003 31/05/2003 1 0 7
01/06/2003 31/01/2004 7 0 3 1 0 7
01/02/2004 31/12/2004 7 0 3
01/01/2006 31/12/2007 1 0 4
01/01/2008 1 8 0 1 0 4
*/
I have the following query that give me above result, but is big and hard to read. And for complex input tables it can be more complex.
Please help to find best algorithm for above requirements. I also look at some PLSQL code (procedure) having as parameter a cursor (for table aaa and bbb) and returning merging periods based on input.
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54.
WITH w0
AS (
SELECT date_from date_, 'df' cat FROM aaa
UNION
SELECT date_from, 'df' FROM bbb
UNION
SELECT date_to date_, 'dt' cat FROM aaa
UNION
SELECT date_to, 'dt' FROM bbb
)
, w1 AS (
SELECT w0.*
, LEAD(date_) OVER (ORDER BY date_) lead_dt
, LEAD(cat) OVER (ORDER BY date_) lead_cat
FROM w0
)
, w2 AS (
SELECT *
FROM (
SELECT date_ + CASE WHEN cat = 'dt' THEN 1 ELSE 0 END df
, lead_dt - CASE WHEN lead_cat = 'df' THEN 1 ELSE 0 END dt
FROM w1
)
WHERE 1=1
AND df <= NVL(dt,df)
)
SELECT w2.df
, w2.dt
, aaa.c1
, aaa.c2
, aaa.c3
, bbb.c4
, bbb.c5
, bbb.c6
FROM w2
LEFT OUTER JOIN aaa
ON 1=1
AND ((w2.df BETWEEN aaa.date_from AND NVL(aaa.date_to,w2.df))
OR
(aaa.date_from BETWEEN w2.df AND NVL(w2.dt,aaa.date_from))
)
LEFT OUTER JOIN bbb
ON 1=1
AND ((w2.df BETWEEN bbb.date_from AND NVL(bbb.date_to,w2.df))
or
(bbb.date_from BETWEEN w2.df AND NVL(w2.dt,bbb.date_from))
)
WHERE c1 IS NOT NULL
OR c2 IS NOT NULL
OR c3 IS NOT NULL
OR c4 IS NOT NULL
OR c5 IS NOT NULL
OR c6 IS NOT NULL
;
|
|
|