按最后关系选择或没有关系

时间:2017-03-16 21:54:11

标签: sql postgresql

我在任务和操作之间有关系。我想在没有操作的情况下选择手动任务,或者选择一个动作!='取消'进行上一次操作。

实施例

tasks table :
-------------
|id | state |
| 3 |auto   |
| 5 |manual |
| 6 |manual |
| 2 |manual |
| 8 |manual |

operations table :
----------------------------------
|id | action    | task_id | time |
| 1 | processed | 8       | 8:00 |
| 2 | cancelled | 8       | 9:00 |
| 3 | processed | 6       | 8:00 |
| 4 | processed | 2       | 8:00 |
| 5 | cancelled | 2       | 7:00 |

result : tasks 5,6,2

以下是我设法做的事情:

SELECT tasks.*
WHERE tasks.id NOT IN(
    SELECT operations.task_id)
OR tasks.id IN
    SELECT operations.task_id
    WHERE action!=cancelled
    .....
    .....
    ORDER BY time DESC

我不确定如何结束此查询。此外,它还将对任务中的每一行执行2次查询。

3 个答案:

答案 0 :(得分:1)

嗯。我在想distinct on获取有关操作的信息:

select distinct on (task_id)
from operations o
order by task_id, time desc;

然后left join来完成任务:

select t.*
from tasks t left join
     (select distinct on (task_id)
      from operations o
      order by task_id, time desc
     ) o
     on o.task_id = t.task_id
where o.task_id is null or o.action <> 'cancelled';

答案 1 :(得分:1)

让我们说这是你的架构:

CREATE TABLE tasks (
    id int unique,
    state text
);

CREATE TABLE operations (
    id serial,
    action text,
    task_id int REFERENCES tasks (id),
    time text
);

INSERT INTO tasks (id, state) VALUES (3, 'auto');
INSERT INTO tasks (id, state) VALUES (5, 'manual');
INSERT INTO tasks (id, state) VALUES (6, 'manual');
INSERT INTO tasks (id, state) VALUES (2, 'manual');
INSERT INTO tasks (id, state) VALUES (8, 'manual');

INSERT INTO operations (action, task_id, time) VALUES ('processed', 8, '8:00');
INSERT INTO operations (action, task_id, time) VALUES ('cancelled', 8, '9:00');
INSERT INTO operations (action, task_id, time) VALUES ('processed', 6, '8:00');
INSERT INTO operations (action, task_id, time) VALUES ('processed', 2, '8:00');
INSERT INTO operations (action, task_id, time) VALUES ('cancelled', 2, '7:00');

这将是您的查询:

SELECT *
FROM tasks
WHERE state = 'manual' AND
      (
        NOT EXISTS (SELECT 1 FROM operations WHERE task_id = tasks.id LIMIT 1)
        OR
        (SELECT action FROM operations WHERE task_id = tasks.id ORDER BY id DESC LIMIT 1) != 'cancelled'
      )
;

输出:

 id | state  
----+--------
  5 | manual
  6 | manual
(2 rows)

答案 2 :(得分:1)

如果您愿意依赖&#39;取消&#39;是任务执行的最后一次操作 - 即,如果取消将任务置于终端状态,那么就不必专注于取消 last 操作,只取决于是否取消。

在这种情况下,您可以使用相当简单的外部联接来查找结果:

select t.*
from
  tasks t
  left outer join operations o
    on t.id = o.task_id
       and o.action = 'cancelled'
where t.state = 'manual' and o.id is null;

特别注意过滤条件中的o.id is null - 假设&#39; id&#39;表是operations的主键,只有在operations的行不满足tasks的给定行的连接条件时,它才能在连接结果中为空。也就是说,仅适用于没有关联&#39;取消&#39;操作(包括那些根本没有操作的操作)。

另一方面,如果您需要包含已取消&#39;操作,但 last 操作不同,那么您可以使用窗口函数来帮助您识别每个任务的最后一个操作:

select t.*
from
  tasks t
  left outer join (
      select
        operations.*,
        max(operations.time) over (partition by task_id) as last_time 
      from operations
    ) o
    on t.id = o.task_id
      and o.action = 'cancelled'
      and o.time = o.last_time
where t.state = 'manual' and o.id is null;

此处必须o.time = o.last_time出现在连接条件中,而不是内联视图的过滤条件(它将是非法的)或外部查询的过滤条件(它将出错的地方)效果)。