为什么我的左连接没有返回空值?

时间:2010-03-19 18:21:00

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

在sql server 2008中,我有以下查询:

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid 
left join items i on ic.itemid = i.itemid and i.siteid = 132
where (ic.isactive = 1 or ic.isactive is null)
order by c.title, s.title

我正在尝试在子类别中获取项目,但如果类别或子类别中没有项目,我仍然希望返回记录。永远不会返回没有项目的子类别。我做错了什么?

谢谢

修改

使用第二个左连接和where子句修改了查询,但它仍然没有返回空值。 :/

编辑2

将siteid移至项目左连接。当我这样做时,我获得了比预期更多的记录。有些项目具有空的siteid,我只想在具有特定ID时包含它们。

编辑3

表格结构:

Categories Table 
-------
CategoryID
Title

SubCategories Table
-------
SubCategoryID
CategoryID
Title

ItemCategories Table
-------
ItemCategoryID
ItemID
SubCategoryID
IsActive

Items Table 
--------
ItemID
Title
SiteID

6 个答案:

答案 0 :(得分:33)

join items i ...更改为LEFT join items i ...并且您的查询应该按预期工作。

修改
除非考虑空值,否则不能在where子句中过滤LEFT JOIN表,因为左连接允许这些列具有值,或者在没有行匹配时为null:

and i.siteid = 132将丢弃任何具有NULL i.siteid的行,其中不存在任何行。将其移至ON:

left join items i on ic.itemid = i.itemid and i.siteid = 132

或使WHERE句柄为NULL:

WHERE ... AND (i.siteid = 132 OR i.siteid IS NULL)

编辑基于OP的编辑3

SET NOCOUNT ON
DECLARE @Categories table (CategoryID int,Title varchar(30))
INSERT @Categories VALUES (1,'Cat AAA')
INSERT @Categories VALUES (2,'Cat BBB')
INSERT @Categories VALUES (3,'Cat CCC')

DECLARE @SubCategories table (SubCategoryID int,CategoryID int,Title varchar(30))
INSERT @SubCategories VALUES (1,1,'SubCat AAA A')
INSERT @SubCategories VALUES (2,1,'SubCat AAA B')
INSERT @SubCategories VALUES (3,1,'SubCat AAA C')
INSERT @SubCategories VALUES (4,2,'SubCat BBB A')

DECLARE @ItemCategories table (ItemCategoryID int, ItemID int, SubCategoryID int, IsActive char(1))
INSERT @ItemCategories VALUES (1,1,2,'Y')
INSERT @ItemCategories VALUES (2,2,2,'Y')
INSERT @ItemCategories VALUES (3,3,2,'Y')
INSERT @ItemCategories VALUES (4,4,2,'Y')
INSERT @ItemCategories VALUES (5,7,2,'Y')

DECLARE @Items table (ItemID int, Title varchar(30), SiteID int)
INSERT @Items VALUES (1,'Item A',111)
INSERT @Items VALUES (2,'Item B',111)
INSERT @Items VALUES (3,'Item C',132)
INSERT @Items VALUES (4,'Item D',111)
INSERT @Items VALUES (5,'Item E',111)
INSERT @Items VALUES (6,'Item F',132)
INSERT @Items VALUES (7,'Item G',132)
SET NOCOUNT OFF

我不是100%确定OP之后是什么,这将返回在问题中给出的siteid=132时可以加入的所有信息

SELECT
    c.title as categorytitle
        ,s.title as subcategorytitle
        ,i.title as itemtitle
        --,i.itemID, ic.SubCategoryID, s.CategoryID
    FROM @Items                          i
        LEFT OUTER JOIN @ItemCategories ic ON i.ItemID=ic.ItemID
        LEFT OUTER JOIN @SubCategories   s ON ic.SubCategoryID=s.SubCategoryID
        LEFT OUTER JOIN @Categories      c ON s.CategoryID=c.CategoryID
    WHERE i.siteid = 132

输出:

categorytitle                  subcategorytitle               itemtitle
------------------------------ ------------------------------ ------------------------------
Cat AAA                        SubCat AAA B                   Item C
NULL                           NULL                           Item F
Cat AAA                        SubCat AAA B                   Item G

(3 row(s) affected)

这将列出所有类别,即使与siteid=132

不匹配也是如此
;WITH AllItems AS
(
SELECT
    s.CategoryID, ic.SubCategoryID, ItemCategoryID, i.ItemID
        ,c.title AS categorytitle, s.title as subcategorytitle, i.title as itemtitle
    FROM @Items                          i
        LEFT OUTER JOIN @ItemCategories ic ON i.ItemID=ic.ItemID
        LEFT OUTER JOIN @SubCategories   s ON ic.SubCategoryID=s.SubCategoryID
        LEFT OUTER JOIN @Categories      c ON s.CategoryID=c.CategoryID
    WHERE i.siteid = 132
)
SELECT
    categorytitle, subcategorytitle,itemtitle
    FROM AllItems
UNION
SELECT
    c.Title, s.Title, null
    FROM @Categories                     c
        LEFT OUTER JOIN @SubCategories   s ON c.CategoryID=s.CategoryID
        LEFT OUTER JOIN @ItemCategories ic ON s.SubCategoryID=ic.SubCategoryID
        LEFT OUTER JOIN AllItems         i ON c.CategoryID=i.CategoryID AND  s.SubCategoryID=i.SubCategoryID
    WHERE i.ItemID IS NULL
ORDER BY categorytitle,subcategorytitle

输出:

categorytitle                  subcategorytitle               itemtitle
------------------------------ ------------------------------ ------------------------------
NULL                           NULL                           Item F
Cat AAA                        SubCat AAA A                   NULL
Cat AAA                        SubCat AAA B                   Item C
Cat AAA                        SubCat AAA B                   Item G
Cat AAA                        SubCat AAA C                   NULL
Cat BBB                        SubCat BBB A                   NULL
Cat CCC                        NULL                           NULL

(7 row(s) affected)

答案 1 :(得分:6)

i.siteid上的“WHERE”条件意味着输出中必须有“items”行。你需要写(i.siteid为null或i.siteid = 132)或将“i.siteid = 132”放入“ON”子句中 - 这也适用于itemcategories join:

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid and ic.isactive = 1
left join items i on ic.itemid = i.itemid and i.siteid = 132
order by c.title, s.title

答案 2 :(得分:1)

也许这个连接也应该是左连接?

join items i on ic.itemid = i.itemid and i.siteid = 132

修改

现在,您只在where子句中选择现有的站点ID:

i.siteid = 132

它应该允许空值,尝试这样的事情:

(i.siteid = 132 or i.siteid is null)

或者您可以将i.siteid = 132移回加入条件

答案 3 :(得分:0)

where (ic.isactive = 1 or ic.isactive is null) and i.siteid = 132

在where子句中使用i.siteID = 132基本上无效对项目执行左连接。

答案 4 :(得分:0)

第二次尝试,我想我现在遇到了你的问题。如果我理解正确,那么在您的情况下,您最终会遇到SiteID中的两种NULL(当您看到10,000e结果时,但仍然在正确的轨道上)。

来自左连接的NULL和来自实际Item表siteid的NULL。你想拥有来自左连接的那些,但不想要数据中的那些。

在测试匹配行的存在时,这是外连接的一个非常常见的错误。

请记住,如果您想要不匹配的行,那么应该始终仅对定义为NOT NULL的列测试NULL(来自外部表的主键是这里的自然候选者)。否则,您无法区分由于LEFT连接而为NULL的行和即使是INNER连接也将为NULL的行

至少有两种方法: a)在左连接踢出之前,将子网格上的左连接过滤掉具有siteid的行为空 b)重写标准(假设Item中需要ItemID)来说明

select      
    c.title as categorytitle,
    s.title as subcategorytitle,
    i.title as itemtitle
from categories c
join subcategories s on c.categoryid = s.categoryid
left join itemcategories ic on s.subcategoryid = ic.subcategoryid 
left join items i on ic.itemid = i.itemid
where (ic.isactive = 1 or ic.isactive is null) AND (i.siteid = 132 or i.itemid is null)
order by c.title, s.title

(我假设查询到项目表的连接是为了给你你所期望的)。

如果item中需要itemid,则上述条件为 - 具有siteid 132的行或真正来自无法匹配的左连接的行(请注意,条件在i.itemid上为空,而i.siteid为null)。

答案 5 :(得分:-3)

尝试将Items的连接更改为左连接。