Выбор записей в таблице SQL, если выполняется любой из операторов JOIN

#sql #sql-server

#sql #sql-сервер

Вопрос:

У меня есть таблица Companies , которую я хотел бы отфильтровать только для записей, на которые ссылаются две другие таблицы, Employees и Contracts .

Мой первоначальный инстинкт заключается в том, что я мог бы сделать что-то вроде этого:

 SELECT c.* 
FROM Companies c
JOIN Employees ON EmployerId = c.Id
JOIN Contracts ON CompanyId = c.Id
  

Однако при этом выбираются записи, на которые ссылаются обе Employees Contracts таблицы и .

Как мне переписать этот запрос, чтобы сопоставить записи, на которые ссылаются из любой таблицы?

Комментарии:

1. Похоже, вам нужно пересечь

Ответ №1:

Изначально я создал версию с ошибками, которая завершилась неудачей, с ЛЕВЫМИ ВНЕШНИМИ СОЕДИНЕНИЯМИ. Это можно было бы исправить, но на самом деле, вероятно, более эффективно использовать ТАМ, ГДЕ СУЩЕСТВУЕТ, например,

 SELECT c.* 
FROM Companies c
WHERE EXISTS (SELECT EmployerID FROM Employee WHERE EmployerID = c.ID)
    OR EXISTS (SELECT CompanyId FROM Contracts WHERE CompanyId = c.ID)
  

Причина, по которой моя предыдущая версия была ошибочной, заключалась в том, что в ней было соединение с сотрудниками и контрактами, что означало, что оно возвращало бы одну строку для каждой существующей (и, возможно, перекрестное произведение результатов). Даже с GROUP BY или DISTINCT, вероятно, SQL Server пришлось бы проделать большую работу.

Приведенное выше решение получает только одну строку для каждой компании, независимо от количества сотрудников или контрактов.

Комментарии:

1. Это кажется намного более элегантным, чем обычное решение, которое заключается в ОБЪЕДИНЕНИИ соединений. К тому времени, когда я вызвал DISTINCT для удаления дубликатов, я подозреваю, что это намного медленнее, чем то, что вы предложили.

2. Поскольку результаты этого запроса помещаются во временную таблицу, мой текущий обходной путь — просто добавить второе СОЕДИНЕНИЕ во временную таблицу с помощью INSERT INTO . Это тоже работает, но не так быстро и не имеет дело с дубликатами.

3. Будет ли это работать лучше, если я использую «SELECT 1» вместо «SELECT EmployerId» и «SELECT CompanyID»?

4. ‘SELECT 1’ может дать вам очень незначительный прирост производительности. ‘SELECT * ‘ также может это сделать. Но на практике я не заметил разницы. Попробуйте повернуть SET STATISTICS TIME, IO ON и попробовать их (в разных порядках), чтобы увидеть. Что касается наилучших ответов, иногда это зависит от размера данных и индексов. Если у вас много данных, вышеуказанное также может вызвать проблемы. Вы также можете попробовать SELECT c.* FROM Companies c INNER JOIN (SELECT EmployerID as ID FROM Employees UNION SELECT CompanyID FROM Contracts) a ON c.Id = a.Id

Ответ №2:

Вероятно UNION , для этого вам понадобится

 SELECT c.* 
FROM Companies c
JOIN Employees ON EmployerId = c.Id
UNION
SELECT c.* 
FROM Companies c
JOIN Contracts ON CompanyId = c.Id