加入 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 ) 如果满足以下条件:

    如果有一个重复的 ma​​tch_id 但它有不同的 player_id,则在 2 个单独的列中显示他们的名字。 如果只有 1 个 ma​​tch_id 并且它有 1 个 player_id 并且 has_opponent 列为 0,那么这应该只显示 first_opponent_name 如果只有 1 个 ma​​tch_id 并且它有 1 个 player_id 并且 has_opponent 列为 1,那么 first_opponent_namesecond_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 严格匹配相同的datematch_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 或 nullmax 将这些聚合为 1 行,丢弃 null 非常感谢您的解释。

以上是关于加入 2 个表并将重复的行值显示到新列中的主要内容,如果未能解决你的问题,请参考以下文章

将函数应用于两列并将输出映射到新列[重复]

SQL:添加行值并显示在新列中

将数据框中的值列表附加到新列[重复]

Pandas - 如何将属性保存到数组中并将它们的值保存到新列中

根据相似的行值创建新列

计算前几行中大于当前行值的值