提取按月postgresql分组的日期范围的天数

extract days of daterange grouped by month postresql

我的 OrderHistory table 中有一个 pickupDate 和 returnDate。我想按月提取所有 OrderHistory 条目的租赁天数 grouped/ordered。 cte 似乎是解决方案,但我不知道如何在我的查询中实现它,因为我看到的 cte 指的是它们自己,上面写着“FROM cte”。

我试过这样的事情:

SELECT
   SUM((EXTRACT (DAY FROM("OrderHistory"."returnDate")-("OrderHistory"."pickupDate")))) as traveltime
  , to_char("OrderHistory"."pickupDate"::date, 'YYYY-MM') as M  
  
FROM
  "OrderHistory" 

  GROUP BY 
  M
  
ORDER BY 
  M

但结果不会将预订拆分为两个月(例如 pickupDate=2022 年 3 月 27 日和 returnDate=2022 年 4 月 3 日),但会将整个 7 天分配给 3 月,因为返回日期在其中.它应该在 3 月显示 4 天,在 4 月显示 3 天。

对于这个可能非常愚蠢的问题,我深表歉意,但我是初学者。 (顺便说一句,我的代码是用 postgresql 编写的)

PostgreSQL naming conventions

Are PostgreSQL column names case-sensitive?

use legal, lower-case names exclusively so double-quoting is not needed.

最终结果 db fiddle

添加日期范围列。 alter table order_history add column date_ranges daterange; 更新 order_history

with a(m_begin,  m_end, pickup_date) as
(select date_trunc('month', pickup_date)::date,
 (date_trunc('month', pickup_date) + interval '1 month - 1 day')::date,
        pickup_date from order_history)
update order_history set date_ranges =
    daterange(a.m_begin, a.m_end,'[]') from a 
        where a.pickup_date = order_history.pickup_date;

然后最终查询:

WITH A AS(
select
    pickup_date,
    return_date,
    return_date - pickup_date as  total,
case when return_date <@ date_ranges then (return_date  - pickup_date)
else ( date_trunc('month', pickup_date) + interval '1 month - 1 day')::date - pickup_date
end partial_mth
from order_history),
b as (SELECT *, a.total - partial_mth parital_not_mth FROM a)
select *,
case when to_char(pickup_date,'YYYY-MM') = to_char(return_date,'YYYY-MM')
then
sum(partial_mth) over(partition by to_char(pickup_date,'YYYY-MM')) +
sum(parital_not_mth) over (partition by to_char(return_date,'YYYY-MM'))
else sum(partial_mth) over(partition by to_char(pickup_date,'YYYY-MM'))
end
from b;

在尝试不同的方法后,我认为我找到了我的问题的最佳答案,我想与社区分享:

WITH hier as (
  SELECT
  "OrderHistory"."pickupDate" as start_date
  , "OrderHistory"."returnDate" as end_date                 
  , to_char("OrderHistory"."pickupDate"::date, 'YYYY-MM') as M
  
  
FROM
  "OrderHistory" 
  
  GROUP BY 
  1, 2, 3
  
  
ORDER BY 
  3

), calendar as (
  select date '2022-01-01' + (n || ' days')::interval calendar_date
  from generate_series(0, 365) n
)


select

to_char(calendar_date::date, 'YYYY-MM')
, count(*) as tage_gebucht


from calendar

inner join hier on calendar.calendar_date between start_date and end_date

where calendar_date between '2022-01-01' and '2022-12-31'


group by 1

order by 1;

我认为这是我想到的最简单的解决方案。