Разделите строку MYSQL из GROUP_CONCAT на (массив, например, выражение, список), который IN() может понять
Этот вопрос следует из MYSQL join results set wiped results во время IN() в where where?
Итак, короткая версия вопроса. Как преобразовать строку, возвращаемую GROUP_CONCAT, в список разделенных запятыми выражений, который IN() будет рассматривать как список нескольких элементов для циклического перебора?
N.B. Документы MySQL, по-видимому, ссылаются на "(запятая, разделенная, списки)", используемая IN() как "списки выражений", и интересно, что страницы на IN() кажутся более или менее единственными страницами в документах MySQL когда-либо ссылаются на списки выражений. Поэтому я не уверен, что здесь будут использоваться функции, предназначенные для создания массивов или временных таблиц.
Длинная версия вопроса на основе примера: из двухстрочной базы данных:
SELECT id, name, GROUP_CONCAT(tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id GROUP BY person.id;
+----+------+----------------------+
| id | name | GROUP_CONCAT(tag_id) |
+----+------+----------------------+
| 1 | Bob | 1,2 |
| 2 | Jill | 2,3 |
+----+------+----------------------+
Как я могу повернуть это, который, поскольку он использует строку, рассматривается как логический эквивалент (1 = X) AND (2 = X)...
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id
GROUP BY person.id HAVING ( ( 1 IN (GROUP_CONCAT(tag.tag_id) ) ) AND ( 2 IN (GROUP_CONCAT(tag.tag_id) ) ) );
Empty set (0.01 sec)
... во что-то, где результат GROUP_CONCAT рассматривается как список, так что для Боб он будет эквивалентен:
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 1
GROUP BY person.id HAVING ( ( 1 IN (1,2) ) AND ( 2 IN (1,2) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Bob | 1,2 |
+------+--------------------------+
1 row in set (0.00 sec)
... и для Джилл это эквивалентно:
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 2
GROUP BY person.id HAVING ( ( 1 IN (2,3) ) AND ( 2 IN (2,3) ) );
Empty set (0.00 sec)
... так что общий результат будет эксклюзивным предложением поиска, требующим всех перечисленных тегов, которые не используют HAVING COUNT (DISTINCT...)?
(примечание: эта логика работает без И, применяя к первому символу строки, например
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id
GROUP BY person.id HAVING ( ( 2 IN (GROUP_CONCAT(tag.tag_id) ) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Jill | 2,3 |
+------+--------------------------+
1 row in set (0.00 sec)
Ответы
Ответ 1
Вместо использования IN()
, будет ли использоваться FIND_IN_SET()
?
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
Вот полный пример, основанный на примере проблемы в вопросе, подтвержденный как проверенный спрашивающим в более раннем редактировании вопроса:
SELECT name FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY person.id
HAVING ( FIND_IN_SET(1, GROUP_CONCAT(tag.tag_id)) ) AND ( FIND_IN_SET(2, GROUP_CONCAT(tag.tag_id)) );
+------+
| name |
+------+
| Bob |
+------+
Ответ 2
Вы можете передать строку как массив, используя разделитель разделения и взорвать его в функции, которая будет работать с результатами.
Для тривиального примера, если у вас есть такой массив строк: "one | two | tree | four | five" и хотите знать, находятся ли два в массиве, вы можете сделать так:
create function str_in_array( split_index varchar(10), arr_str varchar(200), compares varchar(20) )
returns boolean
begin
declare resp boolean default 0;
declare arr_data varchar(20);
-- While the string is not empty
while( length( arr_str ) > 0 ) do
-- if the split index is in the string
if( locate( split_index, arr_str ) ) then
-- get the last data in the string
set arr_data = ( select substring_index(arr_str, split_index, -1) );
-- remove the last data in the string
set arr_str = ( select
replace(arr_str,
concat(split_index,
substring_index(arr_str, split_index, -1)
)
,'')
);
-- if the split index is not in the string
else
-- get the unique data in the string
set arr_data = arr_str;
-- empties the string
set arr_str = '';
end if;
-- in this trivial example, it returns if a string is in the array
if arr_data = compares then
set resp = 1;
end if;
end while;
return resp;
end
|
delimiter ;
Я хочу создать набор полезных функций mysql для работы с этим методом. Любой желающий, пожалуйста, свяжитесь со мной.
Для получения дополнительных примеров посетите http://blog.idealmind.com.br/mysql/how-to-use-string-as-array-in-mysql-and-work-with/