MySQL 从列中选择使用 ^ 作为分隔符
Posted
技术标签:
【中文标题】MySQL 从列中选择使用 ^ 作为分隔符【英文标题】:MySQL Select from column use ^ as delimiter 【发布时间】:2015-07-24 12:53:13 【问题描述】:我的问题类似于mysql Split String and Select with results。目前我有 2 张桌子:
student
uid | subject_id | name
1 | 1^2^3^4 | a
2 | 2^3^ | b
3 | 1 | c
subject
uid | subject_name
1 | math
2 | science
3 | languange
4 | sport
我预期的结果是:
uid | name | subject_passed
1 | a | math, science, languange, sport
2 | b | science, languange
3 | c | sport
我试过这个查询:
SELECT
student.uid,
student.name,
group_concat(subject.subject_name) as subjects_passed
from student
join subject on find_in_set(subject.uid,student.subject_id ) > 0
group by student.uid
返回错误:
#1064 - 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册以获取正确的语法使用 near '在 find_in_set(subject.uid,student.subject_id) > 0 上加入主题 组'在第 7 行
我相信是因为FIND_IN_SET
。根据文档,此函数需要 ,
作为分隔符。有没有我可以使用的替代查询?
【问题讨论】:
嗯,最好的 做法是不要将多个值存储在单个列中,而是使用联结[uid|single_subject_id]
表
patrick,您确实需要将此模式更改为连接模式。想看它 ?您的违反了第三范式 (3NF) 设计模式。然后你又在其他 cmets 中说你有很多数据,所以我怀疑你甚至会根据惯性来做:>
是的,目前我想做的是增强它,可以试试 Abhik 的推荐,桌子设计不是我设计的 >_<..>
【参考方案1】:
为什么不用REPLACE
分隔符:
SELECT
student.uid,
student.name,
GROUP_CONCAT(subject.subject_name) AS subjects_passed
FROM student
JOIN subject ON FIND_IN_SET(subject.uid, REPLACE(student.subject_id, '^', ',')) > 0
GROUP BY student.uid
SQLFiddle
如果您决定对表进行非规范化,那么创建联结表并生成数据是相当简单的:
-- Sample table structure
CREATE TABLE student_subject (
student_id int NOT NULL,
subject_id int NOT NULL,
PRIMARY KEY (student_id, subject_id)
);
-- Sample query to denormalize student <-> subject relationship
SELECT
student.uid AS student_id,
subject.uid AS subject_id
FROM student
JOIN subject ON FIND_IN_SET(subject.uid, REPLACE(student.subject_id, '^', ',')) > 0
+------------+------------+
| student_id | subject_id |
+------------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
+------------+------------+
【讨论】:
哇,感谢您的更新。所以我将查询结果导出为 csv 格式,并存储回新表..太棒了.. 否,仅导出到 CSV 以查看数据是否正常。然后使用INSERT...SELECT
查询。
您好,我再次尝试运行此查询,“select st.uid, st.name, group_concat(sb.subject_name) aspassed_subject from student st join student_subject ss on ss.stid = st.uid join sb.uid 上的主题 sb = ss.subid 其中 st = studentID --(来自存储过程的 IN 参数)按 st.uid 分组;"但与“替换”解决方案相比,它需要更长的时间,知道吗? student_subject(3.5M 行), subject(1.5K 行), student(700k)
350万行太多,必须在student_subject
表上定义索引。如果可能,请创建一个复合主键 (stid, subid)
,以便没有学生可以两次加入同一学科。如果这是不可能的,那么就在上面创建一个索引。还要反向创建相同的索引,即(subid, stid)
。让 MySQL 选择最好的。顺便说一句,我觉得 1.5k 主题看起来太多了。
可以说是从几所学校合并而来的,每个学校都有自己的学科代码和名称。想象一下整个数据库包含一个城市的学生信息,那是 1.5k 科目的来源。有点多余和混乱。【参考方案2】:
您永远不应该使用分隔符存储数据,并且应该规范化表格并创建第三个表格来存储学生与主题的关系。
但是在当前情况下,您可以这样做
select
st.uid,
st.name,
group_concat(sb.subject_name) as subject_name
from student st
left join subject sb on find_in_set(sb.uid,replace(st.subject_id,'^',',')) > 0
group by st.uid
这里是创建第三个表并存储关系的选项
create table student_to_subject (id int primary key auto_increment, stid int, subid int);
insert into student_to_subject(stid,subid) values
(1,1),(1,2),(1,3),(1,4),(2,2),(2,3),(3,1);
现在您可以从 student
表中删除列 subject_id
所以查询变成了
select
st.uid,
st.name,
group_concat(sb.subject_name) as passed_subject
from student st
join student_to_subject sts on sts.stid = st.uid
join subject sb on sb.uid = sts.subid
group by st.uid;
http://www.sqlfiddle.com/#!9/f02df
【讨论】:
为什么会有人反对这个答案?! @DrewPierce 有人得到了我的回答真的 pi** :-) @SalmanA 是的,一旦我发布了答案,我就意识到了!但是我总是选择不提供任何重复的答案。并关闭重复的答案,无论答案多么简单:) @SalmanA,我是袜子木偶,嗯?Definition: a false online identity, typically created by a person or group in order to promote their own opinions or views.
好的,如果你这么认为的话。
你好 abhik,谢谢你的推荐,目前学生表有相当多的数据,有什么方法可以根据前两个表自动填充/插入第三个表?【参考方案3】:
认为您可以在调用 find_in_set 之前将 ^
替换为 ,
:
SELECT
student.uid,
student.name,
group_concat(subject.subject_name) as subjects_passed
from student
join subject on find_in_set(subject.uid, replace(student.subject_id,'^',',') ) > 0
group by student.uid
当然,以这种格式存储值是非常糟糕的数据库设计。
【讨论】:
以上是关于MySQL 从列中选择使用 ^ 作为分隔符的主要内容,如果未能解决你的问题,请参考以下文章