MySQL 查询在逗号分隔的字符串中查找值

我的表 SHIRTS中有一个字段 COLORS (varchar(50)),它包含一个逗号分隔的字符串,比如 1,2,5,12,15,。每个数字表示可用的颜色。

当运行查询 select * from shirts where colors like '%1%'以获得所有红色衬衫(颜色 = 1)时,我还获得了灰色(颜色 = 12)和橙色(颜色 = 15)的衬衫。

我应该如何重写查询,以便只选择颜色1,而不是所有的颜色包含数字1?

179198 次浏览

If you're using MySQL, there is a method REGEXP that you can use...

http://dev.mysql.com/doc/refman/5.1/en/regexp.html#operator_regexp

So then you would use:

SELECT * FROM `shirts` WHERE `colors` REGEXP '\b1\b'

Take a look at the FIND_IN_SET function for MySQL.

SELECT *
FROM shirts
WHERE FIND_IN_SET('1',colors) > 0

You should actually fix your database schema so that you have three tables:

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

Then if you want to find all of the shirts that are red, you'd do a query like:

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

The classic way would be to add commas to the left and right:

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

But find_in_set also works:

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

FIND_IN_SET is your friend in this case

select * from shirts where FIND_IN_SET(1,colors)

This will work for sure, and I actually tried it out:

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)

Give it a Try !!!

If the set of colors is more or less fixed, the most efficient and also most readable way would be to use string constants in your app and then use MySQL's SET type with FIND_IN_SET('red',colors) in your queries. When using the SET type with FIND_IN_SET, MySQL uses one integer to store all values and uses binary "and" operation to check for presence of values which is way more efficient than scanning a comma-separated string.

In SET('red','blue','green'), 'red' would be stored internally as 1, 'blue' would be stored internally as 2 and 'green' would be stored internally as 4. The value 'red,blue' would be stored as 3 (1|2) and 'red'0 as 'red'1 ('red'2).

All the answers are not really correct, try this:

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

Works for me

You can achieve this by following function.

Run following query to create function.

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)
);

And call this function like this

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

1. For MySQL:

SELECT FIND_IN_SET(5, columnname) AS result
FROM table

2.For Postgres SQL :

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

Example

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