2012-01-06 24 views
16

Sto utilizzando SQL Server e sto avendo difficoltà a cercare di ottenere i risultati da una query SELECT che desidero. Ho provato ad unirmi a diversi ordini e ad usare sottoquery, ma niente funziona nel modo che voglio. Prendi questo esempio forzato di applicazioni software, con diversi livelli di versione, che potrebbero essere installati su computer di persone.SQL Server: più join tabella con una clausola WHERE

Ho bisogno di eseguire un JOIN con un WHERE, ma per qualche motivo non riesco a ottenere i risultati che voglio.

Forse sto guardando i miei dati errati, non sono abbastanza sicuro del motivo per cui non riesco a farlo funzionare.

Applicazione tavolo

ID Name 
1 Word 
2 Excel 
3 Powerpoint 

Software Table (contiene informazioni sulla versione per le diverse applicazioni)

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

Software_Computer tavolo svincolo

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

Computer tavolo

ID ComputerName 
1 Name1 
2 Name2 

Voglio una query che avrei potuto correre dove mi selezionare un computer specifico per visualizzare la versione del software e l'applicazione è ha, ma voglio anche che per visualizzare quale applicazione non è così hanno (la versione sarebbe un NULL dal momento che non ha che il software su di esso)

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 

voglio il seguente risultato impostato

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

Ma ottengo solo

Results 
ComputerName Name   Version 
Name1   Word   2003 
Name1   Excel   2007 

ho pensato che il RIGHT JOIN includerebbe tutti i risultati nella tabella di applicazione, anche se non sono associati con il computer. Cosa mi sfugge/sto sbagliando?

+0

Forse è perché sono alto, ma non stai limitando le applicazioni dal software del computer? E non ci sono solo due software appartenenti al computer 1? Inoltre, non dovrebbe un join di sinistra scavalcare un join destro? Che ho bisogno di controllare. E PowerPoint ha una versione, quindi perché ti aspetteresti nullo? Inoltre, con più versioni per applicazioni, nella tabella software del computer, è necessario mantenere anche un id versione, altrimenti avresti tutti i tipi di risultati incasinati. – frostymarvelous

risposta

12

Quando si utilizza LEFT JOIN o RIGHT JOIN, fa una differenza se si mette il filtro nel WHERE o nel JOIN.

Vedere questa risposta a una domanda simile che ho scritto qualche tempo fa:
What is the difference in these two queries as getting two different result set?

In breve:

  • se lo metti in clausola WHERE (come avete fatto, i risultati che aren Sono associati a quel computer completamente filtrati
  • se lo si inserisce nello JOIN invece, i risultati che non sono associati a quel computer appaiono nel risultato della query, solo con NULL Valori
    -> questo è ciò che si vuole
+0

Questo sembra essere quello che mi mancava. L'ho appena testato e sembra che stia producendo i risultati che voglio. Grazie. – Stormchao

+0

Il risultato atteso è di avere "nome1" elencato con "powerpoint". Aggiungendo "computer.ID = 1" al join non si ottiene il risultato previsto. La riga con il valore "powerpoint" avrà la colonna del nome del computer come valore nullo. Tra le soluzioni w0lf e Oleg, oleg è il migliore dei due. –

1

È necessario eseguire 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 

Ecco la spiegazione:

Il risultato di una join esterno sinistro (o semplicemente LEFT JOIN) per la tabella A e B contiene sempre tutti i record della tabella "sinistra" (A), anche se la condizione di join non trova alcun record corrispondente nella tabella "destra" (B). Ciò significa che se la clausola ON corrisponde a 0 (zero) record in B, il join restituirà comunque una riga nel risultato, ma con NULL in ogni colonna da B. Ciò significa che un join esterno sinistro restituisce tutti i valori dalla tabella di sinistra, oltre ai valori corrispondenti dalla tabella di destra (o NULL in caso di mancata corrispondenza del predicato di join).Se la tabella giusta restituisce una riga e la tabella sinistra restituisce più di una riga corrispondente per esso, i valori nella tabella di destra verranno ripetuti per ogni riga distinta nella tabella sinistra. Da Oracle 9i in poi è possibile utilizzare l'istruzione JOIN LEFT OUTER e (+).

+0

humm, perché downvote oO –

+0

Questo non funziona. LEFT OUTER JOIN non cambia nulla; il set di risultati intermedio ha ancora "nome1" associato a "parola" ed "excel".Il join esterno destro includerà ancora "powerpoint" nel prossimo set di risultati ma associato al valore NULL per il nome del computer. Quindi, infine, la condizione where rimuove il record powerpoint dal set di risultati perché il suo valore null per il nome computer <> "name1" –

7

La terza fila che ci si aspetta (quello con Powerpoint) viene filtrata dalla condizione Computer.ID = 1 (Provare a eseguire la query con il Computer.ID = 1 or Computer.ID is null esso per vedere cosa succede) .

Tuttavia, l'eliminazione di tale condizione non avrebbe senso, perché dopo tutto, vogliamo l'elenco per un determinato computer.

L'unica soluzione che vedo è eseguire un UNION tra la query originale e una nuova query che recupera l'elenco di applicazioni che è non trovato su tale computer.

La query potrebbe assomigliare a questo:

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

Grazie. Questo è perfetto! Stavo cercando di realizzare troppo con un singolo SELECT. Avrei rinunciato a questo se potessi. – Stormchao

+0

Sapevo che era quello di cui avevi bisogno; era molto chiaro dalla domanda. Non sono sicuro del motivo per cui ho avuto un downvote su questa risposta. Ad ogni modo, sono molto contento che sia stato d'aiuto. :-) – GolfWolf

+0

Non è performante come quello di Oleg. Inoltre, alcuni problemi minori che ho con esso sono 1) UNION ALL perché i set sono già mutuamente esclusivi per definizione 2) essere più espliciti con il CROSS JOIN –

2

provare questo

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

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti da un autore, lascia un commento sotto il loro post - puoi sempre commentare i tuoi post, e una volta che hai [reputazione] sufficiente (http://stackoverflow.com/help/whats-reputation) essere in grado di [commentare qualsiasi post] (http://stackoverflow.com/help/privileges/comment). – Micha

0

Prova questa lavorando bene ....

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

selezionare C.ComputerName, S.Version, A.Name.210 da computer C join interno Software_Computer SC su C.Id = SC.ComputerId INNER JOIN Software S su SC.SoftwareID = S.Id interno unirsi applicazione A su S.ApplicationId = A.Id;

Problemi correlati