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
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
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