SQL Server: объединение нескольких таблиц с предложением WHERE - PullRequest
16 голосов
/ 06 января 2012

Я использую SQL Server и с трудом пытаюсь получить результаты запроса SELECT, который мне нужен. Я пробовал объединяться в разные порядки и использовать подзапросы, но ничего не получается так, как я хочу. Возьмите этот надуманный пример программных приложений с разными уровнями версий, которые могут быть установлены на компьютерах других пользователей.

Мне нужно выполнить JOIN с WHERE, но по какой-то причине я не могу получить желаемые результаты.

Может быть, я неправильно смотрю на свои данные, я не совсем уверен, почему я не могу заставить это работать.

Приложение Таблица

ID  Name
1   Word
2   Excel
3   Powerpoint

Программное обеспечение Таблица (содержит информацию о версии для различных приложений)

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

Software_Computer распределительная таблица

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

Компьютер Стол

ID  ComputerName
1   Name1
2   Name2

Я хочу запрос, который я мог бы выполнить, когда я выбираю определенный компьютер, чтобы показать, какая версия программного обеспечения и приложения имеет, но я также хочу, чтобы он отображал, какое приложение у него нет (версия будет NULL, так как на нем нет этого программного обеспечения)

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 

Я хочу следующий набор результатов

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

Но я просто получаю

Results
ComputerName   Name          Version
Name1          Word           2003
Name1          Excel          2007

Я думал, что RIGHT JOIN будет включать все результаты в таблицу приложения, даже если они не связаны с компьютером. Что я пропускаю / делаю неправильно?

Ответы [ 8 ]

13 голосов
/ 06 января 2012

При использовании LEFT JOIN или RIGHT JOIN имеет значение, положите ли вы фильтр в WHERE или в JOIN.

См. Ответ на аналогичный вопрос, который я написал некоторое время назад:
Какая разница в этих двух запросах при получении двух разных наборов результатов?

Короче говоря:

  • если вы включите его в предложение WHERE (как и вы, результаты, не связанные с этим компьютером, будут полностью отфильтрованы
  • если вместо этого поместить его в JOIN, результаты, не связанные с этим компьютером, появятся в результате запроса, только с NULL значениями
    -> это то, что вы хотите
8 голосов
/ 06 января 2012

В третьей ожидаемой строке (строка с Powerpoint) отфильтровывается условие Computer.ID = 1 (попробуйте выполнить запрос с Computer.ID = 1 or Computer.ID is null, чтобы увидеть, что происходит).

Однако удаление этого условия не имеет смысла, потому что, в конце концов, нам нужен список для данного компьютера.

Единственное решение, которое я вижу, это выполнить UNION между вашим исходным запросом и новым запросом, который возвращает список приложений, которые не найдены на этом компьютере.

Запрос может выглядеть так:

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
2 голосов
/ 06 января 2012

попробуйте

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 голос
/ 12 февраля 2014
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 голос
/ 06 января 2012

Вам нужно сделать 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 

Вот объяснение:

Результат левого внешнего соединения (или просто левого соединения) для таблицы A и B всегда содержит все записи «левой» таблицы (A), даже если условие объединения не находит подходящую запись в "правильной" таблице (В). Это означает, что если предложение ON соответствует 0 (нулю) записей в B, объединение все равно вернет строку в результате - но с NULL в каждом столбец из B. Это означает, что левое внешнее соединение возвращает все значения из левой таблицы плюс совпавшие значения из правой таблицы (или NULL в случае отсутствия соответствующего предиката соединения). Если правильный стол возвращает одну строку, а левая таблица возвращает более одной подходящей строки для этого значения в правой таблице будут повторяться для каждого отчетливый ряд на левом столе. От Oracle 9i и далее СЛЕВА Можно использовать оператор JOIN, а также (+).

0 голосов
/ 24 августа 2018
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 голосов
/ 14 ноября 2016

выберите C.ComputerName, S.Version, A.Name из компьютера C внутреннее соединение Software_Computer SC на C.Id = SC.ComputerId Внутреннее соединение Software S на SC.SoftwareID = S.Id Внутреннее соединение Приложение А на S.ApplicationId = A.Id;

0 голосов
/ 12 января 2016

Попробуйте это работает нормально ....

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 
...