oracle Select dates for items sold within 1 minute of each other

谁说我不能喝 提交于 2021-02-08 11:45:25

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!