请阅读here 为什么你的设计是个坏主意。您应该以normalized 的方式存储数据,如下所示:
表学生:
id | name
1 | John
2 | Jane
3 | Mike
4 | Spike
餐桌爱好:
id | name
1 | biology
2 | geogryphy
3 | football
4 | programming
table students_hobbies:
student_id | hobby_id
1 | 3
1 | 4
2 | 1
2 | 2
3 | 1
3 | 2
3 | 4
4 | 3
架构定义:
CREATE TABLE students (
id INT UNSIGNED AUTO_INCREMENT,
name VARCHAR(50),
PRIMARY KEY (id),
INDEX (name)
);
CREATE TABLE hobbies (
id INT UNSIGNED AUTO_INCREMENT,
name VARCHAR(50),
PRIMARY KEY (id),
INDEX (name)
);
CREATE TABLE students_hobbies (
student_id INT UNSIGNED,
hobby_id INT UNSIGNED,
PRIMARY KEY (student_id, hobby_id),
INDEX (hobby_id, student_id)
);
您的 SELECT 查询现在将是:
SELECT s.*
FROM students s
JOIN students_hobbies sh ON sh.student_id = s.id
JOIN hobbies h ON h.id = sh.hobby_id
WHERE h.name = 'geogryphy';
结果:
| id | name |
| --- | ---- |
| 2 | Jane |
| 3 | Mike |
View on DB Fiddle
但是 - 如果你想坚持你的设计,你可以尝试这样的事情:
SELECT *
FROM students
WHERE student_hobbys LIKE '_|1%'
View on DB Fiddle
但以编程方式生成此查询会相当复杂。在大数据集上,它可能也会比上述解决方案慢,因为这种查询无法使用索引。
如果您想避免应用程序中的复杂代码,您将需要一个相当复杂的查询。一种方法是将您的字符串转换为位掩码,然后使用位运算符& 检查特定位置的位:
SET @hobby_position = 2;
SELECT *
FROM students
WHERE CONV(REVERSE(REPLACE(student_hobbys, '|', '')), 2, 10) & 1 << (@hobby_position - 1);
| id | name | student_hobbys |
| --- | ---- | --------------- |
| 2 | Jane | 1|1|1|1|0|0|1|0 |
| 3 | Mike | 1|1|1|1|0|0|1|0 |
View on DB Fiddle
还有其他方法 - 但您不太可能找到一种可以与您的设计配合使用的简单方法。