【问题标题】:dynamically adding columns from LEFT JOIN从 LEFT JOIN 动态添加列
【发布时间】:2016-02-04 14:24:50
【问题描述】:

我有以下表格:

用户:

+---------------------------+
| id    | e-mail            |
+---------------------------+
| 1     | john@domain.com   |
+---------------------------+
| 2     | jane@domain.com   |
+---------------------------+
| 3     | mark@domain.com   |
+---------------------------+

书籍:

+-------------------------------+
| id    | user_id   | value     |
+-------------------------------+
| 1     | 1         | ABC       |
+-------------------------------+
| 2     | 1         | HIJ       |
+-------------------------------+
| 3     | 2         | XYZ       |
+-------------------------------+

我想构建一个查询以获得以下结果:

+-------------------------------------------------------+
| user_id   | e-mail            | value_1   | value_2   |
+-------------------------------------------------------+
| 1         | john@domain.com   | ABC       | HIJ       |
+-------------------------------------------------------+
| 2         | jane@domain.com   | XYZ       | NULL      |
+-------------------------------------------------------+
| 3         | mark@domain.com   | NULL      | NULL      |
+-------------------------------------------------------+

因此,结果列是根据最大书籍数量动态构建的。如果 john@domain.com 有第三本书,那么结果表应该包含 3 个“值”列,对于 jane@domain.com 和 mark@domain.com,该列将为 NULL。 这样的事情在 MySQL 中是可能的吗?

【问题讨论】:

  • 看起来很经典PIVOT
  • 直接不可能(在存储过程中沉迷于动态 SQL)。如果您准备达到该长度(我建议您尝试将数据的表示放入数据库调用中),那么一个查询可以返回每个用户 ID 的最大值数,然后使用它来动态为旋转的结果集构建查询。

标签: mysql dynamic left-join multiple-columns


【解决方案1】:

哦,好吧,把这当成一个挑战。

以下是一个存储过程,它应该动态构建所需的 SQL 并执行它,返回结果集。似乎适用于我对您的表格的测试副本(假设您的电子邮件列名是 e_mail)。

DELIMITER ;;
CREATE DEFINER=CURRENT_USER PROCEDURE dynamic_table()
BEGIN
    DECLARE max_count_var INT;
    DECLARE v_counter INT UNSIGNED DEFAULT 1;
    DECLARE base_sql TEXT;

    SELECT COUNT(*) AS max_count FROM Books GROUP BY user_id ORDER BY max_count DESC LIMIT 1 INTO max_count_var;

    SET base_sql = 'SELECT a.id AS user_id, a.e_mail ';

    WHILE v_counter <= max_count_var DO
        SET base_sql = CONCAT(base_sql, ',MAX(IF(cnt=', v_counter, ', `value`, NULL)) AS value_', v_counter);
        SET v_counter = v_counter + 1;
    END WHILE;

    SET base_sql = CONCAT(@s, ' FROM Users a LEFT OUTER JOIN ( SELECT id, user_id, value, @cnt:=IF(@user_id = user_id, @cnt + 1, 1) AS cnt, @user_id := user_id FROM Books CROSS JOIN (SELECT @cnt:=1, @user_id:=0) sub0 ORDER BY user_id, id ) b ON a.id = b.user_id GROUP BY a.id, a.e_mail ORDER BY a.id');

    SET @s = base_sql;

    PREPARE stmt FROM @s;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END;;
DELIMITER ;

【讨论】:

  • 这看起来非常棒,但是当我尝试运行它时会触发错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER' at line 1我使用的是 MySQL 5.6
  • 这只是根据您使用的开发环境输入存储过程所需的语法位(这有点痛苦!)。尝试删除 end 后面的双分号以及最后一行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
  • 2011-11-01
相关资源
最近更新 更多