SQL Server: объединение нескольких таблиц с предложением WHERE

Я использую 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 будет включать все результаты в таблицу приложений, даже если они не связаны с компьютером. Что я пропущу/делаю неправильно?

Ответы

Ответ 1

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

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

Короче:

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

Ответ 2

Третья строка, которую вы ожидаете (с 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

Ответ 3

попробуйте это

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

Ответ 4

Вам нужно сделать 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, LEFT OUTER Можно использовать оператор JOIN, а также (+).

Ответ 5

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;

Ответ 6

Попробуйте эту работу...

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 

Ответ 7

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