【问题标题】:Count with subquery, group_by and Left join使用子查询、group_by 和左连接进行计数
【发布时间】:2016-04-25 16:08:09
【问题描述】:

我一直在为这个特定的查询而苦苦挣扎。我正在为在其指南中添加或接收最多地点的用户制作“排行榜”。

我的数据模型如下:

Users

uuid      id
string    first_name
bool      blogger

Guests

uuid      id

Tips (join model)

uuid     id
uuid     guide_id
uuid     place_id
integer  status
uuid     owner_id
string   owner_type

Guides

uuid     id 
uuid     user_id
string   name

Place
uuid     id
string   name

用户可以将地点(通过提示)添加到他/她的指南中。该同一用户还可以从其他用户那里接收到他/她的向导的位置(提示)。这些提示可以接受tips.status = 1

我想要的是以下内容:

在所有指南中添加或接受提示的所有地点的名字和计数,但不包括他们提供给按users.blogger = 1 分组的其他用户的提示。

示例:

Guest = true

you       40
user1     30
user2     25

Guest = false
user3     20
user4     15
user5      5

这是我目前所拥有的:

SELECT tips.owner_id, tips.owner_type, count(tips.owner_id) AS places_count
FROM "tips" 
LEFT JOIN users on (owner_type ='User' AND users.id = owner_id) 
GROUP BY "tips"."owner_id", "tips"."owner_type" 
ORDER BY places_count DESC 
LIMIT 16

此查询确实返回计数,但不考虑收到的提示,它还计算给其他用户的提示。我有一种预感,我需要使用子查询,首先选择给定用户的所有指南 ID,然后“简单地”选择 guide_id = selected_guide_idstips.status = 1 的所有提示的计数。最后按users.blogger = 1对结果进行分组

但是我该怎么写呢?

编辑 1:

我已经用一个额外的Guest 表更新了我的原始问题(这就是我使用table_idowner_type and owner_id instead 的原因。我已经用我想要分组的博客(bool)更新了用户表结果。

样本数据:

Users

id      first_name      blogger
user1   Daniel          true
user2   Quassnoi        false
user3   vkp             true

Guests

id
guest_1
guest_2

Guides

id          user_id     name
guide_1     user_1      Bugers
guide_2     user_1      Cool places
guide_3     user_2      Amsterdam

Tips

id      guide_id    place_id    status  owner_id    owner_type
tip1    guide_1     place_1     1       user_1      User        # user_1 added place_1 to his own guide guide_1 (accepted)
tip2    guide_1     place_2     1       guest_1     Guest       # guest_1 suggested place_2 to user_1's guide guide_1 (accepted)
tip2    guide_1     place_2     0       guest_1     Guest       # guest_1 suggested place_2 to user_1's guide guide_1 (rejected)
tip_3   guide_2     place_1     1       user_2      User        # user_2 added place_1 to his own guide guide_3 (accepted)
tip_4   guide_2     place_2     1       user_2      User        # user_2 added place_2 to his own guide guide_3 (accepted)
tip_5   guide_2     place_3     1       user_1      User        # user_1 added place_3 to user_2's guide guide_2 (accepted)

Places

id      name
place1  burgerbar
place2  burgermeester
place_3 bbq shack

我想要的结果是:

请注意,给其他用户的小费不计入小费提供者。

first_name  tips_count  blogger

Quassnoi    3           false (2 added by himself, 1 received from user_1)
Daniel      2           true (1 added by himself, 1 received from guest1. Note that the rejected tip does not count)
vkp         0           false

编辑 2

我已经稍微改变了 Quassnoi 的回答:

SELECT  *
FROM    users u
LEFT JOIN
    (
    SELECT  g.user_id, COUNT(*) tips_count
    FROM    guides g
    JOIN    tips t
    ON      t.guide_id = g.id
    AND (t.owner_id = g.user_id AND t.status = 1)
    GROUP BY g.user_id
    ) g
ON      g.user_id = u.id
ORDER BY tips_count DESC

然而,这会返回所有tips_count 首先为NULL 的记录。我希望那些为 0 而不是 NULL。如何将 NULL Tips_count 转换为 0?

编辑 3:

我已更新查询,使其仅计算 guide_id 等于给定用户的指南 ID 的提示。

SELECT  *
FROM users u
LEFT JOIN
    (
        SELECT  g.user_id, COUNT(*) tips_count
        FROM    guides g
        JOIN    tips t
        ON      t.guide_id = g.id
        AND     (t.guide_id = g.id AND t.status = 1)
        GROUP BY g.user_id
    ) g
ON g.user_id = u.id
ORDER BY tips_count DESC

【问题讨论】:

  • 样本数据和预期结果将有助于回答问题。
  • 我已将您的问题阅读了 3 遍,如果不查看数据,我将无能为力。
  • 如何判断指南属于用户?
  • 你们说得对,我忘了在指南上添加 user_id。我将添加一些示例数据和结果数据!

标签: sql postgresql count subquery


【解决方案1】:

似乎谁添加/推荐了一个地方并不重要。一旦它被接受(状态 1),它就属于指南,因此也属于指南的用户。因此:

select u.first_name, u.blogger, count(t.id)
from users u
left join guides g on g.user_id = u.id
left join tips t on t.guide_id = g.id and t.status = 1
group by u.id
order by count(t.id) desc;

【讨论】:

  • 你是对的!这就像一个魅力(状态不明确,所以我添加了t.status = 1)。谢谢!!
  • 啊,是的,我只是忘记了状态限定符。很高兴这对你有用。
【解决方案2】:

您的架构现在设置的方式,无法分辨哪个指南属于哪个用户。

假设您忘记提及(或忘记添加)guide.owner,它将是:

SELECT  *
FROM    user u
LEFT JOIN
        (
        SELECT  g.owner, COUNT(*) cnt
                guide g
        JOIN    tips t
        ON      t.guide_id = g.id
                AND (t.owner_id = g.owner OR t.status = 1)
        GROUP BY
                g.owner
        ) g
ON      g.owner = u.id

【讨论】:

  • 我确实忘记在指南上添加 user_id。谢谢你的回答,我马上去测试!我将在我的问题中添加一些测试数据和结果数据。
  • From 在这里可能有用
  • 我确实遇到了语法错误。我稍微改变了你的答案并添加了SORT BY tips_count DESC,但是这会返回很多 NULL Tips_count,我希望 tips_count 为 0 而不是 NULL 我已经相应地更新了我的问题。
  • 感谢您的时间和精力 Quassnoi
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 2010-12-29
相关资源
最近更新 更多