sql - 具有多个连接导致重复的 SQL
问题描述
我正在尝试使用多个左连接进行此查询,但为与项目 ID 关联的每个费用返回重复的更新和科学家(例如,如果有 5 次费用,则每个更新和科学家返回 5 次)。我试图避免使用多个选择语句,但一直遇到问题。
SELECT
projects.*,
coalesce(json_agg(updates ORDER BY update_date DESC) FILTER (WHERE updates.id IS NOT NULL), '[]') AS updates,
coalesce(json_agg(scientists) FILTER (WHERE scientists.user_id IS NOT NULL), '[]') AS scientists,
coalesce(SUM(charges.amount), 0) AS donated,
coalesce(COUNT(charges), 0) AS num_donations
FROM projects
LEFT JOIN updates
ON updates.project_id = projects.id
LEFT JOIN scientists
ON scientists.project_id = projects.id
LEFT JOIN charges
ON charges.project_id = projects.id
WHERE projects.id = '${id}'
GROUP BY projects.id;
预期结果(更改为仅返回 id):
id | updates | scientists | donated | num_donations
--------------------------------------+------------------------------------------+------------------------------------+---------+---------------
17191850-9a03-482f-9afe-7dc6b69974ea | ["0c29417f-0afb-44df-a8cf-24dc5cc7962c"] | ["auth0|5efcfb5f652e5a0019ce2193"] | 155 | 5
实际结果:
id | updates | scientists | donated | num_donations
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------+---------------
17191850-9a03-482f-9afe-7dc6b69974ea | ["0c29417f-0afb-44df-a8cf-24dc5cc7962c", "0c29417f-0afb-44df-a8cf-24dc5cc7962c", "0c29417f-0afb-44df-a8cf-24dc5cc7962c", "0c29417f-0afb-44df-a8cf-24dc5cc7962c", "0c29417f-0afb-44df-a8cf-24dc5cc7962c"] | ["auth0|5efcfb5f652e5a0019ce2193", "auth0|5efcfb5f652e5a0019ce2193", "auth0|5efcfb5f652e5a0019ce2193", "auth0|5efcfb5f652e5a0019ce2193", "auth0|5efcfb5f652e5a0019ce2193"] | 155 | 5
解决方案
如果你有这个:
SELECT p.column, s.column, u.column
FROM
p
JOIN s ON ...
JOIN u ON ...
它产生一排
p1, s1, u1
然后你加入另一个表:
SELECT p.column, s.column, u.column, c.column
FROM
p
JOIN s ON ...
JOIN u ON ...
JOIN c ON ...
它突然产生5行..
p1, s1, u1, c1
p1, s1, u1, c2
p1, s1, u1, c3
p1, s1, u1, c4
p1, s1, u1, c5
并且您希望它再次生成一行,但另一列的计数为 5:
p1, s1, u1, 5
然后您需要对重复数据进行分组并添加计数:
SELECT p.column, s.column, u.column, count(*)
FROM
p
JOIN s ON ...
JOIN u ON ...
JOIN c ON ...
GROUP BY p.column, s.column, u.column
您会注意到 GROUP BY 部分只是 SELECT 部分的精确重复,减去计数(聚合列)
数据库将根据 GROUP BY 中指定的键对数据进行分组。p1, s1, u1
是一个独特的组合,并与 5 个不同的c1 .. c5
值相关联。这种情况下的聚合不适用于 cX 数据(因为它是 count(*),但它可以——如果我们说:
SELECT p.column, s.column, u.column, min(c.column), max(c.column)
然后数据库将此数据集与包含所有 c 值的存储桶一起制作:
p1, s1, u1, [c1, c2, c3, c4, c5]
并将 MIN 和 MAX 函数分别应用于[c1, c2, c3, c4, c5]
铲斗拉动c1
和c5
在您的脑海中,习惯于将分组操作视为准备 group by 中列的唯一组合,再加上将所有这些其他数据项放在一个大的无序存储桶中,并且 MAX/MIN/AVG 等函数对存储桶内容进行操作并提取相关数据(可能来自任何行,自然 MIN 和 MAX 可能会来自不同的行)。分组失去了“这个输入行”的概念,因为它准备了一组新的行
在各种数据库中最典型的分组情况下,SELECT *
如果您正在分组,您将无法使用 - 您在 SELECT 中列出每一列,然后在 GROUP BY 中再次列出。这可能看起来是多余的(实际上某些数据库允许您跳过提供分组依据),但在高级场景中可以按您选择的不同事物进行分组,因此它仅在简单情况下是多余的
现在,希望您对上述所有内容感到失望。一些数据库的函数不仅是 MIN/MAX 等,而且会将存储桶中的所有结果连接起来。像这样的伪SQL:
SELECT p.column, s.column, u.column, STRING_JOIN(c.column, '|')
可以产生:
p1, s1, u1, c1|c2|c3|c4|c5
string_join 函数旨在使用指定为分隔符的管道字符连接存储桶中的所有内容。
但请记住,我们的原始数据是:
p1, s1, u1, c1
p1, s1, u1, c2
p1, s1, u1, c3
p1, s1, u1, c4
p1, s1, u1, c5
如果我们只对 p.column 进行 GROUP BY,那么数据库会将 p1 作为键和更多桶:
p1, [s1,s1,s1,s1,s1], [u1,u1,u1,u1,u1], [c1,c2,c3,c4,c5]
如果你要 STRING_JOIN 中的每一个,你最终会得到你所要求的:
SELECT p.column, STRING_JOIN(s.column, '|'), STRING_JOIN(u.column, '|'), STRING_JOIN(c.column, '|'),
p1, s1|s1|s1|s1|s1, u1|u1|u1|u1|u1, c1|c2|c3|c4|c5
数据库中没有任何人工智能会看起来并说“我会在加入之前从 s 和 u 存储桶中删除重复项”,也不应该有。正如我之前提到的,当数据进入存储桶进行聚合时,所有行和排序的概念都会丢失。如果您的数据是:
p1, x1, y1
p1, x2, y2
你分组/加入你可能最终得到
p1, x1|x2, y2|y1
看到 Y 字符串中元素的顺序与 x 相比是倒置的 - 不要依赖“集合中元素的顺序”来推断关于它们最初来自的行的任何信息
那么,您的查询发生了什么?好吧,您只按一列分组并聚合其他列,如上所示,因此您可以看到如何获得非分组列的重复。
如果您继续按所有列分组,那么您将拥有自己的科学家和更新。如果您非常希望将它们作为 JSON,那么(假设这确实是 postgres)您将拥有 to_json 和 row_to_json,它们将提供单个 json 值,但它并没有真正增加单个列还没有给您的东西。Postgres(如果这是 postgres)将允许您 GROUP BY * 让 json 工作:
SELECT p.column, row_to_json(s), row_to_json(u), count(*)
...
GROUP BY p.column, s.*, u.*
s.* 和 u.* 的存在将允许 row_to_json 调用生成描述 S 和 U 的单行 json,并且计数将计算 Cs
推荐阅读
- php - 如何在php中遍历对象数组
- powerbi - 多对多关系产生错误的总数
- javascript - 如何在 JavaScript 中访问 HTML 单选的值
- office-js - 获取页面方向和大小
- java - 为什么 chrome 不下载响应中包含的文件?
- angular - 处理 GET 请求的 URI 保留字符
- opencv-python - 在 OpenCV python 中共享同一个相机的两个单独的函数
- yugabyte-db - 如何在 YugabyteDB 的服务器级别更改时区
- security - 使文件可用于 IIS 中经过身份验证的 API 连接
- php - gcloud 应用程序部署失败,7.4 运行时“需要 composer-runtime-api ^2.0.0”