Estoy usando Rails 5. Tengo el siguiente modelo ...
class Order < ApplicationRecord
...
has_many :line_items, :dependent => :destroy
El modelo LineItem tiene un atributo, "discount_applied". Me gustaría devolver todos los pedidos donde hay cero instancias de una línea de pedido cuyo campo "discount_applied" no sea nulo. ¿Cómo escribo un método de búsqueda de este tipo?
Respuestas:
No es eficiente, pero pensé que podría resolver su problema:
Actualización :
En lugar de encontrar pedidos en los que todas sus líneas de pedido no tienen descuento, podemos excluir todos los pedidos que tienen líneas de pedido con un descuento aplicado del resultado de salida. Esto se puede hacer con la subconsulta dentro de la cláusula where:
Puedes combinarlos en un único método de búsqueda:
Espero que esto ayude
fuente
En primer lugar, esto realmente depende de si desea utilizar un enfoque Arel puro o si está bien usar SQL. El primero es IMO solo aconsejable si tiene la intención de construir una biblioteca, pero innecesario si está creando una aplicación donde, en realidad, es muy poco probable que cambie su DBMS en el camino (y si lo hace, cambie un puñado de las consultas manuales probablemente serán el menor de sus problemas).
Suponiendo que usar SQL está bien, la solución más simple que debería funcionar en casi todas las bases de datos es esta:
Esto también debería funcionar prácticamente en todas partes (y tiene un poco más de Arel y menos SQL manual):
Dependiendo de su DBMS específico (más específicamente: su respectivo optimizador de consultas), uno u otro podría ser más eficiente.
Espero que ayude.
fuente
Un posible código
Aconsejo familiarizarse con la documentación de AR para los métodos de consulta.
Actualizar
Sin embargo, esto parece estar más interesado que yo inicialmente. Y más complicado, así que no podré darte un código de trabajo. Pero buscaría una solución usando
LineItem.group(order_id).having(discount_applied: nil)
, que debería darle una colección de line_items y luego usarla como subconsulta para encontrar órdenes relacionadas.fuente
Si desea todos los registros donde discount_applied es nulo, entonces:
Order.includes(:line_items).where.not(line_items: {discount_applied: nil})
(el uso incluye para evitar el problema n + 1) o
fuente
Aquí está la solución a su problema.
Primera consulta devolverá los identificadores de
Orders
al menos unaline_item
que tengadiscount_applied
. La segunda consulta devolverá todos losorders
casos donde haya cero instancias deline_item
tener eldiscount_applied
.fuente
Usaría la
NOT EXISTS
función de SQL, que al menos está disponible en MySQL y PostgreSQLDebe tener un aspecto como este
fuente
Si entendí correctamente, desea obtener todos los pedidos para los que ninguna línea de pedido (si la hubiera) tiene un descuento aplicado.
Una forma de obtener esas órdenes usando
ActiveRecord
sería la siguiente:Aquí hay una breve explicación de cómo funciona:
left_outer_joins
, suponiendo que no accederá a las líneas de pedido para cada pedido. También puedes usarleft_joins
, que es un alias.Order
instancia, agregue.eager_load(:line_items)
a la cadena que evitará hacer una consulta adicional para cada pedido (N + 1), es decir, hacerloorder.line_items.each
en una vista.distinct
es esencial para asegurarse de que los pedidos solo se incluyan una vez en el resultado.Actualizar
Mi solución anterior solo estaba comprobando eso
discount_applied IS NULL
para al menos una línea de pedido, no para todos. La siguiente consulta debería devolver los pedidos que necesita.Esto es lo que está pasando:
orders LEFT OUTER JOIN line_items
) para que se incluyan los pedidos sin ningún elemento asociado.Order
objeto, independientemente de cuántos elementos tenga (GROUP BY recipes.id
).HAVING (COUNT(line_items.discount_applied) = 0)
).Espero que eso ayude.
fuente
No puede hacer esto de manera eficiente con un clásico rails left_joins, pero sql left join fue construido para manejar estos casos
Una combinación interna simple devolverá todas las órdenes, unidas con todos los elementos de línea,
pero si no hay elementos de línea para esta orden, la orden se ignora (como un falso donde)
Con la combinación izquierda, si no se encontraron elementos de línea, sql la unirá a un entrada vacía para mantenerla
Así que nos fuimos unidos a los line_items que no queremos, y encontramos todos los pedidos unidos con un line_items vacío
Y evite todo el código con
where(id: pluck(:id))
ohaving("COUNT(*) = 0")
, el día esto matará su base de datosfuente