oracle为彼此之间在一分钟内出售的商品选择日期

问题描述

Oracle 12c数据库。

我有汽车销售表:

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,sold_date) values ('1','07-SEP-19 10:00:45');

insert into car_sales (num_cars,sold_date) values ('9','Jaguars','07-SEP-19 06:00:00');

insert into car_sales (num_cars,sold_date) values ('7','leamington','30-AUG-19 13:10:13');

insert into car_sales (num_cars,sold_date) values ('10','Trans Am','30-AUG-19 09:00:00');

insert into car_sales (num_cars,sold_date) values ('2','30-AUG-19 13:10:48');

insert into car_sales (num_cars,'06-SEP-19 18:00:00');

insert into car_sales (num_cars,sold_date) values ('4','06-SEP-19 09:00:00');

insert into car_sales (num_cars,sold_date) values ('100','06-SEP-19 08:59:45');

insert into car_sales (num_cars,'corvette','06-SEP-19 09:00:10');

insert into car_sales (num_cars,'Toyota','06-SEP-19 10:00:00');

insert into car_sales (num_cars,sold_date) values ('15','07-SEP-19 11:05:00');

insert into car_sales (num_cars,'07-SEP-19 17:02:07');

insert into car_sales (num_cars,sold_date) values ('3','30-AUG-19 13:10:25');

commit;

我只需要选择一个位置在1分钟内发生的销售(销售日期)。

我创建了以下sql示例,但它不只显示在1分钟内某个位置共享销售日期的记录,而是显示该位置的所有记录。另外,是否可以根据位置类型创建一个结果集的列表,以匹配1分钟内的日期?我不知道如何得到结果,然后将结果显示为:

对于1分钟内的记录:

coventry  07-SEP-19 10:00:45 Rovers
coventry  07-SEP-19 10:00:12 Rovers 

Listagg是:

LOCATION listagg(EQUIPMENT_TYPE)

coventry Rovers,Rovers  

-在此示例中,equipment_type恰好是流动站,流动站,即匹配1分钟销售量的任何equipment_type。

SQL>
select location,sold_date,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,num_cars
                  order by sold_date desc;

我如何创建正确的结果,并按位置在60秒内销售的equipment_types结果列表清单。

先谢谢您。 吉莉

解决方法

您可以使用LAG / LEAD分析函数比较上一行和下一行,以确定它们是否在当前行的一分钟之内:

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;

其中,为您的示例数据:

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,DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   9,'Jaguars',DATE '2019-09-07' + INTERVAL '06:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   7,'leamington',DATE '2019-08-30' + INTERVAL '13:10:13' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT  10,'Trans Am',DATE '2019-08-30' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   2,DATE '2019-08-30' + INTERVAL '13:10:48' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   8,DATE '2019-09-06' + INTERVAL '18:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   4,DATE '2019-09-06' + INTERVAL '09:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT 100,DATE '2019-09-06' + INTERVAL '08:59:45' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   1,'corvette',DATE '2019-09-06' + INTERVAL '09:00:10' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   2,'Toyota',DATE '2019-09-06' + INTERVAL '10:00:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT  15,DATE '2019-09-07' + INTERVAL '11:05:00' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   2,DATE '2019-09-07' + INTERVAL '17:02:07' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   3,DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL;

输出:

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 提琴here


更新

一个简短得多的Oracle 12c查询使用MATCH_RECOGNIZE

SELECT location,' )
         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;

其中,用于测试数据:

CREATE TABLE CAR_SALES ( NUM_CARS,DATE '2019-09-07' + INTERVAL '10:00:45' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   3,DATE '2019-09-07' + INTERVAL '10:01:15' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   3,DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   9,DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL;

输出:

LOCATION   | EQUIPMENT_TYPES             | SOLD_TIMES                         
:--------- | :-------------------------- | :----------------------------------
coventry   | Rovers,Rovers | 10:00:12,10:00:45,10:01:15,10:01:30
leamington | Rovers,Trans Am    | 13:10:13,13:10:48         
leamington | Trans Am,corvette    | 08:59:45,09:00:10         

db 提琴here

,

例如,我没有答案,应该如何汇总几行,每行比上一行不到一分钟,

  SELECT   1,DATE '2019-09-07' + INTERVAL '10:00:12' HOUR TO SECOND FROM DUAL UNION ALL
  SELECT   2,DATE '2019-09-07' + INTERVAL '10:01:30' HOUR TO SECOND FROM DUAL

由于您已经有一个解决方案,可以将所有这些值聚合到一个组中(即10:01:30-10:00:12> 1分钟,但它们仍在同一组中),我将说明如何取得首次销售和最后一次销售之间的最大差值

在这种情况下,最好对range between current row and interval '1' minute following使用分析函数。例如,对于每笔交易,我们都可以在下一分钟轻松获得同一地点有多少笔交易:

with CAR_SALES ( NUM_CARS,SOLD_DATE ) AS (
  SELECT   1,DATE '2019-08-30' + INTERVAL '13:10:25' HOUR TO SECOND FROM DUAL
)
SELECT 
   location,num_cars,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;

我添加了一些额外的行,以使其更容易看到差异。 结果:

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.

此外,我们还可以轻松地检查前面的行,并仅过滤cnt_preceding> 1或cnt_following> 1的行,即邻居

select *
from (
   SELECT 
      location,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;

结果: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

因此,我们现在唯一需要的是按照不重叠的间隔

with CAR_SALES ( NUM_CARS,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
;

结果:

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.

如您所见,MNO返回MATCH_NUMBER(),即此位置中的组号,因此现在我们可以轻松地汇总这些组:

with CAR_SALES ( NUM_CARS,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,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,dt_strt
order by 1,2
;

具有结果的完整测试用例: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                 13:10:13,13:10:48
leamington          2 2019-09-06 08:59:45 Trans Am,corvette                 08:59:45,09:00:10

,

您可以使用LISTAGG(equipment_type,') WITHIN GROUP (ORDER BY <sold_date_to_minute_precision>)并将其他(非聚合)列添加到GROUP BY列表中:

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,'yyyy-mm-dd hh24:mi')

Demo

,

在Oracle中,可以使用TRUNC函数将日期截断为分钟。然后,您可以按该值分组以查找同一分钟内售出的所有汽车。

  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;

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...