一列保存多个ID(将多个用逗号隔开的ID转换成用逗号隔开的名称)
ID 多个 保存 名称 转换成 一列 逗号 隔开
2023-06-13 09:14:34 时间
背景:在做项目时,经常会遇到这样的表结构在主表的中有一列保存的是用逗号隔开ID。如,当一个员工从属多个部门时、当一个项目从属多个城市时、当一个设备从属多个项目时,很多人都会在员工表中加入一个deptIdsVARCHAR(1000)列(本文以员工从属多个部门为例),用以保存部门编号列表(很明显这不符合第一范式,但很多人这样设计了,在这篇文章中我们暂不讨论在这种应用场景下,如此设计的对与错,有兴趣的可以在回复中聊聊),然后我们在查询列表中需要看到这个员工从属哪些部门。
初始化数据:
部门表、员工表数据:
复制代码代码如下:
初始化数据:
部门表、员工表数据:
IFEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N"[dbo].[Department]"))
DROPTABLE[dbo].Department
GO
--部门表
CREATETABLEDepartment
(
idint,
namenvarchar(50)
)
INSERTINTODepartment(id,name)
SELECT1,"人事部"
UNION
SELECT2,"工程部"
UNION
SELECT3,"管理部"
SELECT*FROMDepartment
IFEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N"[dbo].[Employee]"))
DROPTABLE[dbo].Employee
GO
--员工表
CREATETABLEEmployee
(
idint,
namenvarchar(20),
deptIdsvarchar(1000)
)
INSERTINTOEmployee(id,name,deptIds)
SELECT1,"蒋大华","1,2,3"
UNION
SELECT2,"小明","1"
UNION
SELECT3,"小华",""
SELECT*FROMEmployee
希望得到的结果:
第一步,是得到如下的数据。即将员工表集合与相关的部门集合做交叉连接,其中使用了fun_SplitIds函数(作用是将ids分割成id列表),然后员工集合与这个得到的集合做交叉连接
SELECTE.*,ISNULL(D.name,"")ASdeptName
FROMEmployeeASE
OUTERAPPLYdbo.fun_SplitIds(E.deptIds)ASDID
LEFTJOINDepartmentASDONDID.ID=D.id;
;WITHEmployeTAS(
--员工的基本信息(使用OUTERAPPLY将多个ID拆分开来,然后与部门表相关联)
--此时已将员工表所存的IDS分别与部门相关联,下面需要将此集合中的deptName聚合成一个记录
SELECTE.*,ISNULL(D.name,"")ASdeptName
FROMEmployeeASE
OUTERAPPLYdbo.fun_SplitIds(E.deptIds)ASDID
LEFTJOINDepartmentASDONDID.ID=D.id
),mikeAS(
SELECTid,name,deptIds,deptName
,ROW_NUMBER()OVER(PARTITIONBYidORDERBYid)ASlevel_num
FROMEmployeT
),mike2AS(
SELECTid,name,deptIds,CAST(deptNameASNVARCHAR(100))ASdeptName,level_num
FROMmike
WHERElevel_num=1
UNIONALL
SELECTm.id,m.name,m.deptIds,CAST(m2.deptName+","+m.deptNameASNVARCHAR(100))ASdeptName,m.level_num
FROMmikeASm
INNERJOINmike2ASm2ONm.ID=m2.idANDm.level_num=m2.level_num+1
),maxMikeByIDTAS(
SELECTid,MAX(level_num)ASlevel_num
FROMmike2
GROUPBYID
)
SELECTA.id,A.name,A.deptIds,A.deptName
FROMmike2ASA
INNERJOINmaxMikeByIDTASBONA.id=B.IDANDA.level_num=B.level_num
ORDERBYA.idOPTION(MAXRECURSION0)
结果如下:
IFEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N"[dbo].[Department]"))
DROPTABLE[dbo].Department
GO
--部门表
CREATETABLEDepartment
(
idint,
namenvarchar(50)
)
INSERTINTODepartment(id,name)
SELECT1,"人事部"
UNION
SELECT2,"工程部"
UNION
SELECT3,"管理部"
SELECT*FROMDepartment
IFEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N"[dbo].[Employee]"))
DROPTABLE[dbo].Employee
GO
--员工表
CREATETABLEEmployee
(
idint,
namenvarchar(20),
deptIdsvarchar(1000)
)
INSERTINTOEmployee(id,name,deptIds)
SELECT1,"蒋大华","1,2,3"
UNION
SELECT2,"小明","1"
UNION
SELECT3,"小华",""
SELECT*FROMEmployee
--创建一个表值函数,用来拆分用逗号分割的数字串,返回只有一列数字的表
IFEXISTS(SELECT*FROMsys.objectsWHEREobject_id=OBJECT_ID(N"[dbo].[fun_SplitIds]"))
DROPFUNCTION[dbo].fun_SplitIds
GO
CREATEFUNCTIONdbo.fun_SplitIds(
@Idsnvarchar(1000)
)
RETURNS@t_idTABLE(idVARCHAR(36))
AS
BEGIN
DECLARE@iINT,@jINT,@lINT,@vVARCHAR(36);
SET@i=0;
SET@j=0;
SET@l=len(@Ids);
while(@j<@l)
begin
SET@j=charindex(",",@Ids,@i+1);
IF(@j=0)set@j=@l+1;
SET@v=cast(SUBSTRING(@Ids,@i+1,@j-@i-1)asVARCHAR(36));
INSERTINTO@t_idVALUES(@v)
SET@i=@j;
END
RETURN;
END
GO
;WITHEmployeTAS(
--员工的基本信息(使用OUTERAPPLY将多个ID拆分开来,然后与部门表相关联)
--此时已将员工表所存的IDS分别与部门相关联,下面需要将此集合中的deptName聚合成一个记录
SELECTE.*,ISNULL(D.name,"")ASdeptName
FROMEmployeeASE
OUTERAPPLYdbo.fun_SplitIds(E.deptIds)ASDID
LEFTJOINDepartmentASDONDID.ID=D.id
),mikeAS(
SELECTid,name,deptIds,deptName
,ROW_NUMBER()OVER(PARTITIONBYidORDERBYid)ASlevel_num
FROMEmployeT
),mike2AS(
SELECTid,name,deptIds,CAST(deptNameASNVARCHAR(100))ASdeptName,level_num
FROMmike
WHERElevel_num=1
UNIONALL
SELECTm.id,m.name,m.deptIds,CAST(m2.deptName+","+m.deptNameASNVARCHAR(100))ASdeptName,m.level_num
FROMmikeASm
INNERJOINmike2ASm2ONm.ID=m2.idANDm.level_num=m2.level_num+1
),maxMikeByIDTAS(
SELECTid,MAX(level_num)ASlevel_num
FROMmike2
GROUPBYID
)
SELECTA.id,A.name,A.deptIds,A.deptName
FROMmike2ASA
INNERJOINmaxMikeByIDTASBONA.id=B.IDANDA.level_num=B.level_num
ORDERBYA.idOPTION(MAXRECURSION0)
相关文章
- 注意:雪花算法并不是ID的唯一选择!
- <statement> or DELIMITER expected, got ‘id‘
- 将非数字的用户ID映射到位图的方案探讨
- Adobe InDesign (ID)2022软件安装包与安装教程(含全版本安装包) +干货分享
- MySQL Error number: MY-010662; Symbol: ER_NDB_SERVER_ID_RESERVED_OR_TOO_LARGE; SQLSTATE: HY000 报错 故障修复 远程处理
- ORA-07262: sptpa: sptpa called with invalid process id. ORACLE 报错 故障修复 远程处理
- 解决Hibernate JPA中insert插入数据后自动执行select last_insert_id()
- Linux查看组ID:获取组ID的简单方法(linux查看组id)
- Mysql: 删除指定ID数据的技巧(mysql删除数据id)
- 重置MySQL表之ID条目的解决方案(mysql重置id)
- Oracle数据库自动增长ID的奇妙实现(oracle自动增长id)
- 解决MySQL数据表ID重复问题(mysqlid重复)
- 值MySQL ID最大值探究(mysqlid最大)
- MySQL查询:如何使用多个ID查询(mysql多个id查询)
- Linux改变用户ID:一个挑战(linux更改用户id)
- 如何获取MySQL中的ID?(获取mysqlid)
- MySQL查找最大ID的简单方法(mysql 取最大id)
- 、ssms使用SSMS按行ID管理SQL Server数据(sqlserver行id)
- MySQL 快速获取ID方案(mysql 获取 id)
- MSSQL配置实例ID实战详解(mssql配置实例id)
- 深入探究Linux进程组ID的作用与实现方式(linux进程组id)
- Linux磁盘分区:妙用ID掌控不同卷(linux分区id)
- MSSQL如何清空自动增长的ID(mssql清空自动id)
- 自增MSSQL自动增长ID号重新排序(mssql 清空id)
- Oracle全局事务ID探索深层原理(oracle全局事务id)
- MySQL中ID出现乱码解决方法(mysql中id数字乱码)
- MySQL中ID从0开始让你更清晰掌握数据索引(mysql中id从0开始)
- 未创建MySQL中的IDC语言如何实现(c mysql未创建id)
- 用Oracle命令查询ID记录(id oracle命令)
- 如何获取Oracle 数据库中的下一个ID(oracle 下一个id)
- Oracle ID达到最大值巨大成就(oracle id最大值)