使用has_many关系的计数来选择 - 使用?

时间:2013-07-09 12:33:11

标签: mysql sql ruby-on-rails

订单has_many交付

订单的列号为number_of_deliveries_paid_for

如何选择交货数量(COUNT(deliveries.id))小于number_of_deliveries_paid_for

的所有订单

目前,我正在做:

Order.joins('LEFT OUTER JOIN deliveries ON deliveries.order_id = orders.id')
     .select('orders.*, COUNT(deliveries.id) AS delivery_count')
     .select { |o| o if o.number_of_deliveries_paid_for > o.delivery_count }

但是这会返回一个数组,返回一个ActiveRecord :: Relation会很好,我想在sql中执行它会更有效。

更新 _ __ _ __ _ __ _ __ _ __

这似乎有效

Order.joins('LEFT OUTER JOIN deliveries ON deliveries.order_id = orders.id')
     .group('orders.id')
     .having('COUNT(deliveries.id) < orders.number_of_deliveries_paid_for')

我可以在其末尾链接其他范围等。但是如果我将.count添加到最后,我会收到错误

ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column
'orders.number_of_deliveries_paid_for' in 'having clause':
SELECT COUNT(*) AS count_all, orders.id AS orders_id FROM `orders`
LEFT OUTER JOIN deliveries ON deliveries.order_id = orders.id GROUP BY orders.id 
HAVING COUNT(deliveries.id) < orders.number_of_deliveries_paid_for

让我焦虑不安!

1 个答案:

答案 0 :(得分:1)

我不知道如何在Ruby中编写它,但从SQL的角度来看,我觉得您需要一个子查询来按订单汇总交付数量。

类似的东西(我直接键入SO - 请原谅错别字或愚蠢的语法错误):

SELECT orders.* FROM orders 
                LEFT OUTER JOIN (SELECT COUNT(*) AS C, deliveries.order_id AS ID
                                        FROM deliveries 
                                        GROUP BY deliveries.order_id) AS S
                ON orders.id = S.ID
                WHERE S.C < orders.number_of_deliveries_paid_for
                   OR (deliveries.id IS NULL AND orders.number_of_deliveries_paid_for > 0);

编辑:事实上,使用ORDER BY ... HAVING会更加简单:

SELECT *, count(*) AS C FROM orders 
                        LEFT OUTER JOIN deliveries 
                        ON deliveries.order_id = orders.id
       GROUP BY orders.id 
       HAVING C < orders.number_of_deliveries_paid_for
           OR (deliveries.id IS NULL AND orders.number_of_deliveries_paid_for > 0);

对于好奇,这是我的测试用例:

mysql> select * from orders;
+------+---------+-------------------------------+
| id   | name    | number_of_deliveries_paid_for |
+------+---------+-------------------------------+
|    1 | Order 1 |                             0 |
|    2 | Order 2 |                             2 |
|    3 | Order 3 |                             3 |
+------+---------+-------------------------------+
3 rows in set (0.00 sec)

mysql> select * from deliveries;
+----+----------+
| id | order_id |
+----+----------+
|  1 |        2 |
|  2 |        2 |
|  3 |        3 |
|  4 |        3 |
+----+----------+
4 rows in set (0.00 sec)

mysql> select * from orders JOIN deliveries ON deliveries.order_id = orders.id    -> ;
+------+---------+-------------------------------+----+----------+
| id   | name    | number_of_deliveries_paid_for | id | order_id |
+------+---------+-------------------------------+----+----------+
|    2 | Order 2 |                             2 |  1 |        2 |
|    2 | Order 2 |                             2 |  2 |        2 |
|    3 | Order 3 |                             3 |  3 |        3 |
|    3 | Order 3 |                             3 |  4 |        3 |
+------+---------+-------------------------------+----+----------+
4 rows in set (0.00 sec)

作为旁注,请参阅JOINLEFT JOIN

之间的区别
mysql> select *, COUNT(*) from orders LEFT JOIN deliveries ON deliveries.order_id = orders.id group by orders.id;
+------+---------+-------------------------------+------+----------+----------+
| id   | name    | number_of_deliveries_paid_for | id   | order_id | COUNT(*) |
+------+---------+-------------------------------+------+----------+----------+
|    1 | Order 1 |                             0 | NULL |     NULL |        1 |
|    2 | Order 2 |                             2 |    1 |        2 |        2 |
|    3 | Order 3 |                             3 |    3 |        3 |        2 |
+------+---------+-------------------------------+------+----------+----------+
3 rows in set (0.00 sec)

mysql> select *, COUNT(*) from orders JOIN deliveries ON deliveries.order_id = orders.id group by orders.id;
+------+---------+-------------------------------+----+----------+----------+
| id   | name    | number_of_deliveries_paid_for | id | order_id | COUNT(*) |
+------+---------+-------------------------------+----+----------+----------+
|    2 | Order 2 |                             2 |  1 |        2 |        2 |
|    3 | Order 3 |                             3 |  3 |        3 |        2 |
+------+---------+-------------------------------+----+----------+----------+
2 rows in set (0.01 sec)