postgresql 计算条件为X的时间序列的累计

c6ubokkw  于 2023-02-04  发布在  PostgreSQL
关注(0)|答案(2)|浏览(160)

我有一个包含日期条目的SQL表,其中有三列:日期、项目和状态。该表如下所示:
| 日期|项目|地位|
| - ------|- ------|- ------|
| 2023年1月1日|A类|在|
| 2023年1月1日|乙|在|
| 2023年1月1日|C级|关闭|
| 2023年1月2日|A类|在|
| 2023年1月2日|乙|关闭|
| 2023年1月2日|C级|关闭|
| 2023年1月2日|D级|在|
| 2023年1月3日|A类|在|
| 2023年1月3日|乙|关闭|
| 2023年1月3日|C级|关闭|
| 2023年1月3日|D级|关闭|
查看最近的条目时,我需要按项目、最新日期和状态以及状态未更改的条目的累计计数进行分组。例如,我要查找的输出将是:
| 最新日期|项目|当前状态|当前的天数|
| - ------|- ------|- ------|- ------|
| 2023年1月3日|A类|在|三个|
| 2023年1月3日|乙|关闭|第二章|
| 2023年1月3日|C级|关闭|三个|
| 2023年1月3日|D级|关闭|1个|
如何在PostgreSQL 13.7中获得我想要的输出?
这将返回最新日期、项目和当前状态,但不会正确计算项目处于当前状态的天数:

WITH CTE AS (
  SELECT 
    item, 
    date, 
    status, 
    LAG(status) OVER (PARTITION BY item ORDER BY date) AS prev_status, 
    ROW_NUMBER() OVER (PARTITION BY item ORDER BY date DESC) AS rn
  FROM 
    schema.table
)
SELECT 
  MAX(date) AS latest_date, 
  item, 
  status AS current_status, 
  SUM(CASE WHEN prev_status = status THEN 0 ELSE 1 END) 
    OVER (PARTITION BY item ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS number_of_days
FROM 
  CTE 
WHERE 
  rn = 1 
GROUP BY
item, status, prev_status, date
ORDER BY 
  item
flseospp

flseospp1#

使用cte构建连续状态的运行:

with recursive cte(s_date, date, item, status, s_count, result) as (
    select e.date, e.date, e.item, e.status, 1, '[]'::jsonb from entries e 
    left join entries e1 on e1.item = e.item and e.date - interval '1 day' = e1.date where e1.date is null
    union all
    select c.s_date, e.date, c.item, e.status, 
      case when e.status = c.status then c.s_count + 1 else 1 end, 
      case when e.status = c.status then c.result else c.result || jsonb_build_object('s', c.status, 'c', c.s_count) end 
    from cte c join entries e on e.item = c.item and c.date + interval '1 day' = e.date
)
select date(t1.md), t1.item, e.status, (select max(((v -> 'c')#>>'{}')::int) 
      from jsonb_array_elements(r::jsonb) v where (v -> 's')#>>'{}' = e.status) 
    from (select t.s_date, t.item, max(t.date) md, max(t.result::text) r 
 from (select c.s_date, c.date, c.item, c.result || jsonb_build_object('s', c.status, 'c', c.s_count) result from cte c) t
group by t.s_date, t.item) t1
join entries e on e.item = t1.item and date(e.date) = date(t1.md)

See fiddle.

dwthyt8l

dwthyt8l2#

根据您的评论,您希望找到连续状态where status = last status value的最大计数,这变成了一个间隙和岛问题。这可以使用两个row_numbers之间的差和last_value函数来解决,如下所示:

with last_status as
(
  select *, 
      last_value(status) over (partition by item order by date_ 
        range between unbounded preceding and unbounded following) current_status,
      max(date_) over (partition by item) latest_date,
      row_number() over (partition by item order by date_) -
      row_number() over (partition by item, status order by date_) grp
  from table_name
),
consecutive_status_counts as
(
  select latest_date, item, current_status, status, count(*) cnt
  from last_status 
  where current_status = status
  group by latest_date, item, current_status, status, grp 
)
select latest_date, 
       item,
       current_status, 
       max(cnt) number_of_days_on_current
from consecutive_status_counts
group by latest_date, item, current_status
order by item

See demo

相关问题