rails active sql ruby-on-rails-3 postgresql ruby-on-rails-4 activerecord

sql - active - ¿Cómo usar CUALQUIER lugar en lugar de IN en una cláusula WHERE con Rails?



active record rails (1)

Solía ​​tener una consulta como:

MyModel.where(id: ids)

Lo que genera una consulta sql como:

SELECT "my_models".* FROM "my_models" WHERE "my_models"."id" IN (1, 28, 7, 8, 12)

Ahora quiero cambiar esto para usar ANY lugar de IN . Creé esto:

MyModel.where("id = ANY(VALUES(#{ids.join ''),(''}))"

Ahora, cuando uso una matriz vacía ids = [] obtengo el siguiente error:

MyModel Load (53.0ms) SELECT "my_models".* FROM "my_models" WHERE (id = ANY(VALUES())) ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")" ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")" Position: 75: SELECT "social_messages".* FROM "social_messages" WHERE (id = ANY(VALUES())) from arjdbc/jdbc/RubyJdbcConnection.java:838:in `execute_query''


Hay dos variantes de expresiones IN :

Del mismo modo, dos variantes con ANY construcción:

Una subconsulta funciona para cualquier técnica, pero para la segunda forma de cada una, IN espera una lista de valores (como se define en el SQL estándar) mientras que = ANY espera una matriz .

¿Cuál usar?

ANY es una adición posterior más versátil, se puede combinar con cualquier operador binario que devuelva un valor booleano. IN quema a un caso especial de ANY . De hecho, su segunda forma se reescribe internamente:

IN se reescribe con = ANY
NOT IN se reescribe con <> ALL

Verifique el resultado de EXPLAIN para cualquier consulta para ver por usted mismo. Esto prueba dos cosas:

  • IN nunca puede ser más rápido que = ANY .
  • = ANY no va a ser sustancialmente más rápido.

La elección debe decidirse por lo que es más fácil de proporcionar : una lista de valores o una matriz (posiblemente como una matriz literal, un valor único).

Si los ID que va a pasar provienen de la base de datos de todos modos, es mucho más eficiente seleccionarlos directamente (subconsulta) o integrar la tabla de origen en la consulta con un JOIN (como comentó @mu ).

Para pasar una larga lista de valores de su cliente y obtener el mejor rendimiento , use una matriz, unnest() y únase, o proporciónela como expresión de tabla usando VALUES (como comentó @PinnyM ). Pero tenga en cuenta que un JOIN conserva posibles duplicados en el conjunto / conjunto proporcionado, mientras que IN o = ANY no lo hacen. Más:

En presencia de valores NULL, NOT IN es a menudo la elección incorrecta y NOT EXISTS sería correcto (y también más rápido):

Sintaxis para = ANY

Para la expresión de matriz, Postgres acepta:

Para evitar conversiones de tipo no válidas, puede emitir explícitamente:

ARRAY[1,2,3]::numeric[] ''{1,2,3}''::bigint[]

Relacionado:

O podría crear una función Postgres tomando un parámetro VARIADIC , que toma argumentos individuales y forma una matriz a partir de ellos:

¿Cómo pasar la matriz de Ruby?

Suponiendo que id sea integer :

MyModel.where(''id = ANY(ARRAY[?]::int[])'', ids.map { |i| i})

Pero solo estoy incursionando en Ruby. @mu proporciona instrucciones detalladas en esta respuesta relacionada: