1

给定这个模式(在 postgresql-9.2 中):

CREATE TABLE foo (
   id serial PRIMARY KEY,
   ...other columns elided...
);

CREATE TYPE event_type AS ENUM ('start', 'stop');

CREATE TABLE foo_event (
   id serial PRIMARY KEY,
   foo_id integer NOT NULL REFERENCES foo (id),
   timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   type event_type NOT NULL
);

我怎样才能得到所有foo的“跑步”?也就是说,foo最近事件是“开始”的foos,或者更好的是,至少开始了一次并且stop在最后一个事件之后没有 s 的 s start(以防我将来添加更多事件类型)。


到目前为止,我最好的尝试是:

SELECT * FROM foo
 WHERE id NOT IN
 (SELECT foo_id FROM foo_event
  WHERE type='stop');

当然,这里的问题是它永远不会返回任何foo曾经停止过的 s。

4

1 回答 1

3

使用MAX()聚合来检索每个组的最新start事件foo_id,您可以针对子查询执行检索大于子句中事件的LEFT JOIN最新(也是MAX())事件的子查询。在 events 子查询中查找带有s 的,表示不匹配。stopstartONNULLstop

SELECT 
  foo.*,
  fstart.*
FROM 
  foo 
  INNER JOIN (
   /* Left side gets most recent start events */
   SELECT
    foo_id, 
    MAX(timestamp) AS start_ts
   FROM
    foo_event
   WHERE event_type = 'start'
   GROUP BY foo_id
 ) fstart ON foo.id = fstart.foo_id
 /* Right side gets most recent stop events */
 LEFT JOIN (
  SELECT
    foo_id,
    MAX(timestamp) AS stop_ts
  FROM
    foo_event
  WHERE event_type = 'stop'
  GROUP BY foo_id
 /* JOIN rows that have a greater stop time than start time */
 ) fstop ON fstart.foo_id = fstop.foo_id AND fstop.stop_ts > fstart.start_ts
 /* And find rows where there's no matching stop event greater than the start */
WHERE fstop.stop_ts IS NULL

看看那个,它确实有效!http://sqlfiddle.com/#!12/8642d/1

注意,如果停止事件可以与其相应的开始事件同时结束,请在子句中使用>=而不是...>ON

于 2013-02-20T03:21:55.143 回答