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;