2012-01-06 52 views
16

Estoy usando SQL Server y estoy teniendo dificultades para obtener los resultados de una consulta SELECT que deseo. Intenté unirme a diferentes pedidos y usar subconsultas, pero nada funciona del modo que quiero. Tome este ejemplo artificial de aplicaciones de software, con diferentes niveles de versión, que podrían instalarse en computadoras de personas.Servidor SQL: varias tablas se unen con una cláusula WHERE

Necesito realizar un JOIN con un WHERE, pero por alguna razón no puedo obtener los resultados que quiero.

Quizás estoy viendo mis datos incorrectamente, no estoy muy seguro de por qué no puedo hacer que esto funcione.

Aplicación tabla

ID Name 
1 Word 
2 Excel 
3 Powerpoint 

Software Tabla (contiene información de la versión para diferentes aplicaciones)

ID ApplicationID Version 
1 1    2003 
2 1    2007 
3 2    2003 
4 2    2007 
5 3    2003 
6 3    2007 

Software_Computer tabla unión

ID SoftwareID ComputerID 
1 1   1 
2 4   1 
3 2   2 
4 5   2 

ordenador mesa

ID ComputerName 
1 Name1 
2 Name2 

Quiero una consulta que podría correr en el que seleccionar un equipo específico para mostrar la versión de software y aplicaciones IS tiene, pero también quiero que se muestre lo que la aplicación no lo hace tiene (la versión sería una NULL ya que no tiene que el software en él)

SELECT Computer.ComputerName, Application.Name, Software.Version 
FROM Computer 
JOIN Software_Computer 
    ON Computer.ID = Software_Computer.ComputerID 
JOIN Software 
    ON Software_Computer.SoftwareID = Software.ID 
RIGHT JOIN Application 
    ON Application.ID = Software.ApplicationID 
WHERE Computer.ID = 1 

Quiero el siguiente conjunto de resultados

ComputerName Name   Version 
Name1   Word   2003 
Name1   Excel   2007 
Name1   Powerpoint NULL 

Pero apenas consigo

Results 
ComputerName Name   Version 
Name1   Word   2003 
Name1   Excel   2007 

me pareció que el RIGHT JOIN incluiría todos los resultados en la tabla de aplicación, incluso si no están asociados con el ordenador. ¿Qué estoy perdiendo/haciendo mal?

+0

Tal vez sea porque soy alto, pero ¿no estás limitando las aplicaciones del software de las computadoras? ¿Y no hay solo dos programas pertenecientes a la computadora 1? Además, ¿no debería una combinación izquierda anular una combinación correcta? Eso necesito verificar Y PowerPoint tiene una versión, ¿por qué esperaría nulo? Además, con múltiples versiones para aplicaciones, en la tabla de software de computadoras, también debe mantener una identificación de versión, de lo contrario, tendría todo tipo de resultados desordenados. – frostymarvelous

Respuesta

12

Al utilizar LEFT JOIN o RIGHT JOIN, se hace una diferencia si se pone el filtro en el WHERE o en el JOIN.

Ver esta respuesta a una pregunta similar que escribí hace algún tiempo:
What is the difference in these two queries as getting two different result set?

En resumen:

  • si lo pones en la cláusula WHERE (como lo hizo, los resultados que aren 't asociado con esa computadora se filtra completamente
  • si lo pone en el JOIN en su lugar, los resultados que no están asociados con esa computadora aparecen en el resultado de la consulta, solo con NULL valora
    -> esto es lo que quiere
+0

Esto parece ser lo que me faltaba. Acabo de probarlo y parece estar produciendo los resultados que quiero. Gracias. – Stormchao

+0

El resultado esperado es tener "nombre1" en la lista con el "powerpoint". Agregar el "computer.ID = 1" a la unión no da el resultado esperado. La fila con el valor "powerpoint" tendrá su columna de nombre de computadora como valor nulo. Entre las soluciones de w0lf y Oleg, oleg es la mejor de las dos. –

1

Tiene que hacer un LEFT JOIN.

SELECT Computer.ComputerName, Application.Name, Software.Version 
FROM Computer 
JOIN dbo.Software_Computer 
    ON Computer.ID = Software_Computer.ComputerID 
LEFT JOIN dbo.Software 
    ON Software_Computer.SoftwareID = Software.ID 
RIGHT JOIN dbo.Application 
    ON Application.ID = Software.ApplicationID 
WHERE Computer.ID = 1 

Aquí está la explicación:

El resultado de una combinación externa izquierda (o simplemente combinación izquierda) de la tabla A y B siempre contiene todos los registros de la tabla "izquierda" (A), incluso si la condición de unión no encuentra ningún registro coincidente en la tabla "derecha" (B). Esto significa que si la cláusula ON coincide con 0 (cero) registros en B, , la unión aún devolverá una fila en el resultado, pero con NULL en cada columna de B. Esto significa que una combinación externa izquierda devuelve todos los valores de la tabla de la izquierda, más los valores coincidentes de la tabla correcta (o NULL en caso de que no haya un predicado de combinación coincidente).Si la tabla correcta devuelve una fila y la tabla izquierda devuelve más de una fila coincidente para ella, los valores en la tabla derecha se repetirán para cada fila distinta en la tabla izquierda. A partir de Oracle 9i en adelante, se puede usar la instrucción IZQUIERDA OUTER JOIN así como (+).

+0

humm, ¿por qué downvote oO –

+0

Esto no funciona. La UNIÓN EXTREMA IZQUIERDA no cambia nada; el conjunto de resultados intermedios todavía tiene "nombre1" asociado con "palabra" y "excel".La combinación externa derecha todavía incluirá "powerpoint" en el próximo conjunto de resultados, pero se asocia con el valor NULL para el nombre de la computadora. Entonces, finalmente, la condición where elimina el registro de powerpoint del conjunto de resultados porque es nulo para el nombre de la computadora <> "name1" –

7

La tercera fila que espera (el que tiene Powerpoint) es filtrada por la condición Computer.ID = 1 (intente ejecutar la consulta con el Computer.ID = 1 or Computer.ID is null para ver lo que sucede) .

Sin embargo, cayendo esa condición no tendría sentido, porque después de todo, queremos la lista para una computadora determinada.

La única solución que veo es realizar una UNIÓN entre su consulta original y una nueva consulta que recupera la lista de aplicaciones que son no que se encuentran en esa computadora.

La consulta podría tener este aspecto:

DECLARE @ComputerId int 
SET @ComputerId = 1 

-- your original query 
SELECT Computer.ComputerName, Application.Name, Software.Version 
    FROM Computer 
    JOIN dbo.Software_Computer 
     ON Computer.ID = Software_Computer.ComputerID 
    JOIN dbo.Software 
     ON Software_Computer.SoftwareID = Software.ID 
    RIGHT JOIN dbo.Application 
     ON Application.ID = Software.ApplicationID 
    WHERE Computer.ID = @ComputerId 

UNION 

-- query that retrieves the applications not installed on the given computer 
SELECT Computer.ComputerName, Application.Name, NULL as Version 
FROM Computer, Application 
WHERE Application.ID not in 
    (
     SELECT s.ApplicationId 
     FROM Software_Computer sc 
     LEFT JOIN Software s on s.ID = sc.SoftwareId 
     WHERE sc.ComputerId = @ComputerId 
    ) 
AND Computer.id = @ComputerId 
+0

Gracias. ¡Esto es perfecto! Estaba tratando de lograr demasiado con un solo SELECT. Yo votaría esto si pudiera. – Stormchao

+0

Sabía que eso es lo que necesitabas; fue muy claro a partir de la pregunta. Aunque no estoy seguro de por qué obtuve un voto negativo sobre esta respuesta. De todos modos, estoy muy contento de que haya ayudado. :-) – GolfWolf

+0

No es tan eficiente como el de Oleg. Y también, algunos problemas menores que tengo con él son 1) UNIÓN TODO porque los conjuntos ya son mutuamente excluyentes por definición 2) sean más explícitos con CROSS JOIN –

2

probar esto

DECLARE @Application TABLE(Id INT PRIMARY KEY, NAME VARCHAR(20)) 
INSERT @Application (Id, NAME) 
VALUES (1,'Word'), (2,'Excel'), (3,'PowerPoint') 
DECLARE @software TABLE(Id INT PRIMARY KEY, ApplicationId INT, Version INT) 
INSERT @software (Id, ApplicationId, Version) 
VALUES (1,1, 2003), (2,1,2007), (3,2, 2003), (4,2,2007),(5,3, 2003), (6,3,2007) 

DECLARE @Computer TABLE(Id INT PRIMARY KEY, NAME VARCHAR(20)) 
INSERT @Computer (Id, NAME) 
VALUES (1,'Name1'), (2,'Name2') 

DECLARE @Software_Computer TABLE(Id INT PRIMARY KEY, SoftwareId int, ComputerId int) 
INSERT @Software_Computer (Id, SoftwareId, ComputerId) 
VALUES (1,1, 1), (2,4,1), (3,2, 2), (4,5,2) 

SELECT Computer.Name ComputerName, Application.Name ApplicationName, MAX(Software2.Version) Version 
FROM @Application Application 
JOIN @Software Software 
    ON Application.ID = Software.ApplicationID 
CROSS JOIN @Computer Computer 
LEFT JOIN @Software_Computer Software_Computer 
    ON Software_Computer.ComputerId = Computer.Id AND Software_Computer.SoftwareId = Software.Id 
LEFT JOIN @Software Software2 
    ON Software2.ID = Software_Computer.SoftwareID 
WHERE Computer.ID = 1 
GROUP BY Computer.Name, Application.Name 
1
SELECT p.Name, v.Name 
FROM Production.Product p 
JOIN Purchasing.ProductVendor pv 
ON p.ProductID = pv.ProductID 
JOIN Purchasing.Vendor v 
ON pv.BusinessEntityID = v.BusinessEntityID 
WHERE ProductSubcategoryID = 15 
ORDER BY v.Name; 
+0

Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar aclaraciones de un autor, deje un comentario debajo de su publicación; siempre puede comentar sus propias publicaciones, y una vez que tenga suficiente [reputación] (http://stackoverflow.com/help/whats-reputation) lo hará poder [comentar cualquier publicación] (http://stackoverflow.com/help/privileges/comment). – Micha

0

probar este funcionando bien ....

SELECT computer.NAME, application.NAME,software.Version FROM computer LEFT JOIN software_computer ON(computer.ID = software_computer.ComputerID) 
LEFT JOIN software ON(software_computer.SoftwareID = Software.ID) LEFT JOIN application ON(application.ID = software.ApplicationID) 
where computer.id = 1 group by application.NAME UNION SELECT computer.NAME, application.NAME, 
NULL as Version FROM computer, application WHERE application.ID not in (SELECT s.applicationId FROM software_computer sc LEFT JOIN software s 
on s.ID = sc.SoftwareId WHERE sc.ComputerId = 1) 
AND computer.id = 1 
0

seleccione C.ComputerName, S.Version, A.Name.210 de ordenador C combinación interna Software_Computer SC en C.Id = SC.ComputerId combinación interna Software S en SC.SoftwareID = S.Id interior unirse Aplicación A en S.ApplicationId = A.Id;