web-dev-qa-db-fra.com

SQL Server: plusieurs tables sont jointes avec une clause WHERE

J'utilise SQL Server et j'ai du mal à obtenir les résultats d'une requête SELECT que je souhaite. J'ai essayé de joindre différents ordres et d'utiliser des sous-requêtes, mais rien ne fonctionne comme je le souhaite. Prenons cet exemple artificiel d’applications logicielles, avec différents niveaux de version, pouvant être installées sur des ordinateurs. 

Je dois effectuer un JOIN avec un WHERE, mais pour une raison quelconque, je ne parviens pas à obtenir les résultats souhaités. 

Peut-être que je regarde mal mes données, je ne sais pas trop pourquoi je ne peux pas faire en sorte que cela fonctionne.

Application table 

ID  Name
1   Word
2   Excel
3   PowerPoint

Software Table (contient les informations de version pour différentes applications) 

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

Software_Computer table de jonction 

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

Ordinateur table 

ID  ComputerName
1   Name1
2   Name2

Je veux une requête que je pourrais exécuter où je sélectionne un ordinateur spécifique pour afficher quelle version de logiciel et d'application est, mais je veux aussi qu'il affiche quelle application il n'a pas (la version serait un NULL puisqu'elle n'a pas ce logiciel dessus)

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 

Je veux le jeu de résultats suivant

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

Mais je viens d'avoir 

Results
ComputerName   Name          Version
Name1          Word           2003
Name1          Excel          2007

Je pensais que le RIGHT JOIN inclurait tous les résultats dans la table d'application, même s'ils n'étaient pas associés à l'ordinateur. Qu'est-ce qui me manque/je fais mal?

16
Stormchao

Lorsque vous utilisez LEFT JOIN ou RIGHT JOIN, le fait que le filtre soit placé dans WHERE ou dans JOIN fait une différence.

Voir cette réponse à une question similaire que j'ai écrite il y a quelque temps:
Quelle est la différence entre ces deux requêtes en tant qu'obtention de deux résultats différents?

En bref:

  • si vous le mettez dans la clause WHERE (comme vous l'avez fait, les résultats qui ne sont pas associés à cet ordinateur sont complètement filtrés
  • si vous le placez plutôt dans JOIN, les résultats qui ne sont pas associés à cet ordinateur apparaissent dans le résultat de la requête, uniquement avec les valeurs NULL.
    --> c'est ce que vous voulez
12
Christian Specht

La troisième ligne que vous attendez (celle avec PowerPoint) est filtrée par la condition Computer.ID = 1 (essayez d'exécuter la requête avec le Computer.ID = 1 or Computer.ID is null pour voir ce qui se passe).

Cependant, supprimer cette condition n’aurait aucun sens, car, après tout, nous voulons la liste pour un ordinateur donné.

La seule solution que je vois consiste à effectuer une union entre votre requête d'origine et une nouvelle requête qui récupère la liste des applications qui sont pas trouvées sur cet ordinateur. 

La requête pourrait ressembler à ceci:

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
7
Cristian Lupascu

essaye ça

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
2
Oleg Dok

Vous devez faire 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 

Voici l'explication:

Résultat d'une jointure externe gauche (ou simplement gauche) pour la table A et B contient toujours tous les enregistrements de la table "gauche" (A), même si le condition de jointure ne trouve aucun enregistrement correspondant dans la "bonne" table (B). Cela signifie que si la clause ON correspond à 0 (zéro) enregistrement dans B, la jointure renverra toujours une ligne dans le résultat, mais avec NULL dans chaque column from B. Cela signifie qu'une jointure externe gauche renvoie tous les valeurs de la table de gauche, plus les valeurs correspondantes de la table de droite (ou NULL en cas d'absence de prédicat de jointure correspondant). Si la bonne table renvoie une ligne et la table de gauche renvoie plusieurs lignes correspondantes pour cela, les valeurs dans le tableau de droite seront répétées pour chaque ligne distincte sur la table de gauche. À partir d'Oracle 9i, les sorties LEFT OUTER L'instruction JOIN peut être utilisée aussi bien que (+).

1
aF.
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;
1
Behnam Mohammadi
SELECT Computer.Computer_Name, Application1.Name, Max(Soft.[Version]) as Version1
FROM Application1
inner JOIN Software
    ON Application1.ID = Software.Application_Id
cross join Computer
Left JOIN Software_Computer
    ON Software_Computer.Computer_Id = Computer.ID and Software_Computer.Software_Id = Software.Id
Left JOIN Software as Soft
    ON Soft.Id = Software_Computer.Software_Id
WHERE Computer.ID = 1 
GROUP BY Computer.Computer_Name, Application1.Name 
0
Atish Satish Gosavi

Essayez cela fonctionne 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
Gyan Singh

sélectionnez C.ComputerName, S.Version, A.Name depuis l'ordinateur C interne rejoindre Software_Computer SC sur C.Id = SC.ComputerId Inner join Software S sur SC.SoftwareID = S.Id Jointure interne Application A sur S.ApplicationId = A.Id;

0
aman