获取GROUP BY后的行数

时间:2015-09-21 21:21:55

标签: sql postgresql activerecord relational-division

以下是我在Ruby on Rails项目中使用的代码,用于查找residences amenitiesids 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}

1 个答案:

答案 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;

基本上这是一个关系分裂的案例。我们在这里组建了一系列技术:

相关问题