Почему мое левое соединение не возвращает нули?

В 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, но он по-прежнему не возвращает значения NULL. : /

ИЗМЕНИТЬ 2

Siteid перемещен в элемент left join. Когда я это делаю, я получаю намного больше записей, чем ожидал. Некоторые элементы имеют нулевой идентификатор сайта, и я хочу включать их только тогда, когда у них есть конкретный идентификатор.

ИЗМЕНИТЬ 3

Структура таблицы:

Categories Table 
-------
CategoryID
Title

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

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

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

person jimj    schedule 19.03.2010    source источник
comment
если вы действительно хотите, чтобы ваш запрос тоже работал, опубликуйте структуру таблицы, и кто-нибудь здесь сможет ее написать, иначе придется много гадать.   -  person KM.    schedule 19.03.2010


Ответы (6)


измените _1 _... на _2 _..., и ваш запрос должен работать так, как вы ожидаете.

EDIT
Вы не можете фильтровать таблицы LEFT JOIN в предложении where, если не учитываете значения NULL, потому что левое соединение позволяет этим столбцам иметь значение или быть 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)

РЕДАКТИРОВАТЬ на основе редактирования 3 OP

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)
person KM.    schedule 19.03.2010
comment
попробуйте с WHERE ... AND (i.siteid = 132 OR i.itemid IS NULL): D - person Unreason; 22.03.2010
comment
вам не нужен рекурсивный запрос, чтобы получить последний, они обычно дороги - person Unreason; 22.03.2010
comment
@goran, в моем ответе нет рекурсивных запросов. единственный способ выполнить рекурсивный запрос в TSQL - использовать рекурсивный CTE, а в моем ответе его нет, даже обычный CTE. - person KM.; 23.03.2010
comment
+1 за комментарий, извините за шум, как только увидел С моей головы пошла рекурсия !, что конечно неправильно :) - person Unreason; 24.03.2010
comment
Вы не можете фильтровать таблицы LEFT JOIN в предложении where, если вы не учитываете нули ... В значительной степени подводит итог. Спасибо, что указали на это KM. - person d-_-b; 07.04.2014

Ваш критерий «WHERE» на i.siteid означает, что в выходных данных должна быть строка «items». вам нужно написать (i.siteid имеет значение null или i.siteid = 132) или поместить «i.siteid = 132» в предложение «ON» - что-то, что будет работать и для присоединения категорий товаров:

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
person araqnid    schedule 19.03.2010
comment
Когда я делаю это изменение, я получаю более 10 000 записей, но должно быть возвращено не более ~ 30. - person jimj; 19.03.2010
comment
@Griz, какие строки ты получаешь, чего не ожидаешь? у них неправильная подкатегория siteid? или у вас много дубликатов? - person araqnid; 19.03.2010
comment
Похоже, что он возвращает товары с любым идентификатором сайта. - person jimj; 19.03.2010
comment
Критерий Where должен фильтровать входные записи, а не выходные данные. Нет никакого противоречия в том, что не добавляется null в Where и ожидается получение нулевых значений. - person derloopkat; 21.03.2017

Может быть, это соединение тоже должно быть левым?

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

ИЗМЕНИТЬ:

Теперь вы выбираете только существующие идентификаторы сайтов в предложении where:

i.siteid = 132

Он должен разрешать нулевые значения, попробуйте что-то вроде этого:

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

или вы можете переместить i.siteid = 132 обратно в условие соединения

person Andrew Bezzub    schedule 19.03.2010
comment
Спасибо .. Это помогло. - person Amit Thawait; 27.06.2016

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

Наличие i.siteID = 132 в вашем предложении where по существу сводит на нет выполнение левого соединения для элементов.

person JupiterP5    schedule 19.03.2010

2-я попытка, я думаю, теперь у меня твоя проблема. Если я правильно понимаю, то в вашем случае вы получаете два типа NULL в SiteID (случай, когда вы видите результаты 10,000e, но это все еще на правильном пути).

Есть NULL, которые поступают из левого соединения, и те, которые происходят из фактического идентификатора сайта таблицы элементов. Вы хотите, чтобы те, которые пришли из левого соединения, но не хотели, чтобы те, которые были получены из данных.

Это очень распространенная ошибка с внешними соединениями при проверке существования совпадающих строк.

Имейте в виду, что если вам нужны несовпадающие строки, которые всегда должны проверять NULL только для столбцов, которые определены как NOT NULL (первичный ключ из внешней таблицы является естественным кандидатом здесь). В противном случае вы не сможете различить строки, которые имеют NULL из-за соединения LEFT, и строки, которые будут NULL, даже если это было INNER соединение

По крайней мере, два способа сделать это: а) левое соединение по подзапросу, которое отфильтрует строки с идентификатором сайта, имеет значение null до того, как левое соединение сработает в
б) критерии перезаписи (при условии, что в элементах требуется 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

(Я предполагаю, что запрос до таблицы соединения с элементами дал вам то, что вы ожидали).

Если itemid требуется в элементах, то в приведенном выше условии говорится - строки с siteid 132 или строки, которые действительно происходят из несогласованного левого соединения (обратите внимание, что условие для i.itemid имеет значение null, а не i.siteid равно null).

person Unreason    schedule 22.03.2010

Попробуйте также изменить соединение Items на левое соединение.

person Gratzy    schedule 19.03.2010