Internamente, ci sono due forme separate di IN
, oltre che per il ANY
costrutto.
Uno di questi, prendendo un set , è equivalente all'altro e expr IN (<set>)
porta anche allo stesso piano di query expr = ANY(<set>)
che può utilizzare un indice semplice. Dettagli:
Di conseguenza, le seguenti due query sono equivalenti ed entrambe possono utilizzare l'indice semplice t_a_b_idx
(che può anche essere la soluzione se si sta tentando di ottenere la query per utilizzare l'indice):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
O:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
Identico per entrambi:
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.33..16.71 rows=1 width=8) (actual time=0.101..0.101 rows=0 loops=1)
-> Unique (cost=0.04..0.05 rows=2 width=8) (actual time=0.068..0.070 rows=2 loops=1)
-> Sort (cost=0.04..0.04 rows=2 width=8) (actual time=0.067..0.068 rows=2 loops=1)
Sort Key: "*VALUES*".column1, "*VALUES*".column2
Sort Method: quicksort Memory: 25kB
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=8) (actual time=0.005..0.005 rows=2 loops=1)
-> Index Only Scan using t_plain_idx on t (cost=0.29..8.32 rows=1 width=8) (actual time=0.009..0.009 rows=0 loops=2)
Index Cond: ((a = "*VALUES*".column1) AND (b = "*VALUES*".column2))
Heap Fetches: 0
Planning time: 4.080 ms
Execution time: 0.202 ms
Tuttavia , questo non può essere facilmente trasferito a una funzione, poiché in Postgres non ci sono "variabili di tabella". Il che porta al problema che ha iniziato questo argomento:
Esistono varie soluzioni alternative per questo problema. Una è la risposta alternativa che ho aggiunto lì. Alcuni altri:
La seconda forma di ciascuna è diversa: ANY
accetta un array reale , mentre IN
prende una virgola separata elenco di valori.
Ciò ha conseguenze diverse per la digitazione dell'input. Come possiamo vedere EXPLAIN
nell'output della domanda, questo modulo:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
è visto come una scorciatoia per:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
E vengono confrontati i valori ROW effettivi. Postgres non è attualmente abbastanza intelligente da vedere che l'indice sul tipo composito t_row_idx
è applicabile. Né si rende conto che t_a_b_idx
dovrebbe essere applicabile anche l'indice semplice .
Un cast esplicito aiuta a superare questa mancanza di intelligenza:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
Il cast dell'operando giusto ( ::int_pair[]
) è facoltativo (sebbene preferibile per le prestazioni e per evitare ambiguità). Una volta che l'operando di sinistra ha un tipo noto, l'operando di destra viene forzato da "record anonimo" a un tipo corrispondente. Solo allora l'operatore viene definito in modo inequivocabile. E Postgres seleziona gli indici applicabili in base all'operatore e all'operando di sinistra . Per molti operatori che definiscono aCOMMUTATOR
, il planner di query può capovolgere gli operandi per portare l'espressione indicizzata a sinistra. Ma questo non è possibile con il ANY
costrutto.
Relazionato:
.. i valori sono presi come elementi e Postgres è in grado di confrontare i singoli valori interi come possiamo vedere EXPLAIN
ancora una volta nell'output:
Filter: ((b = 1) OR (b = 2))
Quindi Postgres scopre che è t_a_b_idx
possibile utilizzare l' indice semplice .
Di conseguenza, ci sarebbe un'altra soluzione per il caso particolare nell'esempio : poiché il tipo composito personalizzato int_pair
nell'esempio sembra essere equivalente al tipo di riga della tabella t
stessa, potremmo semplificare:
CREATE INDEX t_row_idx2 ON t ((t));
Quindi questa query userebbe l'indice senza altri casting espliciti:
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE t = ANY(ARRAY[(1,1),(1,2)]);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on t (cost=40.59..496.08 rows=1000 width=8) (actual time=0.19
1..0.191 rows=0 loops=1)
Recheck Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
-> Bitmap Index Scan on t_row_idx2 (cost=0.00..40.34 rows=1000 width=0) (actual time=0.188..0.188 rows=0 loops=1)
Index Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Planning time: 2.575 ms
Execution time: 0.267 ms
Ma i casi d'uso tipici non saranno in grado di utilizzare il tipo implicitamente esistente della riga della tabella.