2014-09-19 2 views
2

В SQL, как бы я сделал что-то вроде обратного соединения?Получить строки, которые не соответствуют строкам в другой таблице

Например предположим, у меня есть следующие две таблицы

UsedSlide 
    SlideId 
    UserId 
    SomeOtherValue 

LegacySlide 
    SlideId 
    UserId 

Как бы выбрать все строки в UsedSlide где SlideId и UserId не совпадают со значениями в любой строке LegacySlide?

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

Бонус: В моем сценарии набор данных невелик, но что, если он был большой? Как сделать это наиболее эффективно?

ответ

4

Вы можете использовать not exists оператора:

SELECT * 
FROM UsedSlide u 
WHERE NOT EXISTS (SELECT * 
        FROM LegacySlide l 
        WHERE u.SlideId = l.SlideId AND u.UserId = l.UserId) 
+0

Да, это то, что я думал (также 'ВЫБРАТЬ 1' может быть лучше). Но не будет ли это префикс ужасно на больших наборах данных? –

+1

Acc. к его «бонусу-вопросу», «НЕ СУЩЕСТВУЕТ» - также самый эффективный подход (с соответствующими индексами). http://sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join –

+1

@GeorgeMauer: также используется 'SELECT 1', но он не эффективнее, чем' SELECT * '. –

2

LEFT JOIN МОЖНО ИСПОЛЬЗОВАТЬ

SELECT * from 
    UsedSlide US 
    LEFT JOIN LegacySlide LS 
    ON US.SlideId = LS.SlideId 
    and US.UserId = LS.UserId 
    WHERE LS.SlideId is NULL 
    AND LS.UserId is NULL 
+0

Это довольно интересно ... любая идея о производительности этого в сравнении с «НЕ СУЩЕСТВУЕТ»? Кроме того, не следует ли использовать условие 'WHERE'' ls', а не 'us'? –

+0

В SQL-сервере с NOT EXISTS наблюдается некоторое ухудшение производительности для большого набора данных.да, вам нужно проверить LS, я обновлю – radar

+1

@georg: в комментарии ito другой ответ я предоставил ссылку на статью, которая добавляет эту тему, которая объясняет, почему Not Exists может быть более эффективным, чем другие подходы, и имеет нет проблем с нулевыми значениями (например, Join). –

1

Используйте КРОМЕ Оператор:

Некоторые примеры кода я испытал:

CREATE TABLE #UsedSlide (SlideId INT NOT NULL, UserId INT NOT NULL, SomeOtherValue VARCHAR(10) NOT NULL) 
CREATE TABLE #LegacySlide (SlideId INT NOT NULL, UserId INT NOT NULL) 

INSERT INTO #UsedSlide(SlideId, UserId, SomeOtherValue) 
VALUES 
(1, 35, 'testing123'), 
(2, 39, 'testingabc'), 
(3, 24, 'testingxyz') 

INSERT INTO #LegacySlide(SlideId, UserId) 
VALUES (1, 35), 
     (2, 39) 

SELECT SlideId, UserId 
FROM #UsedSlide 
EXCEPT 
SELECT SlideId, UserId 
FROM #LegacySlide 

Это дает следующий набор результатов:

SlideId UserId 
------- ------ 
3  24 

Примечание: порядок отчетности в КРОМЕ важно здесь. Если вы запустили последнее заявление как:

SELECT SlideId, UserId 
FROM #LegacySlide 
EXCEPT 
SELECT SlideId, UserId 
FROM #UsedSlide 

... это НЕ привело бы к желаемому эффекту. Это в основном операция над наборами: найдите «кортеж», который не входит в набор кортежей.

Оператор компаньон КРОМЕ является ПЕРЕСЕЧЕНИЕ - найти кортежи общего между двумя наборами. Оба являются очень полезными операторами.

Как в стороне, я считаю, что Oracle имеет МИНУС оператора, что примерно эквивалентно КРОМЕ (кто-то может проверить и найти ссылку?)

+0

Может ли это использоваться для выбора столбцов * all * из 'UsedSlide', хотя нам нужно соединение для этого? –

+1

Вы ** ** будете необходимо присоединиться обратно снова объединяются с исходной таблицей: SELECT, u.SlideId, u.UserId, SomeOtherValue ОТ #UsedSlide у JOIN ( \t ВЫБОР SlideId, UserId \t ОТ #UsedSlide \t кРОМЕ \t ВЫБОР SlideId, USERID \t ОТ #LegacySlide ) м пО u.SlideId = m.SlideId И u.UserId = m.UserId –

+1

@Rajesh, ваш подход очень жизнеспособны, но производительность может пострадать для больших наборов данных из-за проверки IS NULL. Вот хороший пост, который описывает низкую производительность IS NULL: http://blogs.lessthandot.com/index.php/datamgmt/datadesign/do-you-use-column-param-or-param-is-null/ –

Смежные вопросы