Ilekroć muszę sprawdzić, czy istnieje jakiś wiersz w tabeli, zwykle piszę taki warunek:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Niektóre inne osoby piszą to tak:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Gdy warunek jest NOT EXISTS
zamiast EXISTS
: W niektórych przypadkach mogę napisać go z LEFT JOIN
dodatkowym warunkiem (czasami nazywanym anty - dołączeniem ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
Staram się tego unikać, ponieważ uważam, że znaczenie jest mniej jasne, szczególnie gdy to, co jest twoje, primary_key
nie jest tak oczywiste, lub gdy twój klucz podstawowy lub warunek łączenia jest wielokolumnowy (i możesz łatwo zapomnieć o jednej z kolumn). Czasami jednak utrzymujesz kod napisany przez kogoś innego ... i to jest właśnie tam.
Czy jest jakaś różnica (inna niż styl) do użycia
SELECT 1
zamiastSELECT *
?
Czy istnieje przypadek narożny, w którym nie zachowuje się tak samo?Chociaż to, co napisałem, to standardowy SQL (AFAIK): Czy istnieje taka różnica dla różnych baz danych / starszych wersji?
Czy pisanie antijoin ma jakąś przewagę?
Czy współcześni planiści / optymaliści traktują to inaczej niżNOT EXISTS
klauzula?
źródło
EXISTS (SELECT FROM ...)
.Odpowiedzi:
Nie, nie ma żadnej różnicy w wydajności pomiędzy
(NOT) EXISTS (SELECT 1 ...)
i(NOT) EXISTS (SELECT * ...)
we wszystkich głównych DBMS. Często też widziałem,(NOT) EXISTS (SELECT NULL ...)
że jestem używany.W niektórych można nawet pisać,
(NOT) EXISTS (SELECT 1/0 ...)
a wynik jest taki sam - bez żadnego błędu (dzielenie przez zero), co dowodzi, że wyrażenie nie jest nawet oceniane.Jeśli chodzi o
LEFT JOIN / IS NULL
metodę antijoin, korekta: jest to równoważne zNOT EXISTS (SELECT ...)
.W tym przypadku
NOT EXISTS
vsLEFT JOIN / IS NULL
, możesz otrzymać różne plany wykonania. Na przykład w MySQL i głównie w starszych wersjach (wcześniejszych niż 5.7) plany byłyby dość podobne, ale nie identyczne. Optymalizatory innych DBMS (SQL Server, Oracle, Postgres, DB2) są - o ile wiem - mniej więcej w stanie przepisać te dwie metody i rozważyć te same plany dla obu. Nadal nie ma takiej gwarancji, a podczas optymalizacji warto sprawdzać plany z różnych równoważnych przepisań, ponieważ mogą wystąpić przypadki, że każdy optymalizator nie przepisuje (np. Złożone zapytania, wiele sprzężeń i / lub tabele pochodne / podkwerendy wewnątrz podkwerendy, w których warunki z wielu tabel, kolumny złożone stosowane w warunkach łączenia) lub wybory i plany optymalizatora różnią się w zależności od dostępnych indeksów, ustawień itp.Należy również pamiętać, że
USING
nie można go używać we wszystkich DBMS (na przykład SQL Server). CzęściejJOIN ... ON
działa wszędzie.A kolumny muszą być poprzedzone nazwą tabeli / aliasem,
SELECT
aby uniknąć błędów / dwuznaczności, gdy mamy połączenia.Zazwyczaj wolę również umieścić połączoną kolumnę w
IS NULL
sprawdzeniu (chociaż PK lub dowolna niepustowa kolumna byłaby OK, może być użyteczna dla wydajności, gdy planLEFT JOIN
użycia indeksu nieklastrowego):Istnieje również trzecia metoda dla antijoins, wykorzystująca,
NOT IN
ale ma inną semantykę (i wyniki!), Jeśli kolumna wewnętrznej tabeli jest zerowa. Można go jednak wykorzystać, wykluczając wiersze zNULL
, dzięki czemu zapytanie jest równoważne z poprzednimi 2 wersjami:Zwykle daje to również podobne plany w większości DBMS.
źródło
[NOT] IN (SELECT ...)
, choć równoważne, działały bardzo słabo. Unikaj tego!SELECT *
z pewnością wykonuje więcej pracy. Dla uproszczenia radzę używaćSELECT 1
Jest jeszcze jedna kategoria przypadkach
SELECT 1
iSELECT *
nie są wymienne - a dokładniej, jeden zawsze będzie akceptowane w tych przypadkach, gdy inne przeważnie nie będzie.Mówię o przypadkach, w których musisz sprawdzić, czy istnieją wiersze zgrupowanego zestawu. Jeśli tabela
T
ma kolumnyC1
iC2
sprawdzasz, czy istnieją grupy wierszy pasujące do określonego warunku, możesz użyćSELECT 1
tego w następujący sposób:ale nie możesz używać
SELECT *
w ten sam sposób.To jest tylko aspekt składniowy. Jeśli obie opcje są akceptowane składniowo, najprawdopodobniej nie będzie żadnej różnicy pod względem wydajności lub zwracanych wyników, jak wyjaśniono w innej odpowiedzi .
Dodatkowe uwagi po komentarzach
Wydaje się, że niewiele produktów bazodanowych faktycznie obsługuje to rozróżnienie. Produkty takie jak SQL Server, Oracle, MySQL i SQLite z radością przyjmą
SELECT *
powyższe zapytanie bez żadnych błędów, co prawdopodobnie oznacza, że traktują ISTNIEJESELECT
w specjalny sposób.PostgreSQL to jeden RDBMS, w którym
SELECT *
może się nie powieść, ale może nadal działać w niektórych przypadkach. W szczególności, jeśli grupujesz według PK,SELECT *
będzie działać dobrze, w przeciwnym razie nie powiedzie się komunikat:źródło
GROUP BY
, pojęcie „*
nie ma sensu” (a przynajmniej nie jest tak jasne).Prawdopodobnie interesującym sposobem ponownego napisania
EXISTS
klauzuli, która daje czystsze i być może mniej mylące zapytanie, przynajmniej w SQL Server byłoby:Wersja antyprzyłączona wyglądałaby następująco:
Oba są zazwyczaj zoptymalizowane do tego samego planu co
WHERE EXISTS
lubWHERE NOT EXISTS
, ale intencja jest jednoznaczna i nie masz „dziwnego”1
lub*
.Co ciekawe, problemy z zerowym sprawdzaniem
NOT IN (...)
są problematyczne<> ALL (...)
, podczas gdyNOT EXISTS (...)
nie cierpią z powodu tego problemu. Rozważ następujące dwie tabele z kolumną zerowalną:Dodamy trochę danych do obu, z niektórymi pasującymi wierszami, a niektóre, które nie:
NOT IN (...)
Zapytania:Ma następujący plan:
Zapytanie nie zwraca wierszy, ponieważ wartości NULL uniemożliwiają potwierdzenie równości.
To zapytanie z
<> ALL (...)
pokazuje ten sam plan i nie zwraca żadnych wierszy:Wariant przy użyciu
NOT EXISTS (...)
, pokazuje nieco inny kształt planu i zwraca wiersze:Plan:
Wyniki tego zapytania:
To sprawia, że używanie
<> ALL (...)
jest równie podatne na problematyczne wyniki, jakNOT IN (...)
.źródło
*
się za dziwnego: czytamEXISTS (SELECT * FROM t WHERE ...)
ASthere is a _row_ in table _t_ that...
. W każdym razie lubię mieć alternatywy, a twoja jest wyraźnie czytelna. Jedna wątpliwość / zastrzeżenie: jak będzie się zachowywał, jeśli nieb
będzie dopuszczalny? [Miałem złe doświadczenia i kilka krótkich nocy, gdy próbowałem znaleźć błąd spowodowany przezx IN (SELECT something_nullable FROM a_table)
]Należy zrobić „dowód”, że są one identyczne (w MySQL)
następnie powtórz za pomocą
SELECT 1
. W obu przypadkach „rozszerzone” wyjście pokazuje, że zostało przekształconeSELECT 1
.Podobnie
COUNT(*)
zamienia się wCOUNT(0)
.Kolejna rzecz do zapamiętania: W ostatnich wersjach wprowadzono ulepszenia optymalizacji. Może warto porównać
EXISTS
z anty-złączeniami. Twoja wersja może lepiej sobie radzić z jedną z drugą.źródło
W niektórych bazach danych ta optymalizacja jeszcze nie działa. Podobnie jak na przykład w PostgreSQL Począwszy od wersji 9.6, to się nie powiedzie.
I to się powiedzie.
Nie udaje się, ponieważ następujące błędy zawodzą, ale nadal oznacza to, że istnieje różnica.
Więcej informacji o tym dziwactwie i naruszeniu specyfikacji można znaleźć w mojej odpowiedzi na pytanie: czy specyfikacja SQL wymaga GROUP BY w EXISTS ()
źródło
Zawsze używałem
select top 1 'x'
(SQL Server)Teoretycznie
select top 1 'x'
byłoby bardziej wydajneselect *
, ponieważ pierwszy byłby kompletny po wybraniu stałej dotyczącej istnienia szeregu kwalifikacyjnego, podczas gdy drugi wybrałby wszystko.JEDNAK, chociaż bardzo wcześnie mogło być istotne, optymalizacja sprawiła, że różnica nie była istotna w prawdopodobnie wszystkich głównych RDBS.
źródło
top n
bezorder by
jest dobrym pomysłem.select top 1 'x'
nie powinien być bardziej wydajny niżselect *
wExist
wyrażeniu. Praktycznie może być bardziej wydajne, jeśli optymalizator działa nieoptymalnie, ale teoretycznie oba wyrażenia są równoważne.IF EXISTS(SELECT TOP(1) 1 FROM
jest lepszym nawykiem długoterminowym i na różnych platformach, ponieważ nie musisz nawet martwić się o to, jak dobra lub zła jest twoja obecna platforma / wersja; a SQL zmierza odTOP n
parametryzacjiTOP(n)
. To powinna być umiejętność jednorazowa.źródło
TOP
nie jest nawet poprawnym SQL.TOP (n)
w „SQL” - standardowym języku zapytań. Jest jeden na T-SQL, który jest dialektem używanym przez Microsoft SQL Server.