Postgresql:创建日期序列,在日期范围查询中使用它

时间:2017-05-02 13:14:08

标签: postgresql

我对SQL并不满意,但到目前为止,我在项目上取得了很好的进展。现在我完全卡住了。

我试图计算每种状态的公寓数量。我希望每天都能获得这些信息,这样我就可以随着时间推移。我的数据看起来像这样:

table: y_unit_status

unit | date_occurred | start_date | end_date   | status
1    | 2017-01-01    | 2017-01-01 | 2017-01-05 | Occupied No Notice
1    | 2017-01-06    | 2017-01-06 | 2017-01-31 | Occupied Notice
1    | 2017-02-01    | 2017-02-01 |            | Vacant
2    | 2017-01-01    | 2017-01-01 |            | Occupied No Notice

我希望获得如下输出:

date       | occupied_no_notice | occupied_notice | vacant
2017-01-01 | 2                  | 0               | 0
...
2017-01-10 | 1                  | 1               | 0
...
2017-02-01 | 1                  | 0               | 1

或者,这种方法可行:

date       | status             | count
2017-01-01 | occupied no notice | 2
2017-01-01 | occupied notice    | 0

date_occurred:单位状态发生变化的日期 start_date:与date_occurred相同 end_date:状态停止为x并更改为y的日期。

我正在拉入卧室的数量和属性ID,因此第二种方法一次选择一种状态的计数会产生相对大量的行而不是选项1(如果这很重要)。

我发现很多参考资料让我接近我正在寻找的东西,但我总是得到一种滚动的累积计数。

这是我的查询,它会生成一列日期和计数,这些日期和计数会随着时间累积而不是反映特定日期的计数快照。您可以看到我对另一个表格的引用,其中我提到了属性ID。表模式是Property - >单位 - >单位状态。

WITH t AS(
    SELECT i::date from generate_series('2016-06-29', '2017-08-03', '1 day'::interval) i
    )

SELECT t.i as date,
u.hproperty,
count(us.hmy) as count --us.hmy is the id
FROM t
LEFT OUTER JOIN y_unit_status us ON t.i BETWEEN us.dtstart AND 
us.dtend
INNER JOIN y_unit u ON u.hmy = us.hunit -- to get property id
WHERE us.sstatus = 'Occupied No Notice'
AND t.i >= us.dtstart
AND t.i <= us.dtend
AND u.hproperty = '1'
GROUP BY t.i, u.hproperty
ORDER BY t.i
limit 1500

我还尝试了一个FOR循环,迭代日期以确定日期在开始和结束之间的情况但我的逻辑不起作用。感谢您的任何见解!

1 个答案:

答案 0 :(得分:3)

您走在正确的轨道上,但您需要处理NULL中的end_date值。如果这些意味着status假设在未来的某个地方被改变(但不确定它何时会改变),containment operators (@> and <@)daterange type对你来说是完美的(因为范围可以是&#34;无界&#34):

with params as (
  select date '2017-01-01' date_from,
         date '2017-02-02' date_to
)
select     date_from + d, status, count(unit)
from       params
cross join generate_series(0, date_to - date_from) d
left join  y_unit_status on daterange(start_date, end_date, '[]') @> date_from + d
group by   1, 2

要实现第一个变体,您可以使用条件聚合:

with params as (
  select date '2017-01-01' date_from,
         date '2017-02-02' date_to
)
select     date_from + d,
           count(unit) filter (where status = 'Occupied No Notice') occupied_no_notice,
           count(unit) filter (where status = 'Occupied Notice') occupied_notice,
           count(unit) filter (where status = 'Vacant') vacant
from       params
cross join generate_series(0, date_to - date_from) d
left join  y_unit_status on daterange(start_date, end_date, '[]') @> date_from + d
group by   1

备注

  • syntax filter (where <predicate>)是9.4+的新手。在此之前,您可以使用CASE(以及大多数聚合函数不包含NULL值的事实)来模拟它。
  • 您甚至可以将表达式daterange(start_date, end_date, '[]')(使用gist)编入索引以获得更好的效果。

http://rextester.com/HWKDE34743