问题
Oracle 12c database.
I have a car sales table:
CREATE TABLE CAR_SALES
( NUM_CARS NUMBER(10,0),
EQUIPMENT_TYPE VARCHAR2(100),
LOCATION VARCHAR2(500),
SOLD_DATE DATE
) ;
--Insert sample data
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('8','Rovers','coventry','07-SEP-19 10:00:12');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('1','Rovers','coventry','07-SEP-19 10:00:45');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('9','Jaguars','coventry','07-SEP-19 06:00:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('7','Rovers','leamington','30-AUG-19 13:10:13');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('10','Trans Am','leamington','30-AUG-19 09:00:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('2','Trans Am','leamington','30-AUG-19 13:10:48');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('8','Rovers','coventry','06-SEP-19 18:00:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('4','Rovers','leamington','06-SEP-19 09:00:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('100','Trans Am','leamington','06-SEP-19 08:59:45');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('1','corvette','leamington','06-SEP-19 09:00:10');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('2','Toyota','coventry','06-SEP-19 10:00:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('15','Rovers','coventry','07-SEP-19 11:05:00');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('2','Jaguars','coventry','07-SEP-19 17:02:07');
insert into car_sales (num_cars,equipment_type,location,sold_date) values ('3','Trans Am','leamington','30-AUG-19 13:10:25');
commit;
I need to select only the sales (dates of sales) that have occurred within 1 minute by a location.
I have created the following sql example, but it is not displaying only the records that share a sales date within 1 minute for a location, it is showing all the records for a location. Also, is it possible to create a listagg of the result set by location|equipment_type for matching dates within 1 minute? I don't know how I would get the results then have those results display like:
For the records that are within 1 minute:
coventry 07-SEP-19 10:00:45 Rovers
coventry 07-SEP-19 10:00:12 Rovers
Listagg would be:
LOCATION listagg(EQUIPMENT_TYPE)
coventry Rovers,Rovers
-- the equipment_type in this example just happens to be rover,rover, it would be whatever equipment_type is joined by the matching 1 minute sales.
SQL>
select location,sold_date,equipment_type,num_cars
from car_sales c
where exists( select 'X'
from car_sales x
where c.location=x.location
and c.equipment_type=x.equipment_type
and c.sold_date between x.sold_date - interval '1' MINUTE
and x.sold_date + interval '1' MINUTE
)
group by location,sold_date,equipment_type,num_cars
order by sold_date desc;
How could I create the correct results and make a listagg of the results of equipment_types by location that have sales within 60 seconds.
Thank you in advance. Gilly
回答1:
You can use LAG
/LEAD
analytic functions to compare the previous and next rows to determine if they are within a minute of the current row:
SELECT location,
LISTAGG( equipment_type, ',' )
WITHIN GROUP ( ORDER BY sold_date )
AS equipment_types,
LISTAGG( TO_CHAR( sold_date, 'HH24:MI:SS' ), ',' )
WITHIN GROUP ( ORDER BY sold_date )
AS sold_dates
FROM (
SELECT num_cars,
equipment_type,
location,
sold_date,
CASE
WHEN within_minute_of_prev = 1 OR within_minute_of_next = 1
THEN SUM(
CASE
WHEN within_minute_of_prev = 0 AND within_minute_of_next = 1
THEN 1
ELSE 0
END
) OVER ( PARTITION BY location ORDER BY sold_date )
END AS grp
FROM (
SELECT c.*,
CASE
WHEN ( sold_date
- LAG( sold_date ) OVER ( PARTITION BY location ORDER BY sold_date )
) DAY TO SECOND
<= INTERVAL '1' MINUTE
THEN 1
ELSE 0
END AS within_minute_of_prev,
CASE
WHEN ( LEAD( sold_date ) OVER ( PARTITION BY location ORDER BY sold_date )
- sold_date
) DAY TO SECOND
<= INTERVAL '1' MINUTE
THEN 1
ELSE 0
END AS within_minute_of_next
FROM car_sales c
)
)
WHERE grp IS NOT NULL
GROUP BY location, grp;
Which, for your sample data:
CREATE TABLE CAR_SALES ( NUM_CARS, EQUIPMENT_TYPE, LOCATION, SOLD_DATE ) AS
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 9, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, 'Rovers', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 10, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'Rovers', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 100, 'Trans Am', 'leamington', DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'corvette', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Toyota', 'coventry', DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 15, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL;
Outputs:
LOCATION | EQUIPMENT_TYPES | SOLD_DATES :--------- | :----------------------- | :------------------------- coventry | Rovers,Rovers | 10:00:12,10:00:45 leamington | Rovers,Trans Am,Trans Am | 13:10:13,13:10:25,13:10:48 leamington | Trans Am,Rovers,corvette | 08:59:45,09:00:00,09:00:10
db<>fiddle here
Update
A much shorter Oracle 12c query uses MATCH_RECOGNIZE
:
SELECT location,
LISTAGG( equipment_type, ',' )
WITHIN GROUP ( ORDER BY sold_date )
AS equipment_types,
LISTAGG( TO_CHAR( sold_date, 'HH24:MI:SS' ), ',' )
WITHIN GROUP ( ORDER BY sold_date )
AS sold_times
FROM car_sales
MATCH_RECOGNIZE (
PARTITION BY location
ORDER BY sold_date
MEASURES
MATCH_NUMBER() AS mno
ALL ROWS PER MATCH
PATTERN (A B+)
DEFINE
B AS B.sold_date <= PREV(B.sold_date) + interval '1' minute
)
GROUP BY location, mno
ORDER BY location, mno;
Which, for the test data:
CREATE TABLE CAR_SALES ( NUM_CARS, EQUIPMENT_TYPE, LOCATION, SOLD_DATE ) AS
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 9, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, 'Rovers', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 10, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'Rovers', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 100, 'Trans Am', 'leamington', DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'corvette', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Toyota', 'coventry', DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 15, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL;
Outputs:
LOCATION | EQUIPMENT_TYPES | SOLD_TIMES :--------- | :-------------------------- | :---------------------------------- coventry | Rovers,Rovers,Rovers,Rovers | 10:00:12,10:00:45,10:01:15,10:01:30 leamington | Rovers,Trans Am,Trans Am | 13:10:13,13:10:25,13:10:48 leamington | Trans Am,Rovers,corvette | 08:59:45,09:00:00,09:00:10
db<>fiddle here
回答2:
I haven't got an answer how should be aggregated a chain of several rows where each of them is less than a minute before previous one, for example
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL
Since you have already a solution which aggregates all such values into one group(ie 10:01:30-10:00:12 > 1 minute, but they are still in the same group), I'll show how to get groups where the maximum difference between first and last sales <= 1 minute.
In this case it's better to use analytic functions with range between current row and interval '1' minute following
. For example, for each sale we can easily get how many sales we have in the same location in next minute:
with CAR_SALES ( NUM_CARS, EQUIPMENT_TYPE, LOCATION, SOLD_DATE ) AS (
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 9, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, 'Rovers', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 10, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'Rovers', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 100, 'Trans Am', 'leamington', DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'corvette', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Toyota', 'coventry', DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 15, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL
)
SELECT
location,
num_cars,
equipment_type,
sold_date,
count(*)over(partition by LOCATION order by SOLD_DATE range between current row and interval'1' minute following) cnt
from car_sales
order by location,sold_date;
I've added a couple additional rows to make it easier to see the difference. Results:
LOCATION NUM_CARS EQUIPMEN SOLD_DATE CNT
---------- ---------- -------- ------------------- ----------
coventry 2 Toyota 2019-09-06 10:00:00 1
coventry 8 Rovers 2019-09-06 18:00:00 1
coventry 9 Jaguars 2019-09-07 06:00:00 1
coventry 1 Rovers 2019-09-07 10:00:12 2
coventry 2 Rovers 2019-09-07 10:00:45 3
coventry 3 Rovers 2019-09-07 10:01:15 2
coventry 3 Rovers 2019-09-07 10:01:30 1
coventry 15 Rovers 2019-09-07 11:05:00 1
coventry 2 Jaguars 2019-09-07 17:02:07 1
leamington 10 Trans Am 2019-08-30 09:00:00 1
leamington 7 Rovers 2019-08-30 13:10:13 3
leamington 3 Trans Am 2019-08-30 13:10:25 2
leamington 2 Trans Am 2019-08-30 13:10:48 1
leamington 100 Trans Am 2019-09-06 08:59:45 3
leamington 4 Rovers 2019-09-06 09:00:00 2
leamington 1 corvette 2019-09-06 09:00:10 1
16 rows selected.
Moreover, we can easily check also preceding rows and filter only those rows which have cnt_preceding>1 or cnt_following>1, ie rows which have neighbors <= 1 minute:
select *
from (
SELECT
location,
num_cars,
equipment_type,
sold_date,
count(*)over(partition by LOCATION order by SOLD_DATE range between interval'1' minute preceding and current row) cnt_preceding,
count(*)over(partition by LOCATION order by SOLD_DATE range between current row and interval'1' minute following) cnt_following
from car_sales
)
where
cnt_preceding > 1
or cnt_following > 1
order by location, sold_date;
Results: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=000865dd639ab8d6d6e9fbf64100fcf0
LOCATION NUM_CARS EQUIPMEN SOLD_DATE CNT_PRECEDING CNT_FOLLOWING
---------- ---------- -------- ------------------- ------------- -------------
coventry 1 Rovers 2019-09-07 10:00:12 1 2
coventry 2 Rovers 2019-09-07 10:00:45 2 3
coventry 3 Rovers 2019-09-07 10:01:15 2 2
coventry 3 Rovers 2019-09-07 10:01:30 3 1
leamington 7 Rovers 2019-08-30 13:10:13 1 3
leamington 3 Trans Am 2019-08-30 13:10:25 2 2
leamington 2 Trans Am 2019-08-30 13:10:48 3 1
leamington 100 Trans Am 2019-09-06 08:59:45 1 3
leamington 4 Rovers 2019-09-06 09:00:00 2 2
leamington 1 corvette 2019-09-06 09:00:10 3 1
So the only thing we need now is to aggregate them by non-overlapping intervals <= 1 minute. And I'll show it using another approach - MATCH_RECOGNIZE clause:
with CAR_SALES ( NUM_CARS, EQUIPMENT_TYPE, LOCATION, SOLD_DATE ) AS (
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 9, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, 'Rovers', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 10, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'Rovers', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 100, 'Trans Am', 'leamington', DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'corvette', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Toyota', 'coventry', DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 15, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL
)
select *
from car_sales
match_recognize (
partition by location
order by sold_date
MEASURES
FIRST(A.SOLD_DATE) dt_strt,
LAST(SOLD_DATE) dt_end,
MATCH_NUMBER() AS mno,
CLASSIFIER() AS cls
ALL ROWS PER MATCH
PATTERN (A B+)
DEFINE
B AS B.sold_date < first(A.sold_date) + interval '1' minute
)
order by location, sold_date
;
Results:
LOCATION SOLD_DATE DT_STRT DT_END MNO CLS NUM_CARS EQUIPMEN
---------- ------------------- ------------------- ------------------- ---------- ----- ---------- --------
coventry 2019-09-07 10:00:12 2019-09-07 10:00:12 2019-09-07 10:00:12 1 A 1 Rovers
coventry 2019-09-07 10:00:45 2019-09-07 10:00:12 2019-09-07 10:00:45 1 B 2 Rovers
coventry 2019-09-07 10:01:15 2019-09-07 10:01:15 2019-09-07 10:01:15 2 A 3 Rovers
coventry 2019-09-07 10:01:30 2019-09-07 10:01:15 2019-09-07 10:01:30 2 B 3 Rovers
leamington 2019-08-30 13:10:13 2019-08-30 13:10:13 2019-08-30 13:10:13 1 A 7 Rovers
leamington 2019-08-30 13:10:25 2019-08-30 13:10:13 2019-08-30 13:10:25 1 B 3 Trans Am
leamington 2019-08-30 13:10:48 2019-08-30 13:10:13 2019-08-30 13:10:48 1 B 2 Trans Am
leamington 2019-09-06 08:59:45 2019-09-06 08:59:45 2019-09-06 08:59:45 2 A 100 Trans Am
leamington 2019-09-06 09:00:00 2019-09-06 08:59:45 2019-09-06 09:00:00 2 B 4 Rovers
leamington 2019-09-06 09:00:10 2019-09-06 08:59:45 2019-09-06 09:00:10 2 B 1 corvette
10 rows selected.
As you can see MNO return MATCH_NUMBER(), ie number of the group in this location, so now we can easily aggregates these groups:
with CAR_SALES ( NUM_CARS, EQUIPMENT_TYPE, LOCATION, SOLD_DATE ) AS (
SELECT 1, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 9, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 7, 'Rovers', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 10, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'Rovers', 'coventry', DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'Rovers', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 100, 'Trans Am', 'leamington', DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'corvette', 'leamington', DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Toyota', 'coventry', DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 15, 'Rovers', 'coventry', DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'Jaguars', 'coventry', DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'Trans Am', 'leamington', DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL
)
,matches as (
select *
from car_sales
match_recognize (
partition by location
order by sold_date
MEASURES
FIRST(A.SOLD_DATE) dt_strt,
LAST(SOLD_DATE) dt_end,
MATCH_NUMBER() AS mno,
CLASSIFIER() AS cls
ALL ROWS PER MATCH
PATTERN (A B+)
DEFINE
B AS B.sold_date < first(A.sold_date) + interval '1' minute
)
)
select
location,
mno,
dt_strt,
listagg(EQUIPMENT_TYPE,',')
within group(order by sold_date) EQUIPMENT_TYPEs,
listagg(to_char(sold_date,'hh24:mi:ss'),',')
within group(order by sold_date) sold_dates
from matches
group by
location,
mno,
dt_strt
order by 1,2
;
Full test case with results: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=d2594a250f9adb5a9f290d7f72be2e05
LOCATION MNO DT_STRT EQUIPMENT_TYPES SOLD_DATES
---------- ---------- ------------------- ---------------------------------------- --------------------------------------------------
coventry 1 2019-09-07 10:00:12 Rovers,Rovers 10:00:12,10:00:45
coventry 2 2019-09-07 10:01:15 Rovers,Rovers 10:01:15,10:01:30
leamington 1 2019-08-30 13:10:13 Rovers,Trans Am,Trans Am 13:10:13,13:10:25,13:10:48
leamington 2 2019-09-06 08:59:45 Trans Am,Rovers,corvette 08:59:45,09:00:00,09:00:10
回答3:
You can use LISTAGG(equipment_type,',') WITHIN GROUP (ORDER BY <sold_date_to_minute_precision>)
along with adding other (non-aggregated) columns to GROUP BY list :
SELECT location, TO_CHAR(sold_date,'yyyy-mm-dd hh24:mi') AS sold_date_minutes,
LISTAGG(equipment_type,',') WITHIN GROUP
( ORDER BY location, TO_DATE(sold_date,'yyyy-mm-dd hh24:mi') ) AS equipment_type
FROM car_sales c
GROUP BY location, TO_CHAR(sold_date,'yyyy-mm-dd hh24:mi')
Demo
回答4:
In Oracle, you can use the TRUNC function to truncate the date to the minute. You can then group by that value to find any cars that were sold within the same minute.
SELECT location,
TO_CHAR (TRUNC (sold_date, 'MI'), 'DD-MON-YYYY HH:MI PM') AS sold_minute,
LISTAGG (equipment_type, ',') as equipment_list
FROM car_sales
GROUP BY location, TRUNC (sold_date, 'MI')
HAVING COUNT (*) > 1;
来源:https://stackoverflow.com/questions/63926791/oracle-select-dates-for-items-sold-within-1-minute-of-each-other