2012-01-06 9 views
16

Używam programu SQL Server i mam trudny czas próbując uzyskać wyniki z zapytania SELECT, które chcę. Próbowałem łączenia w różnych zamówień i przy użyciu podzapytania, ale nic nie działa tak, jak chcę. Weź ten wymyślony przykład aplikacji z różnymi poziomami wersji, które mogą być instalowane na komputerach ludzi.Serwer SQL: łączenie wielu tabel z klauzulą ​​WHERE

Muszę wykonać JOIN z WHERE, ale z jakiegoś powodu nie mogę uzyskać wyników, które chcę.

Może patrzę na moje dane źle, nie jestem do końca pewien, dlaczego nie mogę tego zrobić.

Zastosowanie stół

ID Name 
1 Word 
2 Excel 
3 Powerpoint 

Software stołowy (zawiera informacje o wersji dla różnych zastosowań)

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

Software_Computer tabeli junction

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

Computer stół

ID ComputerName 
1 Name1 
2 Name2 

Chcę zapytać, że mogę uruchomić gdzie wybrać konkretny komputer w celu wyświetlenia konkretnych wersji oprogramowania i aplikacji ma, ale ja też chcę go do wyświetlania co aplikacja nie robi mieć (wersja byłaby NULL ponieważ nie ma tego oprogramowania na nim)

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 

chcę następujący zestaw wyników

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

Ale ja po prostu

Results 
ComputerName Name   Version 
Name1   Word   2003 
Name1   Excel   2007 

Myślałem, że RIGHT JOIN obejmowałby wszystkie wyniki w tabeli aplikacji, nawet jeśli nie są one związane z komputerem. Czego mi brakuje/robię źle?

+0

Może to dlatego, że jestem wysoki, ale czy nie ograniczasz Aplikacji przez oprogramowanie komputerowe? A czy nie ma tylko dwóch programów należących do komputera 1? Ponadto, czy lewe połączenie nie powinno mieć wpływu na prawe połączenie? Muszę to sprawdzić. A PowerPoint ma wersję, więc dlaczego miałbyś oczekiwać wartości null? Ponadto, w wielu wersjach dla aplikacji, w tabeli oprogramowania na komputery, musisz także zachować identyfikator wersji, inaczej będziesz miał wszystkie rodzaje pomieszanych wyników. – frostymarvelous

Odpowiedz

12

Podczas korzystania LEFT JOIN lub RIGHT JOIN, to robi różnicę, czy można umieścić filtr w WHERE lub do JOIN.

Zobacz tę odpowiedź na podobne pytanie pisałem jakiś czas temu:
What is the difference in these two queries as getting two different result set?

W skrócie:

  • jeśli umieścić go w klauzuli WHERE (jak to zrobiłeś, wyniki, które aren Jeśli z tym komputerem zostanie całkowicie odfiltrowany
  • , jeśli zamiast tego zostanie wstawiony do JOIN, wyniki, które nie są związane z tym komputerem, pojawią się w wyniku zapytania, tylko z NULL wartości
    -> to co chcesz
+0

To wydaje się być tym, czego mi brakowało. Właśnie to przetestowałem i wygląda na to, że przyniosę oczekiwane rezultaty. Dziękuję Ci. – Stormchao

+0

Spodziewanym rezultatem jest podanie "name1" z "powerpoint". Dodanie "computer.ID = 1" do join nie daje oczekiwanego rezultatu. Wiersz z wartością "powerpoint" będzie miał kolumnę z nazwą komputera o wartości pustej. Pomiędzy rozwiązaniami w0lf's i Olega, oleg jest lepszym rozwiązaniem. –

1

Należy wykonać 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 

Oto wyjaśnienie:

Wynikiem lewe sprzężenie zewnętrzne (lub po prostu LEFT JOIN) do tabeli A i B zawsze zawiera wszystkie zapisy w "lewo" tabeli (A), nawet jeśli warunek łączenia nie znajdzie żadnego pasującego rekordu w "poprawnej" tabeli (B). Oznacza to, że jeśli klauzula ON pasuje do 0 (zero) rekordów w B, , to sprzężenie nadal będzie zwracać wiersz w wyniku - ale z wartością NULL w każdej kolumnie z B. Oznacza to, że lewe sprzężenie zewnętrzne zwraca wszystkie wartości z lewej tabeli plus dopasowane wartości z prawej tabeli (lub NULL w przypadku braku zgodnego predykatu łączenia).Jeśli prawidłowa tabela zwróci jeden wiersz, a lewa tabela zwróci dla niego więcej niż jeden pasujący wiersz, to wartości z prawej tabeli będą powtarzane dla każdego odrębnego wiersza po lewej stronie tabeli. Począwszy od Oracle 9i można używać instrukcji LEFT OUTER JOIN jak również (+).

+0

humm, dlaczego downvote oO –

+0

To nie działa. LEFT OUTER JOIN nic nie zmienia; pośredni zestaw wyników nadal ma "name1" powiązany z "word" i "excel".Prawe sprzężenie zewnętrzne nadal będzie zawierać "punkt mocy" w następnym zestawie wyników, ale będzie powiązane z wartością NULL dla nazwy komputera. Następnie w końcu warunek where usuwa rekord powerpoint z zestawu wyników, ponieważ jego wartość pusta dla nazwy komputera <> "name1" –

7

Trzeci rząd można oczekiwać (jeden z PowerPoint) sączy się przez warunek Computer.ID = 1 (spróbuj uruchomić kwerendę z Computer.ID = 1 or Computer.ID is null, aby sprawdzić, co się dzieje) .

Jednak odrzucenie tego warunku nie miałoby sensu, ponieważ przecież chcemy mieć listę dla danego komputera.

Jedyne rozwiązanie, jakie widzę, to wykonanie UNION pomiędzy pierwotnym zapytaniem a nowym zapytaniem, które pobiera listę aplikacji, które są na tym komputerze znalezione na , a nie.

Kwerenda może wyglądać następująco:

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

Dziękuję. To jest doskonałe! Starałem się osiągnąć zbyt wiele dzięki jednemu SELECT. Zrobiłbym to, gdybym mógł. – Stormchao

+0

Wiedziałem, że tego właśnie potrzebujesz; było bardzo jasne z tego pytania. Nie jestem pewien, dlaczego mam odpowiedź na tę odpowiedź. W każdym razie, bardzo się cieszę, że pomogło. :-) – GolfWolf

+0

To nie jest tak wydajne jak Oleg. A także, kilka drobnych problemów, które mam z nim są 1) UNION ALL, ponieważ zestawy są już wykluczające się z definicji 2) być bardziej wyraźne z CROSS JOIN –

2

spróbować

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

To nie zapewnia odpowiedzi na pytanie. Aby skrytykować lub poprosić o wyjaśnienie od autora, zostaw komentarz pod swoim postem - zawsze możesz komentować swoje posty, a gdy już masz wystarczającą [reputację] (http://stackoverflow.com/help/whats-reputation), być w stanie [komentować dowolny wpis] (http://stackoverflow.com/help/privileges/comment). – Micha

0

Spróbuj to działa dobrze ....

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

wybrać C.ComputerName, S.Version, A.Name z łączenia wewnętrznego C komputera Software_Computer SC na C.Id = SC.ComputerId Oprogramowanie do łączenia wewnętrznego Oprogramowanie S na SC.SoftwareID = S.Id Połączenie wewnętrzne Aplikacja A na S.ApplicationId = A.Id;