1

假设您有两个表:

订单

order_id , ....
1, ...
2, ...

每个订单都有一个“状态历史”。这存储在历史表中:

serial, date, order_id , order_state
10,     2012-01-01, 1,  'INIT'
11,     2012-01-02, 2,  'INIT'
12,     2012-02-03, 1,  'COMPLETED'
13,     2012-02-04, 1,  'DISPATCHED'
14,     2012-02-05, 2,  'COMPLETED'
15,     2012-02-06, 2,  'DISPATCHED'

现在我想知道过去任何时候我所有订单的状态。例如,我想知道 2012-02-05 的订单状态:

order_id, order_state, state_date
1 ,      'DISPATCHED' , 2012-02-04
2,       'COMPLETED'  , 2012-02-05

(state_date 列是可选的)。

有没有一种方法可以编写一个查询,该查询比遍历订单并在应用程序代码中获取其“在给定日期的状态”更有效?

我已经用 Postgresql 中的 rank() WINDOW 函数做到了这一点,但我对它必须获取所有历史记录(在给定日期之前)只是为了选择排名 1 的事实并不满意。在我看来这并不比在应用程序代码中做的更好。

SELECT *  FROM ( SELECT o.order_id,
                       h.order_state,
                       h.state_date,
                       rank() ON (PARTITION BY order_id ORDER BY h.serial DESC)
                              AS hrank
                FROM order o, history h
                WHERE
                     h.order_id = o.order_id AND
                     h.date < given_date
               ) AS rh
WHERE
   rh.hrank = 1;

我真正想要的是我的分区定义中的某种 LIMIT 1,但我不知道这是否可能。

4

3 回答 3

2

SQL小提琴

select distinct on (o.order_id) o.order_id, h.order_state, h.date
from
    orders o
    inner join
    history h on h.order_id = o.order_id
where
    h.date <= '2012-02-05'
order by o.order_id, h.date desc

distinct on将根据声明的顺序返回第一个。在这种情况下是最近的日期。

于 2013-04-10T13:04:11.120 回答
1

这可能会更快:

SELECT o.order_id, 
       h.order_state,
       h.state_date,
FROM "order" o 
  JOIN history h ON h.order_id = o.order_id
WHERE h.order_date = (select max(h2.order_date)
                      from history h2
                      where h2.order_id = h.order_id 
                      and h2.order_date <= date '2012-02-05');

(请注意,订单和历史之间的连接在这里并不是真正必要的,但我假设您的示例查询只是您真正想要的一个子集)

于 2013-04-10T13:08:53.453 回答
0

NOT EXISTS() 救援:

SELECT o.order_id
  -- h.zserial -- you might want to see this as well
  , h.order_state
  , h.state_date
FROM zorder o
JOIN history h ON h.order_id = o.order_id
    -- You probably don't need this date comparison,
    -- ,since it is already in the subquery
WHERE h.zdate < given_date
AND NOT EXISTS (
    SELECT * FROM history nx
    WHERE nx.order_id = o.order_id
    AND nx.zdate < given_date
    AND nx.zserial > h.zserial
    );

注意:我将列名更改为不与关键字冲突的名称。

于 2013-04-10T14:09:06.340 回答