MySQL выберите данные с разделителями - PullRequest
1 голос
/ 14 марта 2019

Я унаследовал таблицу с информацией о некоторых группах людей, в которых одно поле содержит данные с разделителями, а результаты сопоставлены с другой таблицей.

id_group     Name
-----------------------
1            2|4|5
2            3|4|6
3            1|2

А в другой таблице у меня есть списоклюди, которые могут принадлежать к одной или нескольким группам

id_names     Names
-----------------------
1            Jack
2            Joe
3            Fred
4            Mary
5            Bill

Я хотел бы выполнить выборку для данных группы, в результате чего в одном поле будет содержаться список имен, разделенных запятой или пробелом, например, из первогоСтрока группы выше "Джо Фред Билл"

Я рассмотрел использование функции для разделения строки с разделителями, а также рассмотрел подзапросы, но конкатенация результатов подзапросов быстро становится огромной.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 14 марта 2019

Как следует из комментария Строберри выше, есть способ сделать это, но это так ужасно. Это как отделка вашей дорогой кухни переделывать с помощью клейкой ленты. Вы должны испытывать негодование по отношению к человеку, который разработал базу данных таким образом.

SELECT g.id_group, GROUP_CONCAT(n.Names SEPARATOR ' ') AS Names
FROM groups AS g JOIN names AS n
  ON FIND_IN_SET(n.id_names, REPLACE(g.Name, '|', ','))
GROUP BY g.id_group;

Вывод, протестировано на MySQL 5.6:

+----------+---------------+
| id_group | Names         |
+----------+---------------+
|        1 | Joe Mary Bill |
|        2 | Fred Mary     |
|        3 | Jack Joe      |
+----------+---------------+

Сложность этого запроса и тот факт, что он будет вынужден выполнять сканирование таблицы и не может быть оптимизирован, должны убедить вас в что не так с хранением списка идентификаторов в строке с разделителями .

Лучшее решение - создать третью таблицу, в которой вы сохраняете каждого отдельного члена группы в отдельности. То есть несколько строк на группу.

CREATE TABLE group_name (
  id_group INT NOT NULL,
  id_name INT NOT NULL,
  PRIMARY KEY (id_group, id_name)
);

Тогда вы можете сделать запрос более простым способом, и у вас есть возможность создавать индексы, чтобы сделать запрос очень быстрым.

SELECT id_group, GROUP_CONCAT(names SEPARATOR ' ') AS names
FROM groups
JOIN group_name USING (id_group)
JOIN names USING (id_name)
2 голосов
/ 14 марта 2019

Тень верна. Ваша основная проблема - плохой дизайн отношений в базе данных. Обычно такие бизнес-проблемы конструируются как так называемое отношение M: N (от M до N). Для этого вам нужно 3 таблицы:

  • первая таблица - groups, в которой есть поле GroupId с первичным ключом и читаемое поле name (например, 'group1' или что-то еще)

  • вторая таблица people, которая выглядит именно так, как вы показали выше. (не забудьте также включить первичный ключ в поле PeopleId здесь)

  • третья таблица - это таблица-мост, называемая GroupMemberships. У этого есть 2 поля GroupId и PeopleId. Эта таблица связывает первые два друг с другом и отмечает отношение M: N. В одной группе может быть от 1 до N участников, а люди могут быть членами от 1 до M групп.

Наконец, просто объедините таблицы в select и агрегируйте:

SELECT 
    g.Name,
    GROUP_CONCAT(p.Name ORDER BY p.PeopleId DESC SEPARATOR ';') AS Members
FROM
    Groups AS g
    INNER JOIN GroupMemberships AS gm ON g.GroupId = gm.GroupId
    INNER JOIN people AS p ON gm.PeopleId = p.PeopleId
GROUP BY g.Name;
0 голосов
/ 14 марта 2019
DROP TABLE IF EXISTS terrible;

CREATE TABLE terrible
(id_group SERIAL PRIMARY KEY
,Name VARCHAR(255) NOT NULL
);

INSERT INTO terrible VALUES
(1,'2|4|5'),
(2,'3|4|6'),
(3,'1|2');


DROP TABLE IF EXISTS names;

CREATE TABLE names
(id_names SERIAL PRIMARY KEY
,names VARCHAR(12) NOT NULL UNIQUE
);

INSERT INTO names VALUES
(1,'Jack'),
(2,'Joe'),
(3,'Fred'),
(4,'Mary'),
(5,'Bill');

SELECT GROUP_CONCAT(y.names ORDER BY FIND_IN_SET(y.id_names,REPLACE(x.name,'|',','))) n 
  FROM terrible x 
  JOIN names y 
    ON FIND_IN_SET(y.id_names,REPLACE(x.name,'|',',')) > 0 
 GROUP 
    BY id_group;
+---------------+
| n             |
+---------------+
| Joe,Mary,Bill |
| Fred,Mary     |
| Jack,Joe      |
+---------------+
3 rows in set (0.00 sec)
...