OSTRZEŻENIE DOTYCZĄCE ROZWIĄZAŃ:
WIELE ISTNIEJĄCYCH ROZWIĄZAŃ DAJE NIEPRAWIDŁOWE WYDAJNOŚĆ, JEŚLI WIERSZE NIE SĄ WYJĄTKOWE
Jeśli jesteś jedyną osobą tworzącą tabele, może to nie mieć znaczenia, ale kilka rozwiązań da inną liczbę wierszy wyjściowych z danego kodu, gdy jedna z tabel może nie zawierać unikalnych wierszy.
OSTRZEŻENIE DOTYCZĄCE PROBLEMU:
W PRZYPADKU WIELU KOLUMN NIE ISTNIEJE, UWAŻNIE PRZEMYŚL, CO CHCESZ
Kiedy widzę wejście z dwiema kolumnami, wyobrażam sobie, że oznacza to dwie rzeczy:
- Wartości z kolumny a i kolumny b pojawiają się niezależnie w drugiej tabeli
- Wartości z kolumny a i kolumny b pojawiają się w drugiej tabeli razem w tym samym wierszu
Scenariusz 1 jest dość trywialny, po prostu użyj dwóch instrukcji IN.
Zgodnie z większością istniejących odpowiedzi, niniejszym przedstawiam przegląd wspomnianych i dodatkowych podejść do scenariusza 2 (oraz krótką ocenę):
ISTNIEJE (bezpieczne, zalecane dla SQL Server)
Jak zapewnia @mrdenny, EXISTS brzmi dokładnie tak, jak szukasz, oto jego przykład:
SELECT * FROM T1
WHERE EXISTS
(SELECT * FROM T2
WHERE T1.a=T2.a and T1.b=T2.b)
LEFT SEMI JOIN (Bezpieczne, zalecane dla dialektów, które go obsługują)
Jest to bardzo zwięzły sposób dołączania, ale niestety większość dialektów SQL, w tym serwer SQL, obecnie go nie obsługuje.
SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b
Wiele instrukcji IN (bezpieczne, ale uważaj na powielanie kodu)
Jak wspomniał @cataclysm, użycie dwóch instrukcji IN również może załatwić sprawę, być może nawet przewyższy inne rozwiązania. Jednak należy być bardzo ostrożnym przy powielaniu kodu. Jeśli kiedykolwiek zechcesz wybrać z innej tabeli lub zmienić instrukcję where, istnieje zwiększone ryzyko, że stworzysz niespójności w swojej logice.
Podstawowe rozwiązanie
SELECT * from T1
WHERE a IN (SELECT a FROM T2 WHERE something)
AND b IN (SELECT b FROM T2 WHERE something)
Rozwiązanie bez duplikacji kodu (uważam, że to nie działa w zwykłych zapytaniach SQL Server)
WITH mytmp AS (SELECT a, b FROM T2 WHERE something);
SELECT * from T1
WHERE a IN (SELECT a FROM mytmp)
AND b IN (SELECT b FROM mytmp)
WEWNĘTRZNE JOIN (technicznie można to zabezpieczyć, ale często się tego nie robi)
Powodem, dla którego nie polecam używania sprzężenia wewnętrznego jako filtru, jest to, że w praktyce ludzie często pozwalają, aby duplikaty w prawej tabeli powodowały duplikaty w lewej tabeli. Co gorsza, czasami sprawiają, że wynik końcowy jest inny, podczas gdy lewa tabela może nie być unikalna (lub nie jest unikalna w wybranych kolumnach). Co więcej, daje Ci możliwość faktycznego wybrania kolumny, której nie ma w lewej tabeli.
SELECT T1.* FROM T1
INNER JOIN
(SELECT DISTINCT a, b FROM T2) AS T2sub
ON T1.a=T2sub.a AND T1.b=T2sub.b
Najczęstsze błędy:
- Łączenie bezpośrednio na T2, bez bezpiecznego podzapytania. Prowadzi to do ryzyka powielania)
- SELECT * (gwarantowane pobranie kolumn z T2)
- SELECT c (nie gwarantuje, że Twoja kolumna pochodzi i zawsze będzie pochodzić z T1)
- Żadnego DISTINCT lub DISTINCT w niewłaściwym miejscu
ZŁĄCZENIE KOLUMN Z SEPARATOREM (Niezbyt bezpieczne, straszne wykonanie)
Problem funkcjonalny polega na tym, że jeśli użyjesz separatora, który może wystąpić w kolumnie, trudno jest upewnić się, że wynik jest w 100% dokładny. Problem techniczny polega na tym, że ta metoda często powoduje konwersję typów i całkowicie ignoruje indeksy, co skutkuje prawdopodobnie okropną wydajnością. Mimo tych problemów muszę przyznać, że czasami nadal używam go do zapytań ad-hoc na małych zbiorach danych.
SELECT * FROM T1
WHERE CONCAT(a,"_",b) IN
(SELECT CONCAT(a,"_",b) FROM T2)
Zwróć uwagę, że jeśli kolumny są numeryczne, niektóre dialekty SQL będą wymagały najpierw rzutowania ich na łańcuchy. Uważam, że serwer SQL zrobi to automatycznie.
Podsumowując: jak zwykle w SQL jest wiele sposobów, aby to zrobić, używanie bezpiecznych wyborów pozwoli uniknąć niespodzianek i zaoszczędzić czas i na dłuższą metę.