IN vs ANY operator w PostgreSQL

Odpowiedzi:

158

(Ani „operator”, INani ANY„ nie jest”. „Konstrukcja” lub „element składni”).

Logicznie , cytując instrukcję :

INjest równoważne = ANY.

Ale istnieją dwa warianty składniowe z INi dwa warianty ANY. Detale:

IN zabranie zestawu jest równoznaczne z = ANYwzięciem zestawu , jak pokazano tutaj:

Ale drugi wariant każdego z nich nie jest równoważny z drugim. Drugi wariant ANYkonstrukcji przyjmuje tablicę (musi być rzeczywistym typem tablicy), podczas gdy drugi wariant INprzyjmuje listę wartości oddzielonych przecinkami . Prowadzi to do różnych ograniczeń w przekazywaniu wartości i może również prowadzić do różnych planów zapytań w szczególnych przypadkach:

ANY jest bardziej wszechstronny

ANYKonstrukcja jest bardziej uniwersalny, ponieważ mogą one być połączone z różnymi operatorami, nie tylko =. Przykład:

SELECT 'foo' LIKE ANY('{FOO,bar,%oo%}');

W przypadku dużej liczby wartości zapewnienie zestawu skal dla każdej z nich jest lepsze:

Związane z:

Inwersja / przeciwieństwo / wykluczenie

„Znajdź wiersze, w których idznajduje się podana tablica”:

SELECT * FROM tbl WHERE id = ANY (ARRAY[1, 2]);

Inwersja: „Znajdź wiersze, których nieid ma w tablicy”:

SELECT * FROM tbl WHERE id <> ALL (ARRAY[1, 2]);
SELECT * FROM tbl WHERE id <> ALL ('{1, 2}');  -- equivalent array literal
SELECT * FROM tbl WHERE NOT (id = ANY ('{1, 2}'));

Wszystkie trzy równoważne. Pierwsza z konstruktorem tablicy , pozostałe dwie z literałem tablicowym . Typ danych można jednoznacznie wyprowadzić z kontekstu. W przeciwnym razie może być wymagana wyraźna obsada, na przykład '{1,2}'::int[].

Wiersze z id IS NULLnie przechodzą żadnego z tych wyrażeń. Aby NULLdodatkowo uwzględnić wartości:

SELECT * FROM tbl WHERE (id = ANY ('{1, 2}')) IS NOT TRUE;
Erwin Brandstetter
źródło
4
Byłoby miło wyraźnie wyjaśnić, że wyniki drugiego wariantu zawsze będą takie same. Jestem w 99% pewien, że tak jest, ale odpowiedź nie wydaje się tego określać. Oznacza to, że SELECT * from mytable where id in (1, 2, 3)zawsze będą powodować te same wiersze, co SELECT * from mytable where id = ANY('{1, 2, 3}'), nawet jeśli potencjalnie mogą mieć różne plany zapytań.
KPD
1
ANY nie można łączyć z !=operatorem. Myślę, że nie jest to udokumentowane, ale select * from foo where id != ANY (ARRAY[1, 2])to nie to samo, co select * from foo where id NOT IN (1, 2). Z drugiej strony select * from foo where NOT (id = ANY (ARRAY[1, 2]))działa zgodnie z oczekiwaniami.
qris,
1
@qris: ANYmożna łączyć z !=operatorem. Ale to nie wszystko. Dodałem rozdział powyżej. (Zauważ, że <>jest to operator w standardowym SQL - choć !=jest również akceptowany w Postgres.)
Erwin Brandstetter,
Jak działa ostatnia wersja zawierająca NULLwartości? Działałby WHERE id = ANY (ARRAY[1, 2]) OR id IS NULL;równie dobrze?
dvtan
1
@dvtan: (id = ...) IS NOT TRUEdziała, ponieważ wartościowane są id = ...tylko TRUEwtedy, gdy istnieje rzeczywiste dopasowanie. Wyniki FALSElub NULLzdaj nasz test. Zobacz: stackoverflow.com/a/23767625/939860 . Twoje dodane wyrażenie sprawdza coś innego. To byłoby równoważneWHERE id <> ALL (ARRAY[1, 2]) OR id IS NULL;
Erwin Brandstetter
3

Istnieją dwa oczywiste punkty, a także punkty w drugiej odpowiedzi:

  • Są dokładnie równoważne podczas korzystania z zapytań podrzędnych:

    SELECT * FROM table
    WHERE column IN(subquery);
    
    SELECT * FROM table
    WHERE column = ANY(subquery);

Z drugiej strony:

  • Tylko INoperator umożliwia prostą listę:

    SELECT * FROM table
    WHERE column IN(… ,  , …);

Zakładanie, że są dokładnie takie same, przyłapało mnie kilka razy, kiedy zapominałem, że ANYto nie działa w przypadku list.

Manngo
źródło