对范围内的每个日期运行复杂查询

rec*_*bot 9 postgresql join

我有一张订单表

   Column   |            Type             |                      Modifiers                      
------------+-----------------------------+-----------------------------------------------------
 id         | integer                     | not null default nextval('orders_id_seq'::regclass)
 client_id  | integer                     | not null
 start_date | date                        | not null
 end_date   | date                        | 
 order_type | character varying           | not null
Run Code Online (Sandbox Code Playgroud)

数据具有针对 client_id 的非重叠常规订单,并且偶尔会出现临时订单,当它们具有匹配的 client_id 时,会覆盖其 start_date 的常规订单。存在应用程序级别的限制,以防止相同类型的订单重叠。

 id | client_id | start_date |  end_date  | order_type 
----+-----------+------------+------------+------------
 17 |        11 | 2014-02-05 |            | standing
 18 |        15 | 2014-07-16 | 2015-07-19 | standing
 19 |        16 | 2015-04-01 |            | standing
 20 |        16 | 2015-07-18 | 2015-07-18 | temporary
Run Code Online (Sandbox Code Playgroud)

例如,在2015-07-18客户端 16 上,订单 #20 是活动订单,因为它覆盖了常规订单 #19。经过一番大惊小怪,我找到了一种有效的方式来查询某个日期的活动订单 ID。

    SELECT id from (
      SELECT
        id,
        first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
      FROM orders
      WHERE start_date <= ? and (end_date is null OR end_date >= ?)
    ) active_orders
    WHERE id = active_order_id
Run Code Online (Sandbox Code Playgroud)

如果你用2015-07-18占位符查询它,你会得到

 id 
----
 17
 18
 20
Run Code Online (Sandbox Code Playgroud)

与我的其他一些想法(例如计算某个日期客户的临时订单数量的子查询)相比,此查询的查询计划非常小,我对此非常满意。(桌子的设计,我不兴奋)

现在,我需要找到一个日期范围内的所有活动订单,这些订单与它们的活动日期相连。例如,日期范围为2015-07-18to2015-07-19我想要以下结果。

active_date | id 
------------+----
 2015-07-18 | 17
 2015-07-18 | 18
 2015-07-18 | 20
 2015-07-19 | 17
 2015-07-19 | 18
 2015-07-19 | 19
Run Code Online (Sandbox Code Playgroud)

订单 20 覆盖订单 19,2015-07-18但不覆盖2015-07-19

我发现generate_series()我可以生成一系列日期,但我不知道如何将其与此结合以获取日期表和订单 ID。我的预感是交叉连接,但我不知道如何在这种情况下使其工作。

谢谢

更新 添加了一个sql 小提琴

小智 5

我会用select distinct on而不是窗口函数,然后就加入日子。

select 
    distinct on (date, client_id) date, 
    id 
from orders
inner join generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') date
  on start_date <= date and (end_date is null or date <= end_date)
order by date, client_id, order_type desc
Run Code Online (Sandbox Code Playgroud)

http://sqlfiddle.com/#!15/5a420/16/0

如果有什么不清楚的,我可以详细说明。