从LEFT JOIN动态添加列

时间:2016-02-04 14:24:51

标签: mysql dynamic left-join multiple-columns

我有以下表格:

用户:

+---------------------------+
| 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" value"对于jane@domain.com和mark@domain.com,列和此列将为NULL。 MySQL中有这样的事情吗?

1 个答案:

答案 0 :(得分:0)

哦,好吧,把它作为挑战。

以下是一个存储过程,它应动态构建所需的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 ;