是否可以修改LEFT JOIN以返回存在匹配和匹配的空行?

时间:2015-01-09 10:07:09

标签: sql-server sql-server-2008 tsql join

我有一个代表层次结构的表,因此它同时包含idparent_id。此层次结构只有两个级别,父级在parent_id中为空。我试图获取父级具有特定属性的层次结构中每个项目的记录。例如,使用此数据:

CREATE TABLE t (id int, parent_id int, property bit);
INSERT INTO t VALUES
    (1, null,    0),
    (2,    1, null),
    (3, null,    1),
    (4,    3, null),
    (5, null,    1);

我想要检索:

======
| ID |
======
| 3  |
| 4  |
| 5  |
======

我可以使用UNION这样做:

SELECT
   id
FROM
   t
WHERE
   property = 1 AND parent_id is null
UNION
SELECT
   child.id
FROM
   t parent
   INNER JOIN t child
       ON parent.id = child.parent_id
WHERE
   parent.property = 1
ORDER BY
   id;

然而,这会扫描桌子三次。我试图优化这一点,所以尝试了这个:

SELECT
   ISNULL(child.id, parent.id)
FROM
   t parent
   LEFT JOIN t child
       ON parent.id = child.parent_id
WHERE
   parent.property = 1 

然而,这只是给了我:

======
| ID |
======
| 4  |
| 5  |
======

第3行未返回,因为LEFT JOIN没有为3提供单独的行,因为它与第4行中的parent_id匹配。有没有办法修改LEFT JOIN以提供我需要的额外行?是否有其他方法可以执行此查询,而不涉及UNION方法扫描表三次?

2 个答案:

答案 0 :(得分:2)

 SELECT 
   ISNULL(child.id, parent.id)
FROM
   t child
   LEFT JOIN t parent
       ON child.parent_id = parent.id
WHERE
   parent.property = 1  OR child.property = 1
   /* you might want to do this instead to be sure to include only the parent nodes 
      with the property set to 1 :
      parent.property = 1  OR (child.property = 1 AND parent.id IS NULL)
   */

返回:

ID
3
4
5

并提供更好的表现

答案 1 :(得分:0)

这也有效,但执行计划变得讨厌

WITH n(id, parent_id) AS 
   (SELECT id, parent_id
    FROM t
    WHERE property = 1
    UNION ALL
    SELECT nplus1.id, nplus1.parent_id
    FROM t as nplus1 
    inner join n on n.id = nplus1.parent_id)
SELECT id FROM n
order by id