Related
I got a sample data as below in this cte_orders. Each row is a order and contains the user_id, zone_code and zone_name.
I need to rank the top 3 zone_code for each user_id, and I need the output be on columns.
Like this
|user_id | top1_zone | top2_zone | top3_zone |
|--------+-------------+-------------+-------------+
|1000 | 5555-ABCD | 4567-ZMNY | 7888-IXPO |
|--------+-------------+-------------+-------------+
|9999 | 3456-JJKL | 7688-HBGT | 5555-ABCD |
|--------+-------------+-------------+-------------+
Here's the SQL with the test data and the query that I'm trying.
The problem with this query is the result is given a row for each position of the rank.
(
SELECT 1 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 2 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 3 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 4 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 5 as order_id, '1000' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 6 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 7 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 8 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 9 as order_id, '1000' as user_id, '1999' as zone_code, 'LNKJ' as zone_name from dual union all
SELECT 10 as order_id, '1000' as user_id, '5677' as zone_code, 'OPLH' as zone_name from dual union all
SELECT 11 as order_id, '1000' as user_id, '7888' as zone_code, 'IXPO' as zone_name from dual union all
SELECT 12 as order_id, '9999' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 13 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 14 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 15 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 16 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 18 as order_id, '9999' as user_id, '1566' as zone_code, 'LNOI' as zone_name from dual union all
SELECT 19 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 20 as order_id, '9999' as user_id, '7654' as zone_code, 'NNJJ' as zone_name from dual union all
SELECT 21 as order_id, '9999' as user_id, '4433' as zone_code, 'NHJE' as zone_name from dual union all
SELECT 22 as order_id, '9999' as user_id, '4111' as zone_code, 'ABHJ' as zone_name from dual
)
select
user_id,
CASE WHEN rank_zones = 1 then zone_concat end as top1_zone,
CASE WHEN rank_zones = 2 then zone_concat end as top2_zone,
CASE WHEN rank_zones = 3 then zone_concat end as top3_zone
from
(
select
user_id,
zone_code||'-'||zone_name as zone_concat,
row_number() over (partition by user_id order by count(*) desc) rank_zones
from cte_orders
group by
user_id,
zone_code||'-'||zone_name
)
where rank_zones <= 3
group by
user_id,
CASE WHEN rank_zones = 1 then zone_concat end,
CASE WHEN rank_zones = 2 then zone_concat end,
CASE WHEN rank_zones = 3 then zone_concat end
order by user_id;
The output that i getting
|user_id | top1_zone | top2_zone | top3_zone |
|--------+-------------+-------------+-------------+
|1000 | 5555-ABCD | (null) | (null) |
|--------+-------------+-------------+-------------+
|1000 | (null) | 4567-ZMNY | (null) |
|--------+-------------+-------------+-------------+
|1000 | (null) | (null) | 7888-IXPO |
|--------+-------------+-------------+-------------+
|9999 | 3456-JJKL | (null) | (null) |
|--------+-------------+-------------+-------------+
|9999 | (null) | 7688-HBGT | (null) |
|--------+-------------+-------------+-------------+
|9999 | (null) | (null) | 5555-ABCD |
|--------+-------------+-------------+-------------+
How can i fix my query to get one row for each user_id without the nulls?
You can use oracle pivot as below:
with cte_orders as(
SELECT 1 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 2 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 3 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 4 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 5 as order_id, '1000' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 6 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 7 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 8 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 9 as order_id, '1000' as user_id, '1999' as zone_code, 'LNKJ' as zone_name from dual union all
SELECT 10 as order_id, '1000' as user_id, '5677' as zone_code, 'OPLH' as zone_name from dual union all
SELECT 11 as order_id, '1000' as user_id, '7888' as zone_code, 'IXPO' as zone_name from dual union all
SELECT 12 as order_id, '9999' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 13 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 14 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 15 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 16 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 18 as order_id, '9999' as user_id, '1566' as zone_code, 'LNOI' as zone_name from dual union all
SELECT 19 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 20 as order_id, '9999' as user_id, '7654' as zone_code, 'NNJJ' as zone_name from dual union all
SELECT 21 as order_id, '9999' as user_id, '4433' as zone_code, 'NHJE' as zone_name from dual union all
SELECT 22 as order_id, '9999' as user_id, '4111' as zone_code, 'ABHJ' as zone_name from dual
),
cte as(select
user_id,
zone_code||'-'||zone_name as zone_concat,
row_number() over (partition by user_id order by count(*) desc ) rank_zones
from cte_orders
group by
user_id,
zone_code||'-'||zone_name)
select user_id,"1" top1_zone, "2" top2_zone, "3" top3_zone
from cte
PIVOT (
MAX(ZONE_CONCAT) FOR rank_zones IN (1,2,3)
)
Output:
USER_ID
TOP1_ZONE
TOP2_ZONE
TOP3_ZONE
1000
5555-ABCD
4567-ZMNY
5599-HZTR
9999
3456-JJKL
5555-ABCD
7688-HBGT
db<>fiddle here
Or you can use decode in aggregation with group by:
with cte_orders as(
SELECT 1 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 2 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 3 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 4 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 5 as order_id, '1000' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 6 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 7 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
SELECT 8 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 9 as order_id, '1000' as user_id, '1999' as zone_code, 'LNKJ' as zone_name from dual union all
SELECT 10 as order_id, '1000' as user_id, '5677' as zone_code, 'OPLH' as zone_name from dual union all
SELECT 11 as order_id, '1000' as user_id, '7888' as zone_code, 'IXPO' as zone_name from dual union all
SELECT 12 as order_id, '9999' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
SELECT 13 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 14 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 15 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 16 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
SELECT 18 as order_id, '9999' as user_id, '1566' as zone_code, 'LNOI' as zone_name from dual union all
SELECT 19 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
SELECT 20 as order_id, '9999' as user_id, '7654' as zone_code, 'NNJJ' as zone_name from dual union all
SELECT 21 as order_id, '9999' as user_id, '4433' as zone_code, 'NHJE' as zone_name from dual union all
SELECT 22 as order_id, '9999' as user_id, '4111' as zone_code, 'ABHJ' as zone_name from dual
),
cte as(select
user_id,
zone_code||'-'||zone_name as zone_concat,
row_number() over (partition by user_id order by count(*) desc ) rank_zones
from cte_orders
group by
user_id,
zone_code||'-'||zone_name)
select user_id,
max(decode(rank_zones,1,ZONE_CONCAT)) top1_zone,
max(decode(rank_zones,2,ZONE_CONCAT)) top2_zone,
max(decode(rank_zones,3,ZONE_CONCAT)) top3_zone
from cte
group by user_id
Output:
USER_ID
TOP1_ZONE
TOP2_ZONE
TOP3_ZONE
1000
5555-ABCD
4567-ZMNY
5599-HZTR
9999
3456-JJKL
5555-ABCD
7688-HBGT
db<>fiddle here
How can i fix my query to get one row for each user_id without the nulls?
With a little bit of aggregation (and comments, as its consequence).
Sample data:
SQL> with cte_orders as (
2 SELECT 1 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
3 SELECT 2 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
4 SELECT 3 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
5 SELECT 4 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
6 SELECT 5 as order_id, '1000' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
7 SELECT 6 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
8 SELECT 7 as order_id, '1000' as user_id, '4567' as zone_code, 'ZMNY' as zone_name from dual union all
9 SELECT 8 as order_id, '1000' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
10 SELECT 9 as order_id, '1000' as user_id, '1999' as zone_code, 'LNKJ' as zone_name from dual union all
11 SELECT 10 as order_id, '1000' as user_id, '5677' as zone_code, 'OPLH' as zone_name from dual union all
12 SELECT 11 as order_id, '1000' as user_id, '7888' as zone_code, 'IXPO' as zone_name from dual union all
13 SELECT 12 as order_id, '9999' as user_id, '5599' as zone_code, 'HZTR' as zone_name from dual union all
14 SELECT 13 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
15 SELECT 14 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
16 SELECT 15 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
17 SELECT 16 as order_id, '9999' as user_id, '5555' as zone_code, 'ABCD' as zone_name from dual union all
18 SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
19 SELECT 17 as order_id, '9999' as user_id, '7688' as zone_code, 'HBGT' as zone_name from dual union all
20 SELECT 18 as order_id, '9999' as user_id, '1566' as zone_code, 'LNOI' as zone_name from dual union all
21 SELECT 19 as order_id, '9999' as user_id, '3456' as zone_code, 'JJKL' as zone_name from dual union all
22 SELECT 20 as order_id, '9999' as user_id, '7654' as zone_code, 'NNJJ' as zone_name from dual union all
23 SELECT 21 as order_id, '9999' as user_id, '4433' as zone_code, 'NHJE' as zone_name from dual union all
24 SELECT 22 as order_id, '9999' as user_id, '4111' as zone_code, 'ABHJ' as zone_name from dual
25 )
This is what makes what you ask for (the MAX aggregate function; lines #28 - 30):
26 select
27 user_id,
28 max(CASE WHEN rank_zones = 1 then zone_concat end) as top1_zone,
29 max(CASE WHEN rank_zones = 2 then zone_concat end) as top2_zone,
30 max(CASE WHEN rank_zones = 3 then zone_concat end) as top3_zone
31 from
32 (
33 select
34 user_id,
35 zone_code||'-'||zone_name as zone_concat,
36 row_number() over (partition by user_id order by count(*) desc) rank_zones
37 from cte_orders
38 group by
39 user_id,
40 zone_code||'-'||zone_name
41 )
42 where rank_zones <= 5
Comment CASE expressions (lines #45 - 47):
43 group by
44 user_id
45 -- CASE WHEN rank_zones = 1 then zone_concat end,
46 -- CASE WHEN rank_zones = 2 then zone_concat end,
47 -- CASE WHEN rank_zones = 3 then zone_concat end
48 order by user_id;
And the result is then:
USER TOP1_ZONE TOP2_ZONE TOP3_ZONE
---- --------- --------- ---------
1000 5555-ABCD 4567-ZMNY 5599-HZTR
9999 3456-JJKL 5555-ABCD 7688-HBGT
SQL>
I have the following rows:
DateTime Item_no Vo_No
2019-06-27 30322264 0118113
2017-12-27 00265929 0242712
2019-01-21 30322264 0515768
2017-12-27 00265929 0400026
2019-01-21 30322264 0569606
2018-09-25 00265929 0696864
2019-01-21 30317757 0696619
2019-06-27 30317757 0118113
2017-12-27 00265929 0242624
2017-01-24 00265929 0282971
2019-01-21 30317757 0386202
2019-01-21 30317757 0515706
2019-01-21 30322264 0696619
2017-12-27 00265929 0242625
2017-12-27 00265929 0395347
2017-12-27 00265929 0441449
2019-01-21 30317757 0569605
2017-12-27 00265929 0282972
2017-01-24 00265929 0282984
2019-01-21 30322264 0397256
I'm trying to get the distinct item no, with the latest date and the vo_no.
I have came up with the following query:
SELECT MAX(b.upd_dtime), b.item_no
FROM vo_item_t b JOIN (
SELECT a.vo_no, a.vo_state, a.vo_confirm_date, a.vo_order_date
FROM vo_order_t a) y ON y.vo_no = b.vo_no and b.item_No IN(30317757, 30322264, 00265929)
GROUP BY b.item_no
It works pretty well, I get the following result:
Date Item_No
2019-06-27 30322264
2019-06-27 30317757
2018-09-25 00265929
But as you can see, the vo_no is missing for each Item_No, so I tried the following:
SELECT MAX(b.upd_dtime), b.item_no, y.vo_no
FROM vo_item_t b JOIN (
SELECT a.vo_no, a.vo_state, a.vo_confirm_date, a.vo_order_date
FROM vo_order_t a) y ON y.vo_no = b.vo_no and b.item_No IN(30317757, 30322264, 00265929)
GROUP BY b.item_no, y.vo_no
As you can see, I added y.vo_no in the select and the group by. But the result from query is the following:
2019-06-27 30322264 0118113
2017-12-27 00265929 0242712
2019-01-21 30322264 0515768
2017-12-27 00265929 0400026
2019-01-21 30322264 0569606
2018-09-25 00265929 0696864
2019-01-21 30317757 0696619
2019-06-27 30317757 0118113
2017-12-27 00265929 0242624
2017-01-24 00265929 0282971
2019-01-21 30317757 0386202
2019-01-21 30317757 0515706
2019-01-21 30322264 0696619
2017-12-27 00265929 0242625
2017-12-27 00265929 0395347
2017-12-27 00265929 0441449
2019-01-21 30317757 0569605
2017-12-27 00265929 0282972
2017-01-24 00265929 0282984
2019-01-21 30322264 0397256
The distinct on item_no don't work any more.
So my question is: how can I also get the vo_no for the distinct item_no?
You could use an inner join to solve this. There could be an easier way but for now this should work
select x.*,y.vo_no from (
SELECT MAX(b.upd_dtime) as MaxT, b.item_no as Item_NO
FROM vo_item_t b JOIN (
SELECT a.vo_no, a.vo_state, a.vo_confirm_date, a.vo_order_date
FROM vo_order_t a) y ON y.vo_no = b.vo_no and b.item_No IN(30317757, 30322264, 00265929)
GROUP BY b.item_no
)x
INNER JOIN
vo_order_t y
ON x.MaxT = y.Date
AND x.Item_NO = y.Item_No
There's also this solution:
select item_no, max(datetime) mxdt, max(vo_no) keep(dense_rank first order by datetime desc) vo_no
from (select '2019-06-27' datetime, 30322264 item_no, 0118113 vo_no from dual union
select '2017-12-27', 00265929 , 0242712 from dual union
select '2019-01-21', 30322264 , 0515768 from dual union
select '2017-12-27', 00265929 , 0400026 from dual union
select '2019-01-21', 30322264 , 0569606 from dual union
select '2018-09-25', 00265929 , 0696864 from dual union
select '2019-01-21', 30317757 , 0696619 from dual union
select '2019-06-27', 30317757 , 0118113 from dual union
select '2017-12-27', 00265929 , 0242624 from dual union
select '2017-01-24', 00265929 , 0282971 from dual union
select '2019-01-21', 30317757 , 0386202 from dual union
select '2019-01-21', 30317757 , 0515706 from dual union
select '2019-01-21', 30322264 , 0696619 from dual union
select '2017-12-27', 00265929 , 0242625 from dual union
select '2017-12-27', 00265929 , 0395347 from dual union
select '2017-12-27', 00265929 , 0441449 from dual union
select '2019-01-21', 30317757 , 0569605 from dual union
select '2017-12-27', 00265929 , 0282972 from dual union
select '2017-01-24', 00265929 , 0282984 from dual union
select '2019-01-21', 30322264 , 0397256 from dual)
group by item_no
order by item_no
which gives this on your data:
ITEM_NO MXDT VO_NO
---------- ---------- ----------
265929 2018-09-25 696864
30317757 2019-06-27 118113
30322264 2019-06-27 118113
(look up this doc page )
Good afternoon, tell me, please, how to break the interval between dates line by line.
I have a table:
with data as (
SELECT 1 AS id, 232 AS status, to_date('21.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('25.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('25.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('27.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 233 AS status, to_date('27.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('20.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('23.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('23.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('26.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 259 AS status, to_date('26.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual)
select * from data
I want to get the result:
with result_data as (
SELECT 1 AS id, 232 AS status, to_date('21.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 232 AS status, to_date('22.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 232 AS status, to_date('23.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 232 AS status, to_date('24.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('25.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('26.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 233 AS status, to_date('27.08.2019') AS dte FROM dual
UNION ALL
SELECT 1 AS id, 233 AS status, to_date('28.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('20.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('21.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('22.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('23.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('24.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('25.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 259 AS status, to_date('26.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 259 AS status, to_date('27.08.2019') AS dte FROM dual
UNION ALL
SELECT 2 AS id, 259 AS status, to_date('28.08.2019') AS dte FROM dual)
select * from result_data
I tried to do through this request, but nothing worked for me
SELECT trunc(t.start_dte) + level -1 dte FROM (select * from data
where data.end_dte != to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss')) t
CONNECT BY level < trunc(t.end_dte) - trunc(t.start_dte) + 1
There are a lot of rows in the table, are there any thoughts on how to do this rationally?
I've commented the date ranges that finish in year 2999 on your example for the result to be more clear:
with
maxdays
as
(select max( end_dte-start_dte )+1 days from
(SELECT 1 AS id, 232 AS status, to_date('21.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('25.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('25.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('27.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
--UNION ALL
--SELECT 1 AS id, 233 AS status, to_date('27.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('20.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('23.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('23.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('26.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
--UNION ALL
--SELECT 2 AS id, 259 AS status, to_date('26.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
)
),
data
as
(select level-1 l from maxdays connect by level <= days )
select id,status,start_dte+l
from data, (SELECT 1 AS id, 232 AS status, to_date('21.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('25.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('25.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('27.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
--UNION ALL
--SELECT 1 AS id, 233 AS status, to_date('27.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('20.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('23.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('23.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('26.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
--UNION ALL
--SELECT 2 AS id, 259 AS status, to_date('26.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
)
where l <= end_dte-start_dte
order by 2,3,1;
Ok i'm understand now.
The process is called data densification. You can google for it.
with data as (
SELECT 1 AS id, 232 AS status, to_date('21.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('25.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 235 AS status, to_date('25.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('27.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 1 AS id, 233 AS status, to_date('27.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 301 AS status, to_date('20.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('23.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 264 AS status, to_date('23.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('26.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual
UNION ALL
SELECT 2 AS id, 259 AS status, to_date('26.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual)
,ref_dates as ( select (select min(trunc(start_dte)) from data) + level dd from dual
connect by (select min(trunc(start_dte)) from data) + level
<= (select max(trunc(end_dte)) from data where end_dte != to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss')) )
select id,status,dd vol from data partition by (status)
left join ref_dates x on (dd >=trunc(start_dte) and dd <= trunc(end_dte) )
where 1=1
order by status,dd
I think the correct answer is not producing the desired result.
You can try the following query:
SQL> with data as (
2 SELECT 1 AS id, 232 AS status, to_date('21.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('25.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual UNION ALL
3 SELECT 1 AS id, 235 AS status, to_date('25.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('27.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual UNION ALL
4 SELECT 1 AS id, 233 AS status, to_date('27.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual UNION ALL
5 SELECT 2 AS id, 301 AS status, to_date('20.08.2019 10:47:39','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('23.08.2019 0:19:37','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual UNION ALL
6 SELECT 2 AS id, 264 AS status, to_date('23.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('26.08.2019 0:25:11','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual UNION ALL
7 SELECT 2 AS id, 259 AS status, to_date('26.08.2019 19:11:00','dd.mm.yyyy hh24:mi:ss') AS start_dte, to_date('31.12.2999 23:59:59','dd.mm.yyyy hh24:mi:ss') AS end_dte FROM dual)
8 ,D AS (select ID, STATUS, TRUNC(start_dte) AS start_dte, TRUNC(end_dte) AS end_dte from data)
9 SELECT
10 ID,
11 STATUS,
12 START_DTE
13 FROM
14 (
15 SELECT DISTINCT
16 ID,
17 STATUS,
18 START_DTE + LEVEL - 1 AS START_DTE,
19 END_DTE
20 FROM
21 D
22 CONNECT BY
23 LEVEL <= TRUNC(CASE
24 WHEN END_DTE = DATE '2999-12-31' THEN START_DTE + 1
25 ELSE END_DTE
26 END) - START_DTE + 1
27 )
28 WHERE
29 START_DTE != END_DTE
30 ORDER BY
31 ID,
32 START_DTE
33 ;
ID STATUS START_DTE
---------- ---------- ---------
1 232 21-AUG-19
1 232 22-AUG-19
1 232 23-AUG-19
1 232 24-AUG-19
1 235 25-AUG-19
1 235 26-AUG-19
1 233 27-AUG-19
1 233 28-AUG-19
2 301 20-AUG-19
2 301 21-AUG-19
2 301 22-AUG-19
ID STATUS START_DTE
---------- ---------- ---------
2 264 23-AUG-19
2 264 24-AUG-19
2 264 25-AUG-19
2 259 26-AUG-19
2 259 27-AUG-19
16 rows selected.
SQL>
Cheers!!
I have the following query
select paaf.assignment_id,
paaf.position_id,
paaf.effective_start_date effective_start_date,
paaf.effective_end_date effective_end_date
from per_all_assignments_f paaf
where paaf.position_id is not null
and paaf.assignment_type in ('E', 'C')
and paaf.primary_flag = 'Y'
and paaf.assignment_number like '209384%'
order by 3
Which returns
"assignment_id" "position_id" "effective_start_date" "effective_end_date"
6518 5323 01/01/2013 28/02/2014
6518 8133 01/03/2014 30/06/2014
6518 8133 01/07/2014 31/10/2015
6518 239570 01/11/2015 15/11/2015
6518 239570 16/11/2015 31/12/2015
6518 8133 01/01/2016 27/07/2016
6518 8133 28/07/2016 31/12/4712
I grouped this using:
select paaf.assignment_id,
paaf.position_id,
min(paaf.effective_start_date) effective_start_date,
max(paaf.effective_end_date) effective_end_date
from per_all_assignments_f paaf
where paaf.position_id is not null
and paaf.assignment_type in ('E', 'C')
and paaf.primary_flag = 'Y'
and paaf.assignment_number like '209384%'
group by paaf.assignment_id, paaf.position_id
Which returns:
"assignment_id" "position_id" "effective_start_date" "effective_end_date"
6518 5323 01/01/2013 28/02/2014
6518 8133 01/03/2014 31/12/4712
6518 239570 01/11/2015 31/12/2015
But I need a query that returns
"assignment_id" "position_id" "effective_start_date" "effective_end_date"
6518 5323 01/01/2013 28/02/2014
6518 8133 01/03/2014 31/10/2015
6518 239570 01/11/2015 31/12/2015
6518 8133 01/01/2016 31/12/4712
That is to say the position_id of 8133 must have two rows since there are two sections chronologically that must be grouped into 2 rows and not 1 (for 8133).
Is there some way of accomplishing this using the date order?
The answer turned out to be:
with paaf as
(
select paaf.assignment_id,
paaf.position_id,
paaf.effective_start_date effective_start_date,
paaf.effective_end_date effective_end_date
from per_all_assignments_f paaf
where paaf.position_id is not null
and paaf.assignment_type in ('E', 'C')
and paaf.primary_flag = 'Y'
-- and paaf.assignment_number like '209384%'
order by 1, 3
)
select paaf2.assignment_id,
paaf2.position_id,
min(paaf2.effective_start_date) as effective_start_date,
max(paaf2.effective_end_date) as effective_end_date
from (
select paaf.*,
row_number() over (order by paaf.assignment_id, paaf.effective_start_date) as seqnum,
row_number() over (partition by paaf.assignment_id, paaf.position_id order by paaf.assignment_id, paaf.effective_start_date) as seqnum_p
from paaf
) paaf2
group by (paaf2.seqnum - paaf2.seqnum_p), paaf2.assignment_id, paaf2.position_id
This is a gaps-and-islands problem. There are different approaches, but a simple one uses a difference of row number:
with paaf as (<your first query here>
)
select paaf.assignment_id,
paaf.position_id,
min(paaf.effective_start_date) as effective_start_date,
max(paaf.effective_end_date) as effective_end_date
from (select paaf.*,
row_number() over (order by effective_start_date) as seqnum,
row_number() over (partition by position_id order by effective_start_date) as seqnum_p
from paaf
) paaf
group by (seqnum - seqnum_p), position_id, assignment_id;
I have a table which i want to only output 1 field from so it can be part of another queries WHERE statement (WHERE SID IN (THIS NEW QUERY)).
However because of this i can only include SID in the SELECT, but this is removing that is needed to make the distinct count work.
SO SELECT * FROM Tablea gives me:
SID deta detb
22222 8159 3763
22222 8159 3763
44444 4739 6135
44444 4739 6135
44444 4739 6134
44444 4739 6135
55555 5937 0223
55555 5936 0223
66666 8577 9497
66666 8577 9497
66666 8577 9497
66666 8576 9496
66666 8577 9497
88888 3595 0919
88888 3595 0919
88888 3595 0919
88888 3595 0914
77777 5678 3456
Then SELECT DISTINCT SID, deta, detb FROM Tablea gives me:
SID deta detb
22222 8159 3763
44444 4739 6134
44444 4739 6135
55555 5936 0223
55555 5937 0223
66666 8576 9496
66666 8577 9497
88888 3595 0914
88888 3595 0919
77777 5678 3456
The data i want is this:
SID deta detb
44444 4739 6134
44444 4739 6135
55555 5936 0223
55555 5937 0223
66666 8576 9496
66666 8577 9497
88888 3595 0914
88888 3595 0919
Which can be done by using a count of distinct, however my final output i want is this:
SID
44444
55555
66666
88888
But i cant achieve it when only outputting 1 field.
Use group by:
SELECT SID
FROM Tablea
GROUP BY SID
HAVING COUNT(DISTINCT deta || ':' || detab) > 1;
If you actually wanted the full rows (instead of the SID values), then use window functions:
SELECT a.*
FROM (SELECT a.*, COUNT(DISTINCT deta || ':' || detab) OVER (PARTITION BY SID) as cnt
FROM tablea a
) a
WHERE cnt > 1;
I would do it like this:
with sample_data (SID, deta, detb) as (select 22222, 8159, 3763 from dual union all
select 22222, 8159, 3763 from dual union all
select 44444, 4739, 6135 from dual union all
select 44444, 4739, 6135 from dual union all
select 44444, 4739, 6134 from dual union all
select 44444, 4739, 6135 from dual union all
select 55555, 5937, 0223 from dual union all
select 55555, 5936, 0223 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8577, 9497 from dual union all
select 66666, 8576, 9496 from dual union all
select 66666, 8577, 9497 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0919 from dual union all
select 88888, 3595, 0914 from dual union all
select 77777, 5678, 3456 from dual)
--- end of mimicking your sample data
select sid
from (select distinct sid,
deta,
detb
from sample_data)
group by sid
having count(*) > 1;
SID
----------
44444
66666
55555
88888