左联接产生带有值的行,另一个带有NULL

时间:2018-07-11 07:31:16

标签: sql sql-server join

首先让我提供一些背景信息。假设我的任务状态为:完成或未完成。任务可以是未完成然后完成的,也可以是立即完成的。这些状态被保存。我想知道任务是否经历了这种不完整的状态。

/* Two possible states for a task, marked by a bit */
CREATE TABLE state (ID int unique not null, iscomplete bit not null);
INSERT INTO state (ID, iscomplete)
    VALUES (1, 0), (2, 1);

/* Three tasks, 2nd yet incomplete, 1 and 3 complete */
CREATE TABLE task (ID int unique not null, curr_state int not null);
INSERT INTO task (ID, curr_state)
    VALUES (1, 2), (2, 3), (3, 4);

/* All states for tasks, task 1 has had an incomplete state, 3 has not */
CREATE TABLE curr_state (ID int unique not null, task int not null, state int not null);
INSERT INTO curr_state (ID, task, state)
    VALUES (1, 1, 1), (2, 1, 2), (3, 2, 1), (4, 3, 2);

STATE:              TASK:               CURR_STATE:
ID  | IsComplete    ID  | Curr_state    ID  | Task  | State
----+-----------    ----+-----------    ----+-------+-------
 1  | 0 (False)     1   |     2         1   |   1   |   1
 2  | 1 (True)      2   |     3         2   |   1   |   2
                    3   |     4         3   |   2   |   1
                                        4   |   3   |   2

通过此查询,我几乎可以得到所需的信息,这是状态不完整或NULL中有Through的所有完整情况。

SELECT t.ID as Task, s.iscomplete as Complete, st.ID as Through

/* Get finished tasks */
FROM task t
JOIN curr_state c
ON t.curr_state = c.ID
JOIN state s
ON c.state = s.ID

/* JOIN to unfinished states */
LEFT JOIN curr_state cs
ON t.ID = cs.task
LEFT JOIN state st
ON cs.state = st.ID and st.iscomplete = 0

WHERE s.iscomplete = 1

-------- RESULT ---------

Task | Complete | Through
 1       true        1
 1       true      (null)
 3       true      (null)

-------- DESIRED --------

Task | Complete | Through
 1       true        1
 3       true      (null)

唯一的问题是,现在第一个任务有两次,首先是不完整状态,然后是NULL。如何避免这种情况?

给我this

编辑:

第一个LEFT JOIN是多余的,可以交换为简单的JOIN

1 个答案:

答案 0 :(得分:2)

您可以使用OUTER APPLY代替LEFT JOIN

SELECT t.ID as Task, s.iscomplete as Complete, through.ID as Through

/* Get finished tasks */
FROM task t
JOIN curr_state c ON t.curr_state = c.ID
JOIN state s ON c.state = s.ID

/* JOIN to unfinished states */
OUTER APPLY (
   SELECT st.ID
   FROM curr_state cs
   JOIN state st ON cs.state = st.ID 
   WHERE st.iscomplete = 0 AND cs.task = t.ID
) AS through

WHERE s.iscomplete = 1

Demo here