Запрос MySQL находит значения в строке, разделенной запятыми


93

У меня есть поле COLORS (varchar(50))в моей таблице, SHIRTSкоторое содержит строку с разделителями-запятыми, например 1,2,5,12,15,. Каждое число представляет доступные цвета.

При выполнении запроса select * from shirts where colors like '%1%'на получение всех красных рубашек (цвет = 1) я также получаю рубашки серого (= 12) и оранжевого (= 15) цвета.

Как мне переписать запрос, чтобы он выбирал ТОЛЬКО цвет 1, а не все цвета, содержащие число 1?


6
Я полагаю, вы могли бы сделать это с помощью регулярного выражения, но гораздо лучшим решением было бы разбить цвета рубашки в отдельную таблицу (цвета) и использовать таблицу соединения (рубашка_colors), используя идентификаторы цвета / рубашки, чтобы связать их.
ceejayoz

Я не могу поверить с 6 ответами, ни один из них не упомянул тип данных MySQL SET ..
ColinM,

Ответы:


190

Классическим способом было бы добавить запятые слева и справа:

select * from shirts where CONCAT(',', colors, ',') like '%,1,%'

Но find_in_set тоже работает:

select * from shirts where find_in_set('1',colors) <> 0

Я пробовал find_in_set, но он возвращает тот же результат независимо от значения цвета, которое я ввожу ... Есть предложения?
bikey77

@ bikey77: Возможно, это проблема, в документации говорится: Эта функция не работает должным образом, если первый аргумент содержит символ запятой («,»).
Andomar

Моя ошибка, это была логическая ошибка из-за тех же фиктивных значений. Работает нормально. Благодарность!
bikey77

@Andomar Прежде, чем я нашел ваш ответ, я боролся с IN, но ваш работает как шарм ... Большое вам спасибо ..
PHP Mentor

3
Это влияет на производительность, поскольку Find_in_set не использует индекс
Камран Шахид



11

Это наверняка сработает, и я действительно попробовал:

lwdba@localhost (DB test) :: DROP TABLE IF EXISTS shirts;
Query OK, 0 rows affected (0.08 sec)

lwdba@localhost (DB test) :: CREATE TABLE shirts
    -> (<BR>
    -> id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -> ticketnumber INT,
    -> colors VARCHAR(30)
    -> );<BR>
Query OK, 0 rows affected (0.19 sec)

lwdba@localhost (DB test) :: INSERT INTO shirts (ticketnumber,colors) VALUES
    -> (32423,'1,2,5,12,15'),
    -> (32424,'1,5,12,15,30'),
    -> (32425,'2,5,11,15,28'),
    -> (32426,'1,2,7,12,15'),
    -> (32427,'2,4,8,12,15');
Query OK, 5 rows affected (0.06 sec)
Records: 5  Duplicates: 0  Warnings: 0

lwdba@localhost (DB test) :: SELECT * FROM shirts WHERE LOCATE(CONCAT(',', 1 ,','),CONCAT(',',colors,',')) > 0;
+----+--------------+--------------+
| id | ticketnumber | colors       |
+----+--------------+--------------+
|  1 |        32423 | 1,2,5,12,15  |
|  2 |        32424 | 1,5,12,15,30 |
|  4 |        32426 | 1,2,7,12,15  |
+----+--------------+--------------+
3 rows in set (0.00 sec)

Попробуйте !!!


Привет, @rolandomysqldba, я проверил ваш запрос, и он работает нормально, но мне нужно внести в него некоторые изменения. Допустим, я хочу получить все футболки, в столбце которых значение цвета равно 1,2.
Ахсан Саид

6

Если набор цветов более или менее фиксирован, наиболее эффективным и наиболее читаемым способом будет использование строковых констант в вашем приложении, а затем использование SETтипа MySQL с FIND_IN_SET('red',colors)в ваших запросах. При использовании SETтипа с FIND_IN_SET MySQL использует одно целое число для хранения всех значений и использует двоичную "and"операцию для проверки наличия значений, что намного эффективнее, чем сканирование строки, разделенной запятыми.

В SET('red','blue','green'), 'red'будет храниться внутри , как 1, 'blue'будет храниться внутри , как 2и 'green'будет храниться внутри , как 4. Значение 'red,blue'будет сохранено как 3( 1|2) и 'red,green'как 5( 1|4).



3

Фактически вы должны исправить схему своей базы данных, чтобы у вас было три таблицы:

shirt: shirt_id, shirt_name
color: color_id, color_name
shirtcolor: shirt_id, color_id

Затем, если вы хотите найти все красные рубашки, вы должны выполнить такой запрос:

SELECT *
FROM shirt, color
WHERE color.color_name = 'red'
  AND shirt.shirt_id = shirtcolor.shirt_id
  AND color.color_id = shirtcolor.color_id

8
@Blindy: Это верно только в том случае, если вы предполагаете, что OP имеет права редактирования схемы базы данных; имеет время изменить дизайн базы данных, перенести данные и рефакторинг всех клиентов; и что снижение сложности этого запроса перевешивает увеличение сложности остальной части приложения.
Andomar

1
@Andomar, затем снова, когда он столкнется с ограничениями по размеру для извлечения строк и его "записи" будут обрезаны, ТОТ, когда начнется настоящее веселье!
Blindy

3
@Blindy: Вы упускаете суть; Я не утверждаю, что у него есть лучшее решение, просто не у всех есть свобода переделывать свою среду по своему вкусу
Андомар

Я согласен с @Andomar
Adam B


0

1. Для MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2. Для Postgres SQL:

SELECT * 
FROM TABLENAME f
WHERE 'searchvalue' = ANY (string_to_array(COLUMNNAME, ','))

пример

select * 
from customer f
where '11' = ANY (string_to_array(customerids, ','))

0

Вы можете добиться этого с помощью следующей функции.

Выполните следующий запрос, чтобы создать функцию.

DELIMITER ||
CREATE FUNCTION `TOTAL_OCCURANCE`(`commastring` TEXT, `findme`     VARCHAR(255)) RETURNS int(11)
NO SQL
-- SANI: First param is for comma separated string and 2nd for string to find.
return ROUND (   
    (
        LENGTH(commastring)
        - LENGTH( REPLACE ( commastring, findme, "") ) 
    ) / LENGTH(findme)        
);

И назовите эту функцию так

msyql> select TOTAL_OCCURANCE('A,B,C,A,D,X,B,AB', 'A');

-7

Все ответы не совсем правильные, попробуйте следующее:

select * from shirts where 1 IN (colors);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.