这是我在 Ruby on Rails 项目中使用的代码,用于查找具有 便利设施
且 ids
为 48、49 和 50 的residences
. 它们通过连接与一个 has_many 相连。
id_list = [48, 49, 50]
Residence.joins(:listed_amenities).
where(listed_amenities: {amenity_id: id_list}).
group('residences.id').
having("count(listed_amenities.*) = ?", id_list.size)
生成的 SQL:
SELECT "residences".*
FROM "residences"
INNER JOIN "listed_amenities" ON "listed_amenities"."residence_id" = "residences"."id"
WHERE "listed_amenities"."amenity_id" IN (48, 49, 50)
GROUP BY residences.id
HAVING count(listed_amenities.*) = 3
我对从该查询中得到的 residences
的数量感兴趣。有没有办法添加 count
或其他东西让数据库进行计算?我不想在 Ruby 中浪费计算能力。添加 .count
方法不起作用。结果为 {528747=>3, 529004=>3, 529058=>3}
。
最佳答案
如果您的设计强制执行参照完整性,则您根本不必为此目的加入表residences
。还假设 (residence_id, amenity_id)
上的 UNIQUE
或 PK
约束(否则您需要不同的查询!)
最佳查询取决于您确切的需求。
使用窗口函数,您可以甚至在单个查询级别执行此操作:
SELECT count(*) OVER () AS ct
FROM listed_amenities
WHERE amenity_id IN (48, 49, 50)
GROUP BY residence_id
HAVING count(*) = 3
LIMIT 1;
此窗口函数将总计数附加到每一行而不聚合行。考虑 SELECT
查询中的事件序列:
因此,您可以使用类似的查询来返回所有符合条件的 ID(甚至整行)并将计数附加到每一行(冗余):
SELECT residence_id, count(*) OVER () AS ct
FROM listed_amenities
WHERE amenity_id IN (48, 49, 50)
GROUP BY residence_id
HAVING count(*) = 3;
但最好使用子查询,这通常要便宜得多:
SELECT count(*) AS ct
FROM (
SELECT 1
FROM listed_amenities
WHERE amenity_id IN (48, 49, 50)
GROUP BY residence_id
HAVING count(*) = 3
) sub;
您可以同时返回一个 ID 数组(与上面的集合相反),几乎不会增加任何成本:
SELECT array_agg(residence_id ) AS ids, count(*) AS ct
FROM (
SELECT residence_id
FROM listed_amenities
WHERE amenity_id IN (48, 49, 50)
GROUP BY residence_id
HAVING count(*) = 3
) sub;
还有许多其他变体,您必须阐明预期结果。就像这个:
SELECT count(*) AS ct
FROM listed_amenities l1
JOIN listed_amenities l2 USING (residence_id)
JOIN listed_amenities l3 USING (residence_id)
WHERE l1.amenity_id = 48
AND l2.amenity_id = 49
AND l2.amenity_id = 50;
基本上这是关系除法的情况。我们在这里汇集了一系列技术:
关于sql - 获取 GROUP BY 后的行数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32704765/