【问题标题】:MySQL Select query to fetch record base on list valuesMySQL Select 查询以根据列表值获取记录
【发布时间】:2016-04-05 20:37:40
【问题描述】:

我正在使用 MySQL。 我有 3 个如下表。

表:subject_Master

--------------------------
subjectId   | subjectShortName
----------------------------------
1           |   English
2           |   French
3           |   German
4           |   Latin
----------------------------------

表:class_Master

-----------------------------------
classId     | className
----------------------------------
1           |   Rose
2           |   Dasy
3           |   Lily

表:主题分配

------------------------------------------
allocationId    |   classId |   subjectId
-------------------------------------------
1               |   1       |   1,2
2               |   2       |   2,3,4
3               |   3       |   1,2,3,4

我如何获得 SQL 结果如下,想要获取 subjectAllocation 行中每个 subjectId 的 SubjectName

ClassName   |   SubjectName
-------------------------------------------
Rose        |   English,French
Dasy        |   French,German,Latin
Lily        |   English,French,German,Latin

【问题讨论】:

  • 首先修复subjectAllocation的数据结构。请参阅 previous questions answer 了解如何对其进行规范化
  • subjectAllocation 的数据结构有什么问题?
  • 它存储可变长度的字符串数组而不是可访问的数据。这就是您无法获得所需输出的原因。阅读junction tables(也称为链接表)
  • 是否可以为每个值循环数组以从 subjectMaster 获取它的 subjectName?你能建议数据结构应该是什么吗?
  • 数据结构应该是subjectAllocation { allocationId integer PK, classID integer FK, subjectID integer FK }。 subjectID 现在是一个整数,可以访问和链接到它的实际主题,而不是一个不可访问的字符串列表。你必须做一些预处理(如上面链接的帖子)才能把它变成这个可用的形式

标签: mysql sql select group-concat find-in-set


【解决方案1】:

使用FIND_IN_SET()函数:

试试这个:

SELECT A.allocationId, 
       B.className, 
       GROUP_CONCAT(C.subjectShortName) AS subjectName
FROM subjectAllocation A
INNER JOIN class_Master B ON A.classId = B.classId 
INNER JOIN subject_Master C ON FIND_IN_SET(C.subjectId, A.subjectId) 
GROUP BY A.allocationId;

【讨论】:

  • 非常感谢。一个问题..这个“FIND_IN_SET”有性能问题吗?我希望它不是它的内置功能
  • @TanushPal 是mysql的内置函数。
【解决方案2】:

计划

  • 修复subjectAllocation,使其可访问和可链接
  • 使用序列生成器(来自 digits_v)将可变长度字符串展开为结构化数据
  • 以简单的方式访问结构化数据,加入索引字段

设置

create table subject_Master
(
  subjectId integer primary key not null,
  subjectShortName varchar(23) not null
);

create table class_Master
(
  classId integer primary key not null,
  className varchar(23) not null
);

create table subjectAllocation_inaccessible
(
  allocationId integer primary key not null,
  classId integer not null,
  subjectId varchar(32) not null,
  foreign key ( classId ) references class_Master ( classId )
);

create table subjectAllocation
(
  allocationId integer primary key not null,
  classId integer not null,
  subjectId integer not null,
  foreign key ( classId ) references class_Master ( classId ),
  foreign key ( subjectId ) references subject_Master ( subjectId )
);

insert into subject_Master
( subjectId, subjectShortName )
values
( 1           ,   'English' ),
( 2           ,   'French'  ),
( 3           ,   'German'  ),
( 4           ,   'Latin'   )
;

insert into class_Master
( classId, className )
values
( 1           ,   'Rose' ),
( 2           ,   'Dasy' ),
( 3           ,   'Lily' )
;

insert into subjectAllocation_inaccessible
( allocationId, classId, subjectId )
values
( 1               ,   1       ,   '1,2'      ),
( 2               ,   2       ,   '2,3,4'    ),
( 3               ,   3       ,   '1,2,3,4'  )
;

修复主题分配

create view digits_v
as
SELECT 0 AS N 
UNION ALL 
SELECT 1 
UNION ALL 
SELECT 2 
UNION ALL 
SELECT 3 
UNION ALL 
SELECT 4 
UNION ALL 
SELECT 5 
UNION ALL 
SELECT 6 
UNION ALL 
SELECT 7 
UNION ALL 
SELECT 8 
UNION ALL 
SELECT 9
;

insert into subjectAllocation
( allocationId, classId, subjectId )
select @row_number := @row_number + 1 as allocationId, sa.classId, substring_index(substring_index(sa.subjectId, ',', n.n), ',', -1) sub
from subjectAllocation_inaccessible sa
cross join
(
  select a.N + b.N * 10 + 1 n
  from digits_v a
  cross join digits_v b
  order by n
) n
cross join ( select @row_number := 0 ) params
where n.n <= 1 + (length(sa.subjectId) - length(replace(sa.subjectId, ',', '')))
;

访问简单

select c.className, group_concat(s.subjectShortName)
from subjectAllocation sa
inner join class_Master c
on sa.classId = c.classId
inner join subject_Master s
on sa.subjectId = s.subjectId
group by c.className
;

这里与 class_Master 的连接可以使用主索引 (subjectId)

输出

+-----------+----------------------------------+
| className | group_concat(s.subjectShortName) |
+-----------+----------------------------------+
| Dasy      | French,German,Latin              |
| Lily      | German,Latin,English,French      |
| Rose      | English,French                   |
+-----------+----------------------------------+

sqlfiddle


参考

【讨论】:

  • 对不起,我不是 DBA。什么是 select 0, select 1 ... select 9 in view digits_v ?如果我有超过 100 个 subjectId 或 classId .. 是否意味着我必须在视图 digits_v 中创建 100 个选择?
  • 如果您有超过 100 个,则意味着您需要为数字添加另一个交叉连接。基本上所有数字都由多项式序列 an*10^n + ... a0*10^0 定义(对于 an .. a0 的唯一值)。这是构建用于展开可变长度数据的序列的一种方法。如果你有超过 100 个,规范化你的数据会给你比 find_in_set 更好的性能
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 2022-12-02
相关资源
最近更新 更多