加入 2 个表并将重复的行值显示到新列中
Posted
技术标签:
【中文标题】加入 2 个表并将重复的行值显示到新列中【英文标题】:Join 2 tables and display duplicated row value into new column 【发布时间】:2021-09-07 00:41:40 【问题描述】:我已经花了 2 天的时间来解决这个问题,但找不到任何帮助。
我有 2 张桌子。
1.玩家
id | name |
---|---|
1 | AA |
2 | BB |
3 | CC |
4 | DD |
2。匹配
id | player_id | match_id | date | has_opponent |
---|---|---|---|---|
1 | 1 | 1 | 2021-06-23 | 0 |
2 | 2 | 1 | 2021-06-23 | 0 |
3 | 3 | 2 | 2021-06-21 | 0 |
4 | 4 | 3 | 2021-06-22 | 1 |
预期结果
match_id | date | first_opponent_name | second_opponent_name |
---|---|---|---|
1 | 2021-06-23 | AA | BB |
2 | 2021-06-21 | CC | |
3 | 2021-06-22 | DD | DD |
我想用新列加入这两个表( first_opponent_name 和 second_opponent_name ) 如果满足以下条件:
-
如果有一个重复的 match_id 但它有不同的 player_id,则在 2 个单独的列中显示他们的名字。
如果只有 1 个 match_id 并且它有 1 个 player_id 并且 has_opponent 列为 0,那么这应该只显示 first_opponent_name
如果只有 1 个 match_id 并且它有 1 个 player_id 并且 has_opponent 列为 1,那么 first_opponent_name 和 second_opponent strong> _name 应该显示为相同的值。
【问题讨论】:
到目前为止您尝试了哪些方法,您在哪里卡住了? 那些针对性的问题,听起来像是功课? 我正在尝试创建一个预订系统,先生! 【参考方案1】:未经测试,但我认为这会起作用,基本上它在不同的场景中将表连接到自身,然后检查它匹配的场景。
SELECT
m.match_id
,m.date
,p1.name AS first_opponent_name
,CASE WHEN p2.name IS NOT NULL THEN p2.name
WHEN nop2.match_id IS NOT NULL THEN ''
WHEN samep2.match_id IS NOT NULL THEN p1.name END AS second_opponent_name
FROM
matching AS m
INNER JOIN players AS p1
ON m.player_id = p1.player_id
LEFT JOIN matching AS hasp2
ON m.match_id = hasp2.match_id
AND m.player_id <> hasp2.player_id
LEFT JOIN players AS p2
ON hasp2.player_id = p2.player_id
LEFT JOIN (
SELECT
match_id
FROM
matching
WHERE
has_opponent = 0
GROUP BY
match_id
HAVING COUNT(*) = 1
) AS nop2
ON m.match_id = nop2.match_id
LEFT JOIN (
SELECT
match_id
FROM
matching
WHERE
has_opponent = 1
) AS samep2
ON m.match_id = samep2.match_id
可以说,您可以放弃 hasp2 或 samep2 连接,并假设如果不满足其他两种情况,则取决于您对数据的信任程度,以了解这是否是一个可行的选择。
【讨论】:
非常感谢您提供的解决方案,特别是提到数据的可信度。【参考方案2】:WITH cte AS ( SELECT DISTINCT
match_id,
`date`,
MIN(player_id) OVER (PARTITION BY match_id) p1,
MAX(player_id) OVER (PARTITION BY match_id) p2,
has_opponent
FROM matching )
SELECT match_id,
`date`,
p1.name first_opponent_name,
CASE WHEN has_opponent OR (p1 != p2) THEN p2.name END second_opponent_name
FROM cte
JOIN players p1 ON cte.p1 = p1.id
JOIN players p2 ON cte.p2 = p2.id
ORDER BY 1
如果不支持 CTE 和窗口函数,则
SELECT match_id,
`date`,
p1.name first_opponent_name,
CASE WHEN has_opponent OR (p1 != p2) THEN p2.name END second_opponent_name
FROM ( SELECT match_id,
`date`,
MIN(player_id) p1,
MAX(player_id) p2,
has_opponent
FROM matching
GROUP BY match_id,
`date`,
has_opponent ) cte
JOIN players p1 ON cte.p1 = p1.id
JOIN players p2 ON cte.p2 = p2.id
ORDER BY 1
https://dbfiddle.uk/?rdbms=mysql_8.0&rdbms2=mysql_5.7&fiddle=4b947a2e7d31d50dfc1242b194b204fe
查询假定相同的match_id
严格匹配相同的date
和match_id
。
【讨论】:
真的很感激,兄弟!【参考方案3】:唯一棘手的部分是处理多个玩家,这非常适合使用窗口函数和row_number()
;那么这只是将这些与条件案例聚合起来的案例:
select match_Id, date,
Max(case when cnt=1
then name
else
case when rn=1 then name end
end) first_opponent_name,
Max(case when cnt=1 then
case when has_opponent=1 then name else '' end
else
case when rn=2 then name end
end) second_opponent_name
from (
select m.match_id, m.date, m.has_opponent, p.name,
Row_Number() over(partition by match_id order by player_id)rn,
Count(*) over (partition by match_id) cnt
from matching m
join players p on p.id=m.player_id
)x
group by match_id, date
见working example Fiddle
【讨论】:
不错的解决方案,值得注意的是窗口函数仅在 MySQL 8+ 和 MariaDB 10.2+ 中使用 第一次知道有这样的功能。谢谢! 我只是想知道为什么我们必须在这里使用聚合函数 MAX() ?因为它只会返回一个值。 @samrith.v 因为查询正在聚合行 - 如果有多个玩家,则返回 2 行需要使用group by
折叠成 1 行 - case 表达式返回我们的值want 或 null
和 max
将这些聚合为 1 行,丢弃 null
非常感谢您的解释。以上是关于加入 2 个表并将重复的行值显示到新列中的主要内容,如果未能解决你的问题,请参考以下文章