Joh*_*n P 5 sql postgresql aggregate-functions left-join
给定以下 4 个表:
CREATE TABLE events ( id, name )
CREATE TABLE profiles ( id, event_id )
CREATE TABLE donations ( amount, profile_id )
CREATE TABLE event_members( id, event_id, user_id )
Run Code Online (Sandbox Code Playgroud)
我正在尝试获取所有事件的列表,以及任何成员的计数以及任何捐赠的总和。问题是捐赠的总和是错误的(似乎是捐赠的笛卡尔结果 * # of event_members)。
这是 SQL 查询 (Postgres)
SELECT events.name, COUNT(DISTINCT event_members.id), SUM(donations.amount)
FROM events
LEFT OUTER JOIN profiles ON events.id = profiles.event_id
LEFT OUTER JOIN donations ON donations.profile_id = profiles.id
LEFT OUTER JOIN event_members ON event_members.event_id = events.id
GROUP BY events.name
Run Code Online (Sandbox Code Playgroud)
sum(donations.amount) 返回 = 实际捐款总额 * event_members 中的行数。如果我注释掉 count(distinct event_members.id) 和 event_members 左外连接,则总和是正确的。
编辑:埃尔文指出我正确的方向。查询重写为:
SELECT events.name, COUNT(DISTINCT event_members.id),
从捐赠中选择(SUM(donations.amount),其中捐赠.profile_id =profiles.id和profiles.event_id = events.id)作为total_donations
FROM 事件
左外连接 event_members ON event_members.event_id = events.id
GROUP BY events.name
正如我在引用的问题下详细解释的那样,您需要先聚合,然后加入表以避免代理CROSS JOIN。像这样:
SELECT e.name, e.sum_donations, m.ct_members
FROM (
SELECT e.id, e.name, SUM(d.amount) AS sum_donations
FROM events e
LEFT JOIN profiles p ON p.event_id = e.id
LEFT JOIN donations d ON d.profile_id = p.id
GROUP BY 1, 2
) e
LEFT JOIN (
SELECT event_id, COUNT(DISTINCT id) AS ct_members
FROM event_members
GROUP BY 1
) m ON m.event_id = e.id
Run Code Online (Sandbox Code Playgroud)
IFevent_members.id是主键(正如人们可能假设的那样),您可以简化为
COUNT(*) AS ct_members
Run Code Online (Sandbox Code Playgroud)
因为id保证是UNIQUE NOT NULL. 这样快了一点。
你似乎有这两个独立的结构(-[意味着1-N关联):
events -[ profiles -[ donations
events -[ event members
Run Code Online (Sandbox Code Playgroud)
我将第二个包装到子查询中:
SELECT events.name,
member_count.the_member_count
COUNT(DISTINCT event_members.id),
SUM(donations.amount)
FROM events
LEFT OUTER JOIN profiles ON events.id = profiles.event_id
LEFT OUTER JOIN donations ON donations.profile_id = profiles.id
LEFT OUTER JOIN (
SELECT
event_id,
COUNT(*) AS the_member_count
FROM event_members
GROUP BY event_id
) AS member_count
ON member_count.event_id = events.id
GROUP BY events.name
Run Code Online (Sandbox Code Playgroud)