แบบสอบถาม MySQL ค้นหาค่าในสตริงที่คั่นด้วยเครื่องหมายจุลภาค


93

ฉันมีฟิลด์COLORS (varchar(50))ในตารางของฉันSHIRTSที่มีสตริงคั่นด้วยจุลภาคเช่น1,2,5,12,15,. แต่ละหมายเลขแสดงถึงสีที่มี

เมื่อเรียกใช้แบบสอบถามselect * from shirts where colors like '%1%'เพื่อรับเสื้อสีแดงทั้งหมด (color = 1) ฉันยังได้เสื้อที่มีสีเทา (= 12) และสีส้ม (= 15)

ฉันจะเขียนแบบสอบถามใหม่ได้อย่างไรเพื่อให้เลือกเฉพาะสี 1 เท่านั้นไม่ใช่ทุกสีที่มีหมายเลข 1


6
คุณสามารถทำได้ผ่าน regex ฉันคิดว่า แต่วิธีแก้ปัญหาที่ดีกว่ามากคือการแบ่งสีเสื้อออกเป็นตารางแยกต่างหาก (สี) และใช้ตารางรวม (สีเสื้อ) โดยใช้รหัสสี / เสื้อเพื่อเชื่อมโยง
ceejayoz

ไม่อยากจะเชื่อกับ 6 คำตอบที่ไม่มีใครกล่าวถึงประเภทข้อมูล SET ของ MySQL ..
ColinM

1
ตรวจสอบสิ่งนี้: stackoverflow.com/questions/12559876/…
Alireza

คำตอบ:


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 ไม่ได้ใช้ดัชนี
Kamran Shahid



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 ในคอลัมน์
Ahsan Saeed

7

หากชุดสีคงที่มากหรือน้อยวิธีที่มีประสิทธิภาพมากที่สุดและอ่านง่ายที่สุดคือการใช้ค่าคงที่สตริงในแอปของคุณจากนั้นใช้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

หากคุณใช้ MySQL มีวิธีการ REGEXP ที่คุณสามารถใช้ได้ ...

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

ดังนั้นคุณจะใช้:

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

คิดไม่ออกแม้ว่าฉันค่อนข้างมั่นใจว่าเป็นความผิดของฉัน ขอบคุณเพื่อนร่วมงาน
bikey77

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

ฉันเห็นด้วยกับ @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');

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.