Dans SQL Server 2008, j'ai la requête suivante:
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
J'essaie d'obtenir des éléments dans leurs sous-catégories, mais je souhaite toujours renvoyer un enregistrement s'il n'y a aucun élément dans la catégorie ou la sous-catégorie. Les sous-catégories qui n'ont pas d'articles ne sont jamais retournées. Qu'est-ce que je fais mal?
Je vous remercie
MODIFIER
Requête modifiée avec une deuxième jointure gauche et une clause where, mais elle ne renvoie toujours pas de null. : /
MODIFIER 2
Siteid déplacé vers la jointure gauche de l'élément. Quand je fais cela, j'obtiens bien plus d'enregistrements que prévu. Certains éléments ont un siteid nul et je ne veux les inclure que lorsqu'ils ont un identifiant spécifique.
MODIFIER
Structure du tableau:
Categories Table
-------
CategoryID
Title
SubCategories Table
-------
SubCategoryID
CategoryID
Title
ItemCategories Table
-------
ItemCategoryID
ItemID
SubCategoryID
IsActive
Items Table
--------
ItemID
Title
SiteID
changez join items i
... en LEFT join items i
... et votre requête devrait fonctionner comme prévu.
MODIFIER
Vous ne pouvez pas filtrer les tables LEFT JOIN dans la clause where, sauf si vous tenez compte des valeurs NULL, car la jointure gauche permet à ces colonnes d'avoir une valeur ou d'être Null lorsqu'aucune ligne ne correspond:
and i.siteid = 132
Supprimera toutes vos lignes qui ont un NULL i.siteid
, Là où il n'en existait pas. Déplacez-le sur ON:
left join items i on ic.itemid = i.itemid and i.siteid = 132
ou faites les WHERE gérer NULLs:
WHERE ... AND (i.siteid = 132 OR i.siteid IS NULL)
MODIFIER basé sur le montage 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
Je ne suis pas sûr à 100% de ce que l'OP est après, cela retournera toutes les informations qui peuvent être jointes lorsque le siteid=132
Comme indiqué dans la question
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
PRODUCTION:
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)
Cela répertoriera toutes les catégories, même s'il n'y a pas de correspondance avec le 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
PRODUCTION:
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)
Vos critères "WHERE" sur i.siteid signifient qu'il doit y avoir une ligne "items" dans la sortie. vous devez écrire (i.siteid est nul ou i.siteid = 132) ou mettre le "i.siteid = 132" dans la clause "ON" - quelque chose qui fonctionnera également pour la jointure des catégories d'élément:
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
Peut-être que cette jointure devrait également être une jointure gauche?
join items i on ic.itemid = i.itemid and i.siteid = 132
MODIFIER :
Maintenant, vous sélectionnez uniquement les identifiants de site existants dans la clause where:
i.siteid = 132
Il doit autoriser les valeurs nulles, essayez quelque chose comme ceci:
(i.siteid = 132 or i.siteid is null)
ou vous pouvez déplacer i.siteid = 132
retour à la condition de jointure
where (ic.isactive = 1 or ic.isactive is null) and i.siteid = 132
Avoir l'i.siteID = 132 dans votre clause where annule essentiellement la réalisation d'une jointure gauche sur les éléments.
2ème tentative, je pense avoir votre problème maintenant. Si je comprends bien, ce qui se passe, c'est que vous vous retrouvez avec deux types de NULL dans SiteID dans votre cas (le cas lorsque vous voyez 10 000e résultats, mais c'est toujours sur la bonne voie).
Il y a des NULL qui proviennent de la jointure gauche et ceux qui proviennent de la table Itemid siteid réelle. Vous voulez que ceux qui viennent de la jointure gauche, mais vous ne voulez pas ceux des données.
Il s'agit d'une erreur très courante avec les jointures externes lors du test de l'existence de lignes correspondantes.
Gardez à l'esprit que si vous voulez des lignes sans correspondance qui doivent toujours tester NULL uniquement sur les colonnes définies comme NOT NULL (la clé primaire de la table externe est un candidat naturel ici). Sinon, vous ne pouvez pas faire la distinction entre les lignes NULL en raison de la jointure LEFT et les lignes qui seraient NULL même s'il s'agissait d'une jointure INNER
Au moins deux façons de procéder: a) la jointure gauche sur la sous-requête qui filtrera les lignes avec siteid est nulle avant que la jointure gauche ne se déclenche
b) réécrire les critères (en supposant que ItemID est requis dans Items) pour dire
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
(Je suppose que la requête jusqu'à la table join to items vous a donné ce que vous attendiez).
Si itemid est requis dans les articles, la condition ci-dessus dit - lignes avec siteid 132 ou lignes qui proviennent vraiment d'une jointure gauche inégalée (notez que la condition est sur i.itemid est null et non i.siteid est null).