sum() 在具有多个连接的 MySQL 查询中无法正常工作(group by 无法按预期工作)
sum() does not work properly in MySQL query with multiple joins (group by does not work as expected)
我有 table 个订单、网络和用户,我需要获得 订单总数 和 订单总金额 对于每个用户,用户计数在每个用户拥有的同一网络中。
SQL摆弄示例数据: http://sqlfiddle.com/#!9/dcbeea/1
users.userid、orders.id - 唯一主键。
在此示例中,检查用户 #24 行:total_orders、total_revenue、[= 的值37=]network_users 不正确。
用户 #24 的当前结果: total_orders:6,total_revenue:350,network_users:6。
用户 #24 的预期结果: total_orders:3,total_revenue:175,network_users:2.
这是SQL请求:
SELECT u.*,
count(o.id) AS total_orders,
sum(o.total) AS total_revenue,
count(un.userid) as network_users /* Users count in same network */
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
LEFT JOIN users am ON u.ownerid = am.userid
LEFT JOIN users bdr ON u.bdrid = bdr.userid
LEFT JOIN networks n ON u.networkid = n.networkid
LEFT JOIN users un ON n.networkid=un.networkid
GROUP BY u.userid
ORDER BY u.userid DESC;
问题 1:
total_orders 和 total_revenue 这里 return 不正确的值(比它应该的多,看起来它加起来很少次因为网络 table 加入)。
我可以通过添加 distinct - count(distinct(o.id)) AS total_orders 来修复 total_orders,但这不起作用对于总和,因为我不能仅通过不同的 ID 来设置总和,因为我看到无法在 SQL.
中设置它
您可以在 SQLFiddle 示例中看到问题 - 用户 #24 应该有 total_revenue = 175,但是您看到它计算为 350。据我所知,这是因为两个不同的用户关联用户 #24 拥有的同一网络(网络 #1)。
问题 2:
count(un.userid) as network_users - 如果我不添加 count(disctinct(un.userid)) as [=,这将无法正常工作72=]。没有 'distinct' 这会显示我所看到的总体网络计数(而不是网络中与当前用户具有相同网络 ID 的总体用户数)。在 SQL 示例中,用户 ID #24 的 'network_users' 应该是 2(因为该网络中只有 2 个用户),但是我在结果中看到 6。
问题: 如何更改 SQL 请求以获得正确的预期数学结果?
一种方法:计算子select中的network_users
(SELECT子句中的子查询)
SELECT u.userid,
count(o.id) AS total_orders,
coalesce(sum(o.total), 0) AS total_revenue,
(
SELECT count(*)
FROM users un
WHERE un.networkid = u.networkid
) as network_users
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid, u.networkid
ORDER BY u.userid DESC;
结果:
| userid | total_orders | total_revenue | network_users |
| ------ | ------------ | ------------- | ------------- |
| 40 | 1 | 75 | 1 |
| 37 | 0 | 0 | 2 |
| 33 | 0 | 0 | 1 |
| 24 | 3 | 175 | 2 |
另一种方式:在派生的table中执行"preaggregation"(FROM子句中的子查询)
SELECT u.userid,
count(o.id) AS total_orders,
coalesce(sum(o.total), 0) AS total_revenue,
u.network_users
FROM (
SELECT u.userid, count(un.userid) as network_users
FROM users u
LEFT JOIN users un ON un.networkid = u.networkid
GROUP BY u.userid
) u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid
ORDER BY u.userid DESC;
结果:
| userid | network_users | total_orders | total_revenue |
| ------ | ------------- | ------------ | ------------- |
| 40 | 1 | 1 | 75 |
| 37 | 2 | 0 | 0 |
| 33 | 1 | 0 | 0 |
| 24 | 2 | 3 | 175 |
一些注意事项:
- 确保
userid
在 users
table 中是 UNIQUE 或 PRIMARY KEY。否则,从 MySQL 5.7. 开始可能会出现错误
- 我删除了
users am
和 users bdr
的 JOIN。您没有在查询中使用它们。如果你想 select 从他们那里得到任何东西,你可以把它们放回去。
- 我还删除了与
networks
table 的 JOIN。您可以使用 networkid
列将 users
table 与其自身连接起来。
- 将
COALESCE()
用于 SUM()
将 NULL
转换为 0
。
为什么您的查询未能return 预期结果?因为您正在加入来自同一网络的所有其他用户的用户订单。因此订单数和总金额乘以同一网络中的用户数。
这样看。在执行 JOIN
时,首先将来自 table 的行的所有组合放入一个大临时文件 table(在过滤掉任何不适用的内容之后)。
然后 聚合如 COUNT()
和 SUM()
是针对这个大 table.
计算的
通常这是不正确的。通常解决方法是 首先 设计一个查询,该查询使用从中间 table 获得正确总和所需的最少 table 数量。 然后再做JOINs
你可能需要的。
另一种方法(有时)是使用子查询来进行聚合或提供 JOINs
.
的等效项
有时查询会像这样:
SELECT ...
FROM ( SELECT key, COUNT(*), SUM(..) FROM .. GROUP BY .. ) AS a
JOIN b ON ...
JOIN c ON ...
为@Paul 的出色回答添加第三个选项
您可以分别进行计数,然后使用 UNION ALL
将它们放在一起,然后 SUM
这些行
SELECT agg.userid,
SUM(agg.order_count) AS total_orders,
SUM(agg.revenue_sum) AS total_revenue,
SUM(agg.network_user_count) AS network_users
FROM (
/** Orders and Revenue */
SELECT u.userid,
COUNT(o.id) AS order_count,
SUM(o.total) AS revenue_sum,
0 AS network_user_count
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid
UNION ALL
/** Network Users */
SELECT u.userid,
0 AS order_count,
0 AS revenue_sum,
COUNT(un.userid) AS network_user_count
FROM users u
LEFT JOIN users un
ON un.networkid = u.networkid
GROUP BY u.userid
) agg
GROUP BY agg.userid
ORDER BY agg.userid DESC;
内部查询 agg
将给出类似
的结果
| userid | order_count | revenue_sum | network_user_count |
| ------ | ----------- | ----------- | ------------------ |
| …
| 24 | 3 | 175 | 0 |
| 24 | 0 | 0 | 2 |
| …
然后外部查询会将这些行与 SUM
s
合并
有点啰嗦,不过我已经在我们的一些项目中使用了这个方法
我有 table 个订单、网络和用户,我需要获得 订单总数 和 订单总金额 对于每个用户,用户计数在每个用户拥有的同一网络中。
SQL摆弄示例数据: http://sqlfiddle.com/#!9/dcbeea/1
users.userid、orders.id - 唯一主键。
在此示例中,检查用户 #24 行:total_orders、total_revenue、[= 的值37=]network_users 不正确。
用户 #24 的当前结果: total_orders:6,total_revenue:350,network_users:6。
用户 #24 的预期结果: total_orders:3,total_revenue:175,network_users:2.
这是SQL请求:
SELECT u.*,
count(o.id) AS total_orders,
sum(o.total) AS total_revenue,
count(un.userid) as network_users /* Users count in same network */
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
LEFT JOIN users am ON u.ownerid = am.userid
LEFT JOIN users bdr ON u.bdrid = bdr.userid
LEFT JOIN networks n ON u.networkid = n.networkid
LEFT JOIN users un ON n.networkid=un.networkid
GROUP BY u.userid
ORDER BY u.userid DESC;
问题 1: total_orders 和 total_revenue 这里 return 不正确的值(比它应该的多,看起来它加起来很少次因为网络 table 加入)。
我可以通过添加 distinct - count(distinct(o.id)) AS total_orders 来修复 total_orders,但这不起作用对于总和,因为我不能仅通过不同的 ID 来设置总和,因为我看到无法在 SQL.
中设置它您可以在 SQLFiddle 示例中看到问题 - 用户 #24 应该有 total_revenue = 175,但是您看到它计算为 350。据我所知,这是因为两个不同的用户关联用户 #24 拥有的同一网络(网络 #1)。
问题 2: count(un.userid) as network_users - 如果我不添加 count(disctinct(un.userid)) as [=,这将无法正常工作72=]。没有 'distinct' 这会显示我所看到的总体网络计数(而不是网络中与当前用户具有相同网络 ID 的总体用户数)。在 SQL 示例中,用户 ID #24 的 'network_users' 应该是 2(因为该网络中只有 2 个用户),但是我在结果中看到 6。
问题: 如何更改 SQL 请求以获得正确的预期数学结果?
一种方法:计算子select中的network_users
(SELECT子句中的子查询)
SELECT u.userid,
count(o.id) AS total_orders,
coalesce(sum(o.total), 0) AS total_revenue,
(
SELECT count(*)
FROM users un
WHERE un.networkid = u.networkid
) as network_users
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid, u.networkid
ORDER BY u.userid DESC;
结果:
| userid | total_orders | total_revenue | network_users |
| ------ | ------------ | ------------- | ------------- |
| 40 | 1 | 75 | 1 |
| 37 | 0 | 0 | 2 |
| 33 | 0 | 0 | 1 |
| 24 | 3 | 175 | 2 |
另一种方式:在派生的table中执行"preaggregation"(FROM子句中的子查询)
SELECT u.userid,
count(o.id) AS total_orders,
coalesce(sum(o.total), 0) AS total_revenue,
u.network_users
FROM (
SELECT u.userid, count(un.userid) as network_users
FROM users u
LEFT JOIN users un ON un.networkid = u.networkid
GROUP BY u.userid
) u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid
ORDER BY u.userid DESC;
结果:
| userid | network_users | total_orders | total_revenue |
| ------ | ------------- | ------------ | ------------- |
| 40 | 1 | 1 | 75 |
| 37 | 2 | 0 | 0 |
| 33 | 1 | 0 | 0 |
| 24 | 2 | 3 | 175 |
一些注意事项:
- 确保
userid
在users
table 中是 UNIQUE 或 PRIMARY KEY。否则,从 MySQL 5.7. 开始可能会出现错误
- 我删除了
users am
和users bdr
的 JOIN。您没有在查询中使用它们。如果你想 select 从他们那里得到任何东西,你可以把它们放回去。 - 我还删除了与
networks
table 的 JOIN。您可以使用networkid
列将users
table 与其自身连接起来。 - 将
COALESCE()
用于SUM()
将NULL
转换为0
。
为什么您的查询未能return 预期结果?因为您正在加入来自同一网络的所有其他用户的用户订单。因此订单数和总金额乘以同一网络中的用户数。
这样看。在执行 JOIN
时,首先将来自 table 的行的所有组合放入一个大临时文件 table(在过滤掉任何不适用的内容之后)。
然后 聚合如 COUNT()
和 SUM()
是针对这个大 table.
通常这是不正确的。通常解决方法是 首先 设计一个查询,该查询使用从中间 table 获得正确总和所需的最少 table 数量。 然后再做JOINs
你可能需要的。
另一种方法(有时)是使用子查询来进行聚合或提供 JOINs
.
有时查询会像这样:
SELECT ...
FROM ( SELECT key, COUNT(*), SUM(..) FROM .. GROUP BY .. ) AS a
JOIN b ON ...
JOIN c ON ...
为@Paul 的出色回答添加第三个选项
您可以分别进行计数,然后使用 UNION ALL
将它们放在一起,然后 SUM
这些行
SELECT agg.userid,
SUM(agg.order_count) AS total_orders,
SUM(agg.revenue_sum) AS total_revenue,
SUM(agg.network_user_count) AS network_users
FROM (
/** Orders and Revenue */
SELECT u.userid,
COUNT(o.id) AS order_count,
SUM(o.total) AS revenue_sum,
0 AS network_user_count
FROM users u
LEFT JOIN orders o ON o.userid=u.userid
GROUP BY u.userid
UNION ALL
/** Network Users */
SELECT u.userid,
0 AS order_count,
0 AS revenue_sum,
COUNT(un.userid) AS network_user_count
FROM users u
LEFT JOIN users un
ON un.networkid = u.networkid
GROUP BY u.userid
) agg
GROUP BY agg.userid
ORDER BY agg.userid DESC;
内部查询 agg
将给出类似
| userid | order_count | revenue_sum | network_user_count |
| ------ | ----------- | ----------- | ------------------ |
| …
| 24 | 3 | 175 | 0 |
| 24 | 0 | 0 | 2 |
| …
然后外部查询会将这些行与 SUM
s
有点啰嗦,不过我已经在我们的一些项目中使用了这个方法