Пошук запитів MySQL у значенні, відокремленому комами


90

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

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

Як мені переписати запит, щоб вибрати ТІЛЬКИ колір 1, а не всі кольори, що містять число 1?


6
Ви можете зробити це за допомогою регулярного виразу, але набагато кращим рішенням було б розбити кольори сорочок на окрему таблицю (кольори) і використовувати таблицю об’єднання (shirt_colors), використовуючи ідентифікатори кольору / сорочки, щоб зв’язати їх.
ceejayoz

Я не можу повірити, маючи 6 відповідей, жоден з них не згадав тип даних SET MySQL ..
ColinM

Відповіді:


185

Класичним способом буде додавання коми ліворуч і праворуч:

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

2
Це впливає на ефективність, оскільки 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, потім знову, коли він зіткнеться з обмеженнями розміру для пошуку рядків і його "записи" будуть обрізані, ОТТО почнеться справжня забава!
Сліпий

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

Я згоден з @Andomar
Adam B


0

1. Для MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2. для SQL Postgres:

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.