MySQL: select, group by and transform rows to separate columns :)

本小妞迷上赌 提交于 2020-06-01 06:40:31

问题


I need to ask you for help with MySQL select query.
Specific example: employees with the spouse and kids.
I have 2 tables already joined into one and now I need to:
1, select the data with grouping them by 'emp' field
2, transform the result with these rules:

  • only one row with particular emp (emp-A, emp-B, emp-C)
  • each relative (spouse and kids) in succeeding columns (spouse first, kids next)

The table (in fact two joined tables):

+---------+-----------+-----------+------------+
| emp     | relation  | relative  | birthdate  |
+---------+-----------+-----------+------------+
| emp-A   | spouse    | spouse-A  | 1970-xx-xx |
| emp-A   | kid       | kid-A1    | 1971-xx-xx |
| emp-A   | kid       | kid-A2    | 1972-xx-xx |
| emp-A   | kid       | kid-A3    | 1973-xx-xx |
| emp-B   | spouse    | spouse-B  | 1980-xx-xx |
| emp-B   | kid       | kid-B1    | 1981-xx-xx |
| emp-B   | kid       | kid-B2    | 1982-xx-xx |
| emp-C   | kid       | kid-C1    | 1991-xx-xx |
| emp-C   | kid       | kid-C2    | 1992-xx-xx |
+---------+-----------+-----------+------------+

Desired result:

+---------+-----------+-------------+-----------+-------------+-----------+-------------+-----------+-------------+
| emp     | spouse    | birthdate   | kid1      | birthdate1  | kid2      | birthdate2  | kid3      | birthdate3  |
+---------+-----------+-------------+-----------+-------------+-----------+-------------+-----------+-------------+
| emp-A   | spouse-A  | 1970-xx-xx  | kid-A1    | 1971-xx-xx  | kid-A2    | 1972-xx-xx  | kid-A3    | 1973-xx-xx  |
| emp-B   | spouse-B  | 1980-xx-xx  | kid-B1    | 1981-xx-xx  | kid-B2    | 1982-xx-xx  |           |             |
| emp-C   |           |             | kid-C1    | 1991-xx-xx  | kid-C2    | 1992-xx-xx  |           |             |
+---------+-----------+-------------+-----------+-------------+-----------+-------------+-----------+-------------+


After several hours of unsuccessful searches, I gave up.
I'll be very grateful for some clues is it possible to achieve something like this (one select query would be the best option for me).
Thank you in advance.

Answering to emsoff, the source tables:
employees:

+----+---------+
| id | emp     |
+----|---------+
|  1 | emp-A   |
|  2 | emp-B   |
|  3 | emp-C   |
+----|---------+

relatives:

+----+---------+-----------+-----------+------------+
| id | emp_id  | relation  | relative  | birthdate  |
+----+---------+-----------+-----------+------------+
|  1 |       1 | spouse    | spouse-A  | 1970-xx-xx |
|  2 |       1 | kid       | kid-A1    | 1971-xx-xx |
|  3 |       1 | kid       | kid-A2    | 1972-xx-xx |
|  4 |       1 | kid       | kid-A3    | 1973-xx-xx |
|  5 |       2 | spouse    | spouse-B  | 1980-xx-xx |
|  6 |       2 | kid       | kid-B1    | 1981-xx-xx |
|  7 |       2 | kid       | kid-B2    | 1982-xx-xx |
|  8 |       3 | kid       | kid-C1    | 1991-xx-xx |
|  9 |       3 | kid       | kid-C2    | 1992-xx-xx |
+----|---------+-----------+-----------+------------+

Tables joined with employees.id=relatives.emp_id


回答1:


Your data are somewhat difficult to handle.

To maek a pivot table the columsn have to be uniquie, even more, that also five kis could apperar.

It is quite ugly and it works withmyslq 5.7

Schema (MySQL v5.7)

CREATE TABLE employees (
  `id` INTEGER,
  `emp` VARCHAR(5)
);

INSERT INTO employees
  (`id`, `emp`)
VALUES
  ('1', 'emp-A'),
  ('2', 'emp-B'),
  ('3', 'emp-C');

CREATE TABLE relatives (
  `id` INTEGER,
  `emp_id` INTEGER,
  `relation` VARCHAR(6),
  `relative` VARCHAR(8),
  `birthdate` DATE
);

INSERT INTO relatives
  (`id`, `emp_id`, `relation`, `relative`, `birthdate`)
VALUES
  ('1', '1', 'spouse', 'spouse-A', '1970-01-01'),
  ('2', '1', 'kid', 'kid-A1', '1971-01-02'),
  ('3', '1', 'kid', 'kid-A2', '1972-01-01'),
  ('4', '1', 'kid', 'kid-A3', '1973-01-01'),
  ('5', '2', 'spouse', 'spouse-B', '1980-02-01'),
  ('6', '2', 'kid', 'kid-B1', '1981-02-01'),
  ('7', '2', 'kid', 'kid-B2', '1982-02-01'),
  ('8', '3', 'kid', 'kid-C1', '1991-03-01'),
  ('9', '3', 'kid', 'kid-C2', '1992-03-01');

Query #1

SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(case when `relation` = "',
     `relation`,
      '" then `relation` end) AS `',
      relation, '`',
      ',MAX(case when `relation` = "',
     `relation`,
      '" then `birthdate` end) AS `',
      'birthdate_',relation, '`'      
    )
    ORDER BY rownumber
  ) INTO @sql
FROM
  (SELECT 
    `relation`
    ,rownumber
FROM 
    employees e
    INNER JOIN 
    (SELECT
    IF(`relation` = 'kid',IF (@empid = `emp_id`,@rn:= @rn+1,@rn:= 1),IF (@empid = `emp_id`,@rn:= @rn,@rn:= 0)) rownumber
     ,IF(`relation` = 'kid',CONCAT(`relation`,@rn),`relation`) relation
    , `relative`
    , `birthdate`
    ,@empid := `emp_id` emp_id
FROM
    (SELECT 
        * 
    FROM 
        relatives
    ORDER BY `emp_id`,FIELD(`relation`,"spouse","kid"),`birthdate`) rel
    ,(SELECT @empid := 0) a1
    ,(SELECT @rn := 0) a2) r ON e.id = r.emp_id) t1
    ;

SET @sql = CONCAT("SELECT MIN(`emp`), ", @sql, " 
                  FROM   (SELECT 
                            `emp_id`,
                            `emp` ,
                            `relation`
                            ,rownumber
                            , `relative`
                            , `birthdate`
                        FROM 
                            employees e
                            INNER JOIN 
                            (SELECT
                            IF(`relation` = 'kid',IF (@empid = `emp_id`,@rn:= @rn+1,@rn:= 1),IF (@empid = `emp_id`,@rn:= @rn,@rn:= 0)) rownumber
                             ,IF(`relation` = 'kid',CONCAT(`relation`,@rn),`relation`) 'relation'
                            , `relative`
                            , `birthdate`
                            ,@empid := `emp_id` 'emp_id'
                        FROM
                            (SELECT 
                                * 
                            FROM 
                                relatives
                            ORDER BY `emp_id`,FIELD(`relation`,'spouse','kid'),`birthdate`) rel
                            ,(SELECT @empid := 0) a1
                            ,(SELECT @rn := 0) a2) r ON e.id = r.emp_id) t1 
                   GROUP BY `emp_id`");
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

| MIN(`emp`) | spouse | birthdate_spouse | kid1 | birthdate_kid1 | kid2 | birthdate_kid2 | kid3 | birthdate_kid3 |
| ---------- | ------ | ---------------- | ---- | -------------- | ---- | -------------- | ---- | -------------- |
| emp-A      | spouse | 1970-01-01       | kid1 | 1971-01-02     | kid2 | 1972-01-01     | kid3 | 1973-01-01     |
| emp-B      | spouse | 1980-02-01       | kid1 | 1981-02-01     | kid2 | 1982-02-01     |      |                |
| emp-C      |        |                  | kid1 | 1991-03-01     | kid2 | 1992-03-01     |      |                |

View on DB Fiddle



来源:https://stackoverflow.com/questions/61413329/mysql-select-group-by-and-transform-rows-to-separate-columns

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!