Traducir SQL a Active Record Query Interface en Ruby on Rails
Un amigo que estaba trasladando consultas de PHP a Ruby on Rails me pidió ayuda para contar ventas por cliente. La consulta parecía sencilla, pero el ejemplo original mezclaba un JOIN incompleto, sintaxis incorrecta y un GROUP BY ambiguo. Esta es una versión coherente de la operación.
Qué vamos a contar
Queremos los clientes que tienen ventas con un SKU no vacío y no nulo, junto con el número de esas ventas. Suponemos una relación Client has_many :sales y una columna sales.client_id.
SELECT clients.id, clients.name, clients.zipcode,
clients.state, clients.city,
COUNT(sales.id) AS number_of_sales
FROM clients
INNER JOIN sales ON sales.client_id = clients.id
WHERE sales.sku IS NOT NULL AND sales.sku <> ''
GROUP BY clients.id, clients.name, clients.zipcode,
clients.state, clients.city
ORDER BY clients.id ASC;
La misma intención en Active Record
Client.joins(:sales)
.where.not(sales: { sku: [nil, ''] })
.select('clients.id, clients.name, clients.zipcode, clients.state, clients.city, COUNT(sales.id) AS number_of_sales')
.group('clients.id, clients.name, clients.zipcode, clients.state, clients.city')
.order('clients.id ASC')
Los nombres están cualificados para evitar ambigüedades. La asociación ya establece la condición del JOIN: no necesitamos repetirla con un == de Ruby dentro de SQL.
Cuidado con el orden y los clientes sin ventas
He usado un orden determinista por cliente. Si quieres ordenar por última venta, define explícitamente un agregado como MAX(sales.created_at). Ordenar por una fecha cualquiera dentro de un grupo no expresa bien la operación.
Este INNER JOIN excluye clientes sin ventas válidas. Incluirlos con un contador cero exige un LEFT JOIN y colocar el filtro donde no elimine las filas sin coincidencia.
Consulta la guía oficial de consultas Active Record y verifica el SQL generado con to_sql en la versión de Rails de tu proyecto.
0 comentarios
Sé concreto, añade contexto (versión, distro, stack) y si puedes pega logs en bloque de código. Menos drama, más señales.