在 MySQL 中加入表的转置

Posted

技术标签:

【中文标题】在 MySQL 中加入表的转置【英文标题】:Join with a transpose of a table in MySQL 【发布时间】:2020-09-23 18:15:21 【问题描述】:

标题不太公平,但这是我的表结构

    users table
    +---------+
    | user_id |
    +---------+
    |       1 |
    |       2 |
    |       3 |
    +---------+
kids table

+----------+---------+------------+
| child_id | user_id | child_name |
+----------+---------+------------+
|        1 |       1 | child_a    |
|        2 |       1 | child_b    |
|        3 |       2 | child_c    |
|        4 |       3 | child_x    |
|        5 |       3 | child_y    |
|        6 |       3 | child_z    |
+----------+---------+------------+

现在我想转置kids 表并将其与users 表连接。

预期输出。

+---------+---------+---------+---------+
| user_id | child_1 | child_2 | child_3 |
+---------+---------+---------+---------+
|       1 | child_a | child_b |         |
|       2 | child_c |         |         |
|       3 | child_x | child_y | child_z |
+---------+---------+---------+---------+

如您所见,有 3 列子信息,因为用户的最大子数为 3。

到目前为止,我能做的如下,但我将不得不使用其他一些语言来重新格式化这些数据。

select user_id, group_concat(child_name) from kids group by user_id;

知道我可以在 mysql 中获取预期结果吗?

编辑:我知道每个用户最多有 3 个孩子。

【问题讨论】:

主要是您不知道需要创建多少列来容纳所有子项。而不是这个使用 group_concatGROUP BY 子句。 如您所见,我的问题完全一样。 请看meta.***.com/questions/333952/… 【参考方案1】:

您的意思是旋转,不需要users 表,可以尝试以下方法,其中包括GROUP_CONCAT() 函数,通过使用诸如ROW_NUMBER() 之类的解析函数来动态执行:

SET @sql = NULL;

SELECT GROUP_CONCAT(
             DISTINCT
             CONCAT(
                    'MAX(CASE WHEN rn =', rn,
                    ' THEN child_name END) AS child_',rn
                    )
       )
  INTO @sql
  FROM 
  (
   SELECT *,
          ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY child_id) AS rn
     FROM kids                    
  ) k;

SET @sql = CONCAT('SELECT user_id,',@sql,
                   ' FROM 
                     (
                      SELECT *,
                             ROW_NUMBER() OVER 
                             (PARTITION BY user_id ORDER BY child_id) AS rn
                        FROM kids                    
                     ) k
                    GROUP BY user_id'); 

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Demo

【讨论】:

【参考方案2】:

@Shobi 我相信你可以通过多次加入kidsusers 来做到这一点,这应该会给你孩子的名字,就像这样:

SELECT `u`.`user_id`,
    `k`.`child_name` AS `child_1`,
    `k2`.`child_name` AS `child_2`,
    `k3`.`child_name` AS `child_3`
FROM `users` AS `u`
LEFT JOIN `kids` AS `k` ON `k`.`user_id` = `u`.`user_id`
LEFT JOIN `kids` AS `k2` ON `k2`.`user_id` = `u`.`user_id`
    AND `k2`.`child_id` <> `k`.`child_id`
LEFT JOIN `kids` AS `k3` ON `k3`.`user_id` = `u`.`user_id`
    AND `k3`.`child_id` <> `k2`.`child_id`
    AND `k3`.`child_id` <> `k`.`child_id`
GROUP BY `u`.`user_id`

Here is a working example on sqlfiddle.

注意:上述查询适用于 MySQL = 5.7 中获得相同的结果,您可以使用包含 ANY_VALUE() 的查询版本,它被列为适当的使用 here in the MySQL documentation.

SELECT `u`.`user_id`,
    ANY_VALUE(`k`.`child_name`) AS `child_1`,
    ANY_VALUE(`k2`.`child_name`) AS `child_2`,
    ANY_VALUE(`k3`.`child_name`) AS `child_3`
FROM `users` AS `u`
LEFT JOIN `kids` AS `k` ON `k`.`user_id` = `u`.`user_id`
LEFT JOIN `kids` AS `k2` ON `k2`.`user_id` = `u`.`user_id`
    AND `k2`.`child_id` <> `k`.`child_id`
LEFT JOIN `kids` AS `k3` ON `k3`.`user_id` = `u`.`user_id`
    AND `k3`.`child_id` <> `k2`.`child_id`
    AND `k3`.`child_id` <> `k`.`child_id`
GROUP BY `u`.`user_id`
ORDER BY `u`.`user_id`

Here is a working example of this other version of the query in db-fiddle.

【讨论】:

这在没有 group by 的情况下确实有效,我使用的是 mysql 8 以上是合适的解决方案的sql没有问题:-( @Strawberry 您能否详细说明为什么您说我的解决方案不起作用?我愿意接受我的查询不起作用的可能性,但是查看 OP 的预期结果集和我的查询返回的结果集,它们是相同的。此外,OP 表示我的查询做了他想要的。我只是对为什么我的查询返回 OP 请求的结果集感到困惑,但你说它没有。 在没有任何聚合函数的情况下,GROUP BY 子句永远不合适

以上是关于在 MySQL 中加入表的转置的主要内容,如果未能解决你的问题,请参考以下文章

matlab转置矩阵?

matlab中怎么求矩阵的转置

matlab中怎么求矩阵的转置

matlab中求矩阵的转置矩阵,是啥函数?

SPSS中的转置

c++矩阵的转置和快速转置