ruby-on-rails - query - ruby rails active record
Problema al recuperar registros con matriz vacĂa (5)
ActiveRecord (3.2.1 al menos) trata las matrices vacías como NULLs. Los marcadores de posición en una llamada where
son manejados por sanitize_sql
. Si replace_bind_variables
el código durante un tiempo, llegarás a replace_bind_variables
:
def replace_bind_variables(statement, values) #:nodoc:
raise_if_bind_arity_mismatch(statement, statement.count(''?''), values.size)
bound = values.dup
c = connection
statement.gsub(''?'') { quote_bound_value(bound.shift, c) }
end
y luego quote_bound_value
:
def quote_bound_value(value, c = connection) #:nodoc:
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
c.quote(nil)
else
value.map { |v| c.quote(v) }.join('','')
end
else
c.quote(value)
end
end
Una Matriz vacía satisfará las cuatro condiciones para que llegues a c.quote(nil)
y de ahí viene tu NULL. Toda la lógica especial que lleva a c.quote(nil)
indica que esto es un comportamiento intencional.
Diciendo IN (o NO EN) con una lista vacía:
where c in ()
debería producir un error de SQL, por lo que quizás las personas de AR intenten evitarlo convirtiendo silenciosamente ese SQL incorrecto en c in (null)
. Tenga en cuenta que ninguno de estos:
select ... from t where c in (null);
select ... from t where c not in (null);
debería producir algún resultado debido al comportamiento de SQL NULL. Este es un error newbie clásico y la gente de AR realmente debería saber mejor.
Yo preferiría una excepción: decirme que estoy a punto de desplegar una bala a pie sería mucho más amistoso que simplemente entregarme una pistola diferente.
Resumen ejecutivo :
- Esta conducta de "matriz vacía significa NULO" es intencional.
- Nunca se debe intentar
where(''c in (?)'', [])
Owhere(''c not in (?)'', [])
Ya que ninguna de las dos afirmaciones tiene mucho sentido. - Actualice su código de Ruby para comprobar las matrices vacías y haga lo que sea necesario para obtener los resultados que espera.
Tengo una tabla de alrededor de 100 usuarios y también tengo una serie de ID de usuario. Lo que quería hacer es mostrar todos los usuarios que no forman parte de este conjunto de ID de usuario. Cuando hago algo como esto
User.where(''id NOT IN (?)'', [9, 2, 3, 4])
Devuelve con éxito los registros donde la identificación del usuario no pertenece a esa matriz. Sin embargo, si esa matriz está vacía como tal
User.where(''id NOT IN (?)'', [])
No devuelve ningún usuario y la consulta SQL se ve así
SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
¿Alguien sabe por qué sucede esto o podría ser un error? Estoy usando Rails 3.2.5 con PostgreSQL.
En Rails 4 puede usar User.where.not(id: [])
que le dará el resultado correcto. Produce:
SELECT "users".* FROM "users" WHERE (1 = 1)
Lamentablemente, User.where(''id NOT IN (?)'', [])
Debería ser equivalente, pero no lo es. Todavía te da el resultado incorrecto:
SELECT "users".* FROM "users" WHERE (id NOT IN (NULL))
Referencias
No sé si este es el problema que se me pide, pero vine aquí para encontrar todos los registros con un atributo de matriz vacío (serializado). Lo resolví para Rails 5.0 de esta manera:
User.where(my_array_attribute: nil)
O para lo inverso:
User.where.not(my_array_attribute: nil)
Utilice el contenedor de registros activo de ruby:
User.where.not(id: [])
Esto maneja el problema de matriz vacía para usted.
User.where(''id NOT IN (?)'', ids+[0])