以下是我在Ruby on Rails项目中使用的代码,用于查找residences
amenities
与ids
48,49和50的代码。它们通过连接与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}
。
答案 0 :(得分:2)
如果您的设计强制执行参照完整性,则您根本不必为此目的加入表residences
。还假设UNIQUE
上有PK
或(residence_id, amenity_id)
约束(否则您需要不同的查询!)
最佳查询取决于您所需要的完全。
使用窗口功能,可以甚至在单个查询级别执行此操作:
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数组(而不是上面的 set ),几乎没有任何费用:
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;
基本上这是一个关系分裂的案例。我们在这里组建了一系列技术: