2010-03-19 17 views
12

En SQL Server 2008, que tienen la siguiente consulta:¿Por qué mi combinación izquierda no devuelve nulos?

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 

estoy tratando de obtener los elementos en sus subcategorías, pero todavía quiero devolver un registro si no hay artículos en la categoría o subcategoría. Las subcategorías que no tienen elementos nunca se devuelven. ¿Qué estoy haciendo mal?

Gracias

EDITAR

consulta modificada con un segundo izquierda unirse y donde cláusula, pero todavía no es devolver valores nulos. :/

EDITAR 2

siteid Trasladado al elemento hacia la izquierda unirse. Cuando hago esto obtengo muchos más registros de los que esperaba. Algunos artículos tienen un siteid nulo y solo quiero incluirlos cuando tienen una identificación específica.

EDITAR 3

Estructura de tabla:

Categories Table 
------- 
CategoryID 
Title 

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

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

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

si realmente quiere que su consulta también funcione, publique la estructura de la tabla y alguien aquí puede escribirla, de lo contrario, solo hay muchas conjeturas. –

+0

@ Griz, ¿echó un vistazo a mi respuesta? – Unreason

Respuesta

26

cambio join items i ... a ... LEFT join items i y la consulta deben funcionar como se esperaba.

EDITAR
No puede filtrar LEFT JOIN tablas en la cláusula where a menos que cuenta para los nulos, porque la izquierda se unen permite esas columnas que tienen un valor o ser nulo cuando no hay filas partidos:

and i.siteid = 132 arrojará cualquiera de sus filas que tengan NULL i.siteid, donde no existía ninguna.Mover esto a la ON:

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

o hacer que el DONDE manejar los nulos:

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

EDITAR basado en edición de 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 

No estoy 100% seguro de lo que busca el OP, esto devolverá toda la información que se puede unir cuando el siteid=132 como se indica en la pregunta

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 

SALIDA:

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) 

Esto mostrará una lista de todas las categorías, incluso si no hay ninguna coincidencia con la salida 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

intente con WHERE ... AND (i.siteid = 132 O i.itemid IS NULL): D – Unreason

+0

no necesita consulta recursiva para obtener la última, por lo general son caros – Unreason

+2

@goran, no hay consultas recursivas en cualquier parte de mi respuesta. la única forma de hacer una consulta recursiva en TSQL es con un CTE recursivo y no hay ninguno en mi respuesta, ni siquiera un CTE normal. –

-3

Pruebe a cambiar la combinación de elementos a una combinación de la izquierda también.

1

¿Quizás esta unión también debería ser una combinación de la izquierda?

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

EDITAR:

Ahora usted está seleccionando los identificadores de sitio sólo existentes en la cláusula where:

i.siteid = 132 

Debe permitir valores nulos, intentar algo como esto:

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

o puede mover i.siteid = 132 volver a la condición de unión

+0

Gracias ... Esto ayudó. –

5

Sus criterios "DONDE" en i.siteid significa que tiene que haber una fila de "artículos" en la salida. que necesita para escribir (i.siteid es nulo o i.siteid = 132) o poner el "i.siteid = 132" en la posición "ON" algo Cláusula que funcione para los itemcategories unirse también a ella:

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

Cuando realizo esta modificación, obtengo más de 10,000 registros, pero no debería haber más de ~ 30 devueltos. – jimj

+0

@Griz ¿a qué filas está devolviendo que no espera? ¿tienen la subcategoría incorrecta, siteid? o recibes muchos duplicados? – araqnid

+0

Parece devolver elementos con cualquier siteid. – jimj

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

Tener el i.siteID = 132 en su cláusula where esencialmente anula la unión izquierda en los elementos.

0

2º intento, creo que tengo su problema ahora. Si entiendo correctamente, entonces lo que sucede es que terminas con dos tipos de NULL en SiteID en tu caso (el caso en que ves resultados de 10,000e, pero eso todavía está en el camino correcto).

Hay NULL que proceden de la combinación left y los que provienen de la tabla de elementos real siteid. Desea que los que vienen de la izquierda se unan, pero no los quiere de los datos.

Este es un error muy común con las uniones externas al probar la existencia de filas coincidentes.

Tenga en cuenta que si desea filas sin coincidencia que siempre deben probar NULL solo en las columnas que se definen como NOT NULL (la clave primaria de la tabla externa es un candidato natural aquí). De lo contrario, no puede distinguir entre las filas que son NULL debido a la combinación IZQUIERDA y las filas que serían NULL incluso si fuera una unión INTERNA

Al menos dos formas de hacerlo: a) left join en la subconsulta que lo hará filtrar las filas con siteid es nula antes de la izquierda se unen a patadas en
b) reescribir criterios (suponiendo ItemID se solicitan en los puntos) decir

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 

(estoy suponiendo que la consulta hasta la unión con la tabla artículos te estaba dando lo que esperabas).

Si se requiere itemid en los elementos, la condición anterior dice: filas con siteid 132 o filas que realmente provienen de una combinación sin igual (tenga en cuenta que la condición es i.itemid es nula y no i.siteid es nulo) .

Cuestiones relacionadas