En Postgres, puede especificar una cláusula IN, como esta:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
¿Alguien sabe cuál es el número máximo de parámetros que puede pasar a IN?
En Postgres, puede especificar una cláusula IN, como esta:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
¿Alguien sabe cuál es el número máximo de parámetros que puede pasar a IN?
Respuestas:
Según el código fuente ubicado aquí, comenzando en la línea 850, PostgreSQL no limita explícitamente el número de argumentos.
El siguiente es un comentario de código de la línea 870:
/*
* We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only
* possible if the inputs are all scalars (no RowExprs) and there is a
* suitable array type available. If not, we fall back to a boolean
* condition tree with multiple copies of the lefthand expression.
* Also, any IN-list items that contain Vars are handled as separate
* boolean conditions, because that gives the planner more scope for
* optimization on such clauses.
*
* First step: transform all the inputs, and detect whether any are
* RowExprs or contain Vars.
*/
Esto no es realmente una respuesta a la presente pregunta, sin embargo, también podría ayudar a otros.
Al menos puedo decir que hay un límite técnico de 32767 valores (= Short.MAX_VALUE) pasables al backend PostgreSQL, utilizando el controlador JDBC 9.1 de Posgresql.
Esta es una prueba de "eliminar de x donde id en (... 100k valores ...)" con el controlador postgresql jdbc:
Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 100000
at org.postgresql.core.PGStream.SendInteger2(PGStream.java:201)
explain select * from test where id in (values (1), (2));
Seq Scan on test (cost=0.00..1.38 rows=2 width=208)
Filter: (id = ANY ('{1,2}'::bigint[]))
Pero si prueba la segunda consulta:
explain select * from test where id = any (values (1), (2));
Hash Semi Join (cost=0.05..1.45 rows=2 width=208)
Hash Cond: (test.id = "*VALUES*".column1)
-> Seq Scan on test (cost=0.00..1.30 rows=30 width=208)
-> Hash (cost=0.03..0.03 rows=2 width=4)
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=4)
Podemos ver que la tabla temporal de compilación de postgres y unirse con ella
No hay límite para la cantidad de elementos que está pasando a la cláusula IN. Si hay más elementos, lo considerará como una matriz y luego, para cada escaneo en la base de datos, verificará si está contenida en la matriz o no. Este enfoque no es tan escalable. En lugar de usar la cláusula IN, intente usar INNER JOIN con la tabla temporal. Consulte http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ para obtener más información. El uso de escalas INNER JOIN y el optimizador de consultas pueden hacer uso de hash join y otras optimizaciones. Mientras que con la cláusula IN no hay forma de que el optimizador optimice la consulta. He notado una aceleración de al menos 2x con este cambio.
OR
y IN
cláusulas debido a la gran sobrecarga en el análisis y la planificación de tales consultas, no pude confirmar el problema con Postgres 9.5, vea esta respuesta .
Como alguien más experimentado con Oracle DB, también estaba preocupado por este límite. IN
Realicé una prueba de rendimiento para una consulta con ~ 10'000 parámetros en una lista, obteniendo números primos hasta 100'000 de una tabla con los primeros 100'000 enteros al enumerar todos los números primos como parámetros de consulta .
Mis resultados indican que no debe preocuparse por sobrecargar el optimizador de planes de consulta u obtener planes sin uso de índice , ya que transformará la consulta para usarla = ANY({...}::integer[])
donde pueda aprovechar los índices como se esperaba:
-- prepare statement, runs instantaneous:
PREPARE hugeplan (integer, integer, integer, ...) AS
SELECT *
FROM primes
WHERE n IN ($1, $2, $3, ..., $9592);
-- fetch the prime numbers:
EXECUTE hugeplan(2, 3, 5, ..., 99991);
-- EXPLAIN ANALYZE output for the EXECUTE:
"Index Scan using n_idx on primes (cost=0.42..9750.77 rows=9592 width=5) (actual time=0.024..15.268 rows=9592 loops=1)"
" Index Cond: (n = ANY ('{2,3,5,7, (...)"
"Execution time: 16.063 ms"
-- setup, should you care:
CREATE TABLE public.primes
(
n integer NOT NULL,
prime boolean,
CONSTRAINT n_idx PRIMARY KEY (n)
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.primes
OWNER TO postgres;
INSERT INTO public.primes
SELECT generate_series(1,100000);
Sin embargo, esto (bastante viejo) hilo en la lista de correo pgsql-hackers indica que todavía hay un costo no despreciable en la planificación de tales consultas, así que tome mi palabra con un poco de sal.
Si tiene una consulta como:
SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)
puede aumentar el rendimiento si reescribe su consulta como:
SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)
EXPLAIN
dice que está reescribiendo internamente mi IN (...)
como ANY ('{...}'::integer[])
.
Es posible que desee considerar refactorizar esa consulta en lugar de agregar una lista arbitrariamente larga de identificadores ... Podría usar un rango si los identificadores siguen el patrón en su ejemplo:
SELECT * FROM user WHERE id >= minValue AND id <= maxValue;
Otra opción es agregar una selección interna:
SELECT *
FROM user
WHERE id IN (
SELECT userId
FROM ForumThreads ft
WHERE ft.id = X
);