oracle 返回多个开始日期和结束日期之间的所有非重复天数的查询

bf1o4zei  于 2023-06-22  发布在  Oracle
关注(0)|答案(5)|浏览(186)

我将简化提出这个问题的基础。我在ORACLE数据库中有两个表:一个表称为‘tb_vehicle’(id_vehicle,model,...),另一个表为‘tb_trip’(id_vehicle,dt_start,dt_end,...)。
我无法执行返回每辆车在旅途中的天数的查询,不包括重复的天数(什么意思?在同一天,车辆可以离开1、2、3、…次,然而,我只需要知道车辆在当天离开,而不是它离开的次数[我已经对另一张图表进行了这个查询,它非常安静])。
示例:
结核病车辆
| 识别车辆|模型|
| - -----|- -----|
| 1|雪佛兰科鲁兹|
| 2|雪佛兰Sonic|
tb跳闸
| 识别车辆|dt_start| dt_end|
| - -----|- -----|- -----|
| 1| 2019 -05- 06| 2019 -05- 06|
| 1| 2019 -05- 06| 2019 -05- 06|
| 1| 2019 -05- 06| 2019 -08- 21 00:00:00|
| 2| 2019 -05- 06| 2023年6月6日|
OBS:在这个例子中,我忽略了小时和分钟
预期结果:
| 识别车辆|模型|qtd_天|
| - -----|- -----|- -----|
| 1|雪佛兰科鲁兹|4|
| 2|雪佛兰Sonic| 2|
我能够使用PL/SQL计算每辆车的单个统计数据。但是我只需要使用SQL来执行这个查询。
该系统在APEX开发。

snz8szmq

snz8szmq1#

这里有一个选择
样本数据:

SQL> with
  2  tb_vehicle (id_vehicle, model) as
  3    (select 1, 'Chevrolet Cruze' from dual union all
  4     select 2, 'Chevrolet Sonic' from dual
  5    ),
  6  tb_trip (id_vehicle, dt_start, dt_end) as
  7    (select 1, date '2023-06-05', date '2023-06-05' from dual union all
  8     select 1, date '2023-06-05', date '2023-06-05' from dual union all
  9     select 1, date '2023-06-05', date '2023-06-08' from dual union all
 10     select 2, date '2023-06-05', date '2023-06-06' from dual
 11    ),

查询从这里开始; temp CTE列出开始日期和结束日期之间的所有日期;最后一个查询计算每辆车的不同日期(沿着连接到tb_vehicle表):

12  temp as
 13    (select t.id_vehicle, t.dt_start + column_value - 1 datum
 14     from tb_trip t cross join
 15       table(cast(multiset(select level from dual
 16                           connect by level <= dt_end - dt_start + 1
 17                          ) as sys.odcinumberlist))
 18    )
 19  select a.id_vehicle, m.model, count(distinct a.datum) qtd_Days
 20  from temp a join tb_vehicle m on m.id_vehicle = a.id_vehicle
 21  group by a.id_Vehicle, m.model
 22  order by a.id_Vehicle;

ID_VEHICLE MODEL             QTD_DAYS
---------- --------------- ----------
         1 Chevrolet Cruze          4
         2 Chevrolet Sonic          2

SQL>
swvgeqrz

swvgeqrz2#

下面是Littlefoot的想法,其中有一个稍微不同的查询。我使用标准的递归CTE首先从行生成单个日期。然后我计算每辆车的不同天数。最后,我把这个结果加入车辆表。您可能会发现这个查询更容易理解。

with single_days(id_vehicle, dt, dt_end) as
(
  select id_vehicle, dt_start, dt_end from tb_trip
  union all
  select id_vehicle, dt + 1, dt_end from single_days where dt < dt_end
)
, counted_days as
(
  select id_vehicle, count(distinct dt) as days
  from single_days
  group by id_vehicle
)
select *
from tb_vehicle v
join counted_days c using (id_vehicle);

演示:https://dbfiddle.uk/paJKama3

qyuhtwio

qyuhtwio3#

生成所有日期的列表,然后查找不同的日期,这很容易编写代码,但如果您的范围很大,那么它的成本就会很高。
相反,您可以合并范围(通过UNPIVOT计算日期,然后使用MATCH_RECOGNIZE合并重叠),然后使用算术计算范围的大小并为每个车辆进行聚合:

SELECT v.id_vehicle,
       MAX(v.model) AS model,
       SUM(t.dt_end - t.dt_start + 1) AS qtd_days
FROM   tb_trip
       UNPIVOT (
         dt FOR type IN (dt_start AS 1, dt_end AS -1)
       )
       MATCH_RECOGNIZE(
         PARTITION BY id_vehicle
         ORDER BY dt, type DESC
         MEASURES
           FIRST(dt) AS dt_start,
           LAST(dt)  AS dt_end
         PATTERN ( overlaps+ range_end )
         DEFINE
           overlaps  AS SUM(type) > 0,
           range_end AS SUM(type) = 0
       ) t
       INNER JOIN tb_vehicle v
       ON (t.id_vehicle = v.id_vehicle)
GROUP BY v.id_vehicle

其中,对于样本数据:

CREATE TABLE tb_vehicle (id_vehicle, model) AS
SELECT 1, 'Chevrolet Cruze' FROM DUAL UNION ALL
SELECT 2, 'Chevrolet Sonic' FROM DUAL UNION ALL
SELECT 3, 'DeLorean' FROM DUAL;

CREATE TABLE tb_trip (id_vehicle, dt_start, dt_end) AS
SELECT 1, DATE '2023-06-05', DATE '2023-06-05' FROM DUAL UNION ALL
SELECT 1, DATE '2023-06-05', DATE '2023-06-05' FROM DUAL UNION ALL
SELECT 1, DATE '2023-06-05', DATE '2023-06-08' FROM DUAL UNION ALL
SELECT 2, DATE '2023-06-05', DATE '2023-06-06' FROM DUAL UNION ALL
SELECT 3, DATE '1885-01-01', DATE '9999-12-31' FROM DUAL;

输出:
| ID_车辆|型号|QTD_天|
| - -----|- -----|- -----|
| 1|雪佛兰科鲁兹|4|
| 2|雪佛兰Sonic| 2|
| 3|德罗宁|二九六三九四二|
fiddle

8tntrjer

8tntrjer4#

希望下面的代码可以满足你的需求:

select v.id
       ,v.model
       ,sum(case when DT_END<>DT_START then DT_END - DT_START + 1 else 0 end) qtd_days
from tb_trip t,
    tb_vehicle v
where t.id_vehicle = v.id
group by v.id ,v.model 
order by v.id
cetgtptt

cetgtptt5#

使用MATCH_RECOGNIZE的更简单版本

with tb_trip(id_vehicle, dt_start, dt_end) as (
    select 1, to_date('06-05-2023', 'mm-dd-yyyy'), to_date('06-05-2023', 'mm-dd-yyyy') from dual union all
    select 1, to_date('06-05-2023', 'mm-dd-yyyy'), to_date('06-05-2023', 'mm-dd-yyyy') from dual union all
    select 1, to_date('06-05-2023', 'mm-dd-yyyy'), to_date('06-08-2023', 'mm-dd-yyyy') from dual union all
    select 2, to_date('06-05-2023', 'mm-dd-yyyy'), to_date('06-06-2023', 'mm-dd-yyyy') from dual -- union all
),
tb_vehicle(id_vehicle, model) as (
    select 1, 'Chevrolet Cruze' from dual union all
    select 2, 'Chevrolet Sonic' from dual
)
select d.id_vehicle, v.model, sum(dt_end - dt_start +1) as days
from (
    select * from tb_trip
    match_recognize (
        partition by id_vehicle
        order by dt_start, dt_end
        measures first(dt_start) as dt_start, max(dt_end) as dt_end
        pattern( overlaps* strt )
        define
            overlaps as dt_end >= dt_start
    )
) d
join tb_vehicle v on v.id_vehicle = d.id_vehicle
group by d.id_vehicle, v.model
;

相关问题