2010-03-19 2 views
12

В 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 

Я пытаюсь получить элементы в своих категориях, но я все равно хочу, чтобы вернуть запись, если нет элементов в категории или подкатегории. Подкатегории, у которых нет элементов, никогда не возвращаются. Что я делаю не так?

Спасибо

EDIT

Модифицированный запрос со второй слева присоединиться и где положение, но это все еще не возвращался аннулирует. :/

EDIT 2

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

EDIT 3

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

Categories Table 
------- 
CategoryID 
Title 

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

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

Items Table 
-------- 
ItemID 
Title 
SiteID 
+0

Если вы действительно хотите, чтобы ваш запрос тоже работал, разместите структуру таблицы, и кто-то здесь может ее написать, иначе будет много угадываний. –

+0

@ Гриз, ты взглянул на мой ответ? – Unreason

ответ

26

изменение join items i ... к LEFT join items i ... и ваш запрос должен работать, как вы ожидаете.

EDIT
Вы не можете фильтровать LEFT JOIN таблицы в ИНЕКЕ, если не учитывать нулям, потому что левое соединение позволяет эти столбцы, чтобы иметь значение или быть пустым, если ни одна строка не соответствует:

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)

EDIT на основе редактирования параметров порядка 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

попробуйте с WHERE ... AND (i.siteid = 132 ИЛИ i.itemid IS NULL): D – Unreason

+0

вам не нужен рекурсивный запрос, чтобы получить последний, они обычно дорогие – Unreason

+2

@goran, нет рекурсивных запросов где-нибудь в моем ответе. единственный способ сделать рекурсивный запрос в TSQL - это рекурсивный CTE, и в моем ответе нет ни одного обычного CTE. –

-3

Попробуйте изменить соединение элементов с левым соединением.

1

Возможно, это соединение также должно быть левым соединением?

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

EDIT:

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

i.siteid = 132 

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

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

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

+0

Спасибо .. Это помогло. –

5

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

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 
+0

Когда я делаю эту модификацию, я получаю более 10 000 записей, но должно быть не более ~ 30 возвращенных. – jimj

+0

@Griz, в какие строки вы возвращаетесь, чего не ожидаете? есть ли у них неправильная подкатегория, siteid? или вы получаете много дубликатов? – araqnid

+0

Кажется, что они возвращают элементы с любым сайтом. – jimj

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

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

0

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

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

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

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

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

Смежные вопросы