查询返回多个开始日期和结束日期之间所有不同日期的计数。

huangapple go评论64阅读模式
英文:

Query that returns the count of all distinct days between multiple start and end dates

问题

我将为您提供代码的翻译:

-- 查询每辆车的行程天数,排除重复的天数
SELECT
    v.id_vehicle,
    v.model,
    COUNT(DISTINCT TO_DATE(t.dt_start, 'DD-MM-YYYY')) AS qtd_days
FROM
    tb_vehicle v
JOIN
    tb_trip t
ON
    v.id_vehicle = t.id_vehicle
GROUP BY
    v.id_vehicle,
    v.model;

请注意,这是一个SQL查询,用于从ORACLE数据库中的两个表('tb_vehicle'和'tb_trip')中获取每辆车的行程天数。查询排除了重复的天数,只计算每辆车出行的不同日期。

英文:

I'm going to simplify the basis for asking the question. I have two tables in the ORACLE database: one called 'tb_vehicle' (id_vehicle, model, ...) and another table 'tb_trip' (id_vehicle, dt_start, dt_end, ...).

I am not able to carry out a query that returns the number of days that each vehicle was on the trip, excluding the repeated days (what do you mean? in the same day a vehicle can leave 1, 2, 3, ... times, however, I just need to know that the vehicle left on the day and not the number of times it left [I already made this query for another chart and it was very quiet]).

Example:

tb_vehicle

id_vehicle model
1 Chevrolet Cruze
2 Chevrolet Sonic

tb_trip

id_vehicle dt_start dt_end
1 06-05-2023 06-05-2023
1 06-05-2023 06-05-2023
1 06-05-2023 06-08-2023
2 06-05-2023 06-06-2023

OBS: I am ignoring in this example the hours and minutes

intended result:

id_vehicle model qtd_days
1 Chevrolet Cruze 4
2 Chevrolet Sonic 2

I was able to count the individual statistics for each vehicle using PL/SQL. But I need to perform this query only with SQL.

The system is developed at APEX.

答案1

得分: 1

以下是翻译好的部分:

这是一种选项。

示例数据:

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>;
英文:

Here's one option.

Sample data:

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

Query begins here; temp CTE lists all dates between start and end date; the final query calculates distinct dates per vehicle (along with join to tb_vehicle table):

 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 &lt;= 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&gt;

答案2

得分: 1

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

英文:

Here is Littlefoot's idea with a slightly different query. I am using a standard recursive CTE to generate the single days from the rows first. Then I count the distinct days per vehicle. At last I join this result to the vehicle table. You may find this query a tad easier to understand.

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 &lt; 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);

Demo: https://dbfiddle.uk/paJKama3

答案3

得分: 1

以下是您提供的内容的翻译:

生成所有日期的列表,然后查找不同的日期很容易编码,但如果范围很大,那么成本会很高。

相反,您可以合并范围(通过“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) &gt; 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_VEHICLE MODEL QTD_DAYS
1 Chevrolet Cruze 4
2 Chevrolet Sonic 2
3 DeLorean 2963942

fiddle

英文:

Generating a list of all the days and then finding the distinct days is simple to code but if you have large ranges then it will become costly.

Instead you can merge the ranges (by UNPIVOTing the dates and then using MATCH_RECOGNIZE to merge the overlaps) and then using arithmetic to calculate the size of the range and aggregating for each vehicle:

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) &gt; 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

Which, for the sample data:

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

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

Outputs:

ID_VEHICLE MODEL QTD_DAYS
1 Chevrolet Cruze 4
2 Chevrolet Sonic 2
3 DeLorean 2963942

fiddle

答案4

得分: 0

我希望下面的代码能够满足您的需求:

select v.id
       ,v.model
       ,sum(case when DT_END&lt;&gt;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

注意:此代码是SQL查询,用于从名为"tb_trip"和"tb_vehicle"的表中检索数据,并按照"v.id"进行分组和排序。

英文:

I hope the code below can meet your need :

select v.id
       ,v.model
       ,sum(case when DT_END&lt;&gt;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

答案5

得分: 0

Here's the translation of the code snippet you provided:

使用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
;

Please note that I've only translated the code part as per your request.

英文:

Simpler version using MATCH_RECOGNIZE

with tb_trip(id_vehicle, dt_start, dt_end) as (
	select 1, to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;), to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;) from dual union all
	select 1, to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;), to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;) from dual union all
	select 1, to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;), to_date(&#39;06-08-2023&#39;, &#39;mm-dd-yyyy&#39;) from dual union all
	select 2, to_date(&#39;06-05-2023&#39;, &#39;mm-dd-yyyy&#39;), to_date(&#39;06-06-2023&#39;, &#39;mm-dd-yyyy&#39;) from dual -- union all
),
tb_vehicle(id_vehicle, model) as (
	select 1, &#39;Chevrolet Cruze&#39; from dual union all
	select 2, &#39;Chevrolet Sonic&#39; 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 &gt;= dt_start
	)
) d
join tb_vehicle v on v.id_vehicle = d.id_vehicle
group by d.id_vehicle, v.model
;

huangapple
  • 本文由 发表于 2023年6月6日 03:30:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76409483.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定