Truy vấn MySQL tìm kiếm các giá trị trong một chuỗi được phân tách bằng dấu phẩy


90

Tôi có một trường COLORS (varchar(50))trong bảng của mình SHIRTScó chứa một chuỗi được phân tách bằng dấu phẩy chẳng hạn như 1,2,5,12,15,. Mỗi số đại diện cho các màu có sẵn.

Khi chạy truy vấn select * from shirts where colors like '%1%'để lấy tất cả những chiếc áo sơ mi màu đỏ (color = 1), tôi cũng nhận được những chiếc áo sơ mi có màu xám (= 12) và màu cam (= 15).

Tôi nên viết lại truy vấn như thế nào để CHỈ chọn màu 1 và không chọn tất cả các màu chứa số 1?


6
Tôi cho rằng bạn có thể làm điều này thông qua regex, nhưng giải pháp tốt hơn nhiều sẽ là chia các màu áo thành một bảng riêng biệt (các màu) và sử dụng một bảng nối (shirt_colors) bằng cách sử dụng id màu / áo để liên kết chúng.
ceejayoz 17/02/11

Tôi không thể tin với 6 câu trả lời không có trong số họ đề cập loại SET dữ liệu MySQL của ..
ColinM

Câu trả lời:


185

Cách cổ điển là thêm dấu phẩy vào bên trái và bên phải:

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

Nhưng find_in_set cũng hoạt động:

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

Tôi đã thử find_in_set nhưng nó trả về cùng một kết quả bất kể giá trị màu tôi nhập ... Bất kỳ đề xuất nào?
bikey77

@ bikey77: Có thể đây là vấn đề, tài liệu cho biết: Hàm này không hoạt động đúng nếu đối số đầu tiên chứa ký tự dấu phẩy (“,”).
Andomar

Sai của tôi, đó là một sai lầm logic do các giá trị giả giống nhau. Nó hoạt động tốt. Cảm ơn!
bikey77

@Andomar Trước khi tôi tìm thấy câu trả lời của bạn tôi wast đấu tranh với TRÊN nhưng bạn là làm việc như một nét duyên dáng ... Cảm ơn bạn rất nhiều ..
PHP Mentor

2
Nó có tác động đến hiệu suất vì Find_in_set không sử dụng chỉ mục
Kamran Shahid


23

Hãy xem hàm FIND_IN_SET dành cho MySQL.

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

1
Lưu ý: tìm trong bộ không sử dụng chỉ mục trên bảng.
edigu

11

Điều này chắc chắn sẽ hoạt động và tôi thực sự đã thử nó:

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)

Hãy thử một lần !!!


Xin chào @rolandomysqldba, tôi đã kiểm tra truy vấn của bạn và nó hoạt động tốt nhưng tôi cần thực hiện một số thay đổi trong đó. Giả sử tôi muốn lấy tất cả các áo sơ mi có giá trị màu là 1,2 trong cột.
Ahsan Saeed

6

Nếu tập hợp màu ít nhiều cố định, cách hiệu quả nhất và cũng dễ đọc nhất sẽ là sử dụng hằng số chuỗi trong ứng dụng của bạn và sau đó sử dụng SETkiểu của MySQL với FIND_IN_SET('red',colors)trong các truy vấn của bạn. Khi sử dụng SETkiểu với FIND_IN_SET , MySQL sử dụng một số nguyên để lưu trữ tất cả các giá trị và sử dụng nhị phân"and" để kiểm tra sự hiện diện của các giá trị hiệu quả hơn so với việc quét một chuỗi được phân tách bằng dấu phẩy.

Trong SET('red','blue','green'), 'red'sẽ được lưu trữ nội bộ như 1, 'blue'sẽ được lưu trữ nội bộ 2'green'sẽ được lưu trữ nội bộ như 4. Giá trị 'red,blue'sẽ được lưu trữ dưới dạng 3( 1|2) và 'red,green'dưới dạng 5( 1|4).


3

Nếu bạn đang sử dụng MySQL, có một phương pháp REGEXP mà bạn có thể sử dụng ...

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

Vì vậy, sau đó bạn sẽ sử dụng:

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

Không thể tìm ra điều này, mặc dù tôi khá chắc rằng đó là lỗi của mình. Cảm ơn một triệu người bạn đời.
bikey77

3

Bạn thực sự nên sửa lược đồ cơ sở dữ liệu của mình để bạn có ba bảng:

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

Sau đó, nếu bạn muốn tìm tất cả những chiếc áo sơ mi có màu đỏ, bạn sẽ thực hiện một truy vấn như:

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: Điều đó chỉ đúng nếu bạn giả sử OP có quyền chỉnh sửa trên lược đồ cơ sở dữ liệu; có thời gian để thiết kế lại cơ sở dữ liệu, di chuyển dữ liệu và cấu trúc lại tất cả các máy khách; và việc giảm độ phức tạp cho truy vấn này lớn hơn sự gia tăng độ phức tạp trong phần còn lại của ứng dụng.
Andomar

1
@Andomar, sau đó một lần nữa khi anh ta gặp phải các hạn chế về kích thước đối với việc truy xuất hàng và "hồ sơ" của anh ta sẽ bị cắt bớt, ĐÓ là lúc niềm vui thực sự sẽ bắt đầu!
Blindy

3
@Blindy: Bạn đang thiếu điểm; Tôi không cho rằng ông có giải pháp tốt nhất, chỉ là không phải ai cũng có quyền tự do để thiết kế lại môi trường của mình theo ý thích của mình
Andomar

Tôi đồng ý với @Andomar
Adam B


0

1. Đối với MySQL:

SELECT FIND_IN_SET(5, columnname) AS result 
FROM table

2.Đối với SQL Postgres:

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

Thí dụ

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

0

Bạn có thể đạt được điều này bằng chức năng sau.

Chạy truy vấn sau để tạo hàm.

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

Và gọi hàm này như thế này

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

-7

Tất cả các câu trả lời không thực sự chính xác, hãy thử điều này:

select * from shirts where 1 IN (colors);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.