关闭顶部展开顶部

一列保存多个ID(将多个用逗号隔开的ID转换成用逗号隔开的名称)_Mssql数据库教程

编辑Tag赚U币
教程Tag:暂无Tag,欢迎添加,赚取U币!

推荐:经典SQL语句大全
一、基础 1、说明:创建数据库 CREATE DATABASE database - name 2、说明:删除数据库 drop database dbname 3、说明:备份sql server -- - 创建 备份数据的 device USE master EXEC sp_addumpdevice ' disk ' , ' testBack ' , ' c:\mssql7backup\MyNwind_1.dat ' --

背景:在做项目时,经常会遇到这样的表结构在主表的中有一列保存的是用逗号隔开ID。如,当一个员工从属多个部门时、当一个项目从属多个城市时、当一个设备从属多个项目时,很多人都会在员工表中加入一个deptIds VARCHAR(1000)列(本文以员工从属多个部门为例),用以保存部门编号列表(很明显这不符合第一范式,但很多人这样设计了,在这篇文章中我们暂不讨论在这种应用场景下,如此设计的对与错,有兴趣的可以在回复中聊聊),然后我们在查询列表中需要看到这个员工从属哪些部门。

初始化数据:

部门表、员工表数据:

  1. IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Department]')) 
  2. DROP TABLE [dbo].Department 
  3. GO 
  4. --部门表 
  5. CREATE TABLE Department 
  6.     id int
  7.     name nvarchar(50) 
  8. INSERT INTO Department(id,name
  9. SELECT 1,'人事部' 
  10. UNION  
  11. SELECT 2,'工程部' 
  12. UNION  
  13. SELECT 3,'管理部' 
  14.  
  15. SELECT * FROM Department 
  16.  
  17.  
  18. IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Employee]')) 
  19. DROP TABLE [dbo].Employee 
  20. GO 
  21. --员工表 
  22. CREATE TABLE Employee 
  23.     id int,  
  24.     name nvarchar(20), 
  25.     deptIds varchar(1000) 
  26. INSERT INTO Employee(id,name,deptIds) 
  27. SELECT 1,'蒋大华','1,2,3' 
  28. UNION 
  29. SELECT 2,'小明','1' 
  30. UNION  
  31. SELECT 3,'小华','' 
  32.  
  33. SELECT * FROM Employee 

希望得到的结果:

解决方法:

第一步,是得到如下的数据。即将员工表集合与相关的部门集合做交叉连接,其中使用了fun_SplitIds函数(作用是将ids分割成id列表),然后员工集合与这个得到的集合做交叉连接

SELECT E.*,ISNULL(D.name,'') AS deptName FROM Employee AS E     OUTER APPLY dbo.fun_SplitIds(E.deptIds) AS DID    LEFT JOIN Department AS D ON DID.ID=D.id;

 

第二步,已经得到了如上的数据,然后要做的就是根据ID分组,并对deptName列做聚合操作,但可惜的是SQL SERVER还没有提供对字符串做聚合的操作。但想到,我们处理树形结构数据时,用CTE来做关系数据,做成有树形格式的数据,如此我们也可以将这个问题转换成做树形格式的问题,代码如下:

  1. WITH EmployeT AS
  2. --员工的基本信息(使用OUTER APPLY将多个ID拆分开来,然后与部门表相关联) 
  3. --此时已将员工表所存的IDS分别与部门相关联,下面需要将此集合中的deptName聚合成一个记录 
  4. SELECT E.*,ISNULL(D.name,''AS deptName 
  5. FROM Employee AS E  
  6.    OUTER APPLY dbo.fun_SplitIds(E.deptIds) AS DID 
  7.    LEFT JOIN Department AS D ON DID.ID=D.id 
  8. ),mike AS
  9.    SELECT id,name,deptIds,deptName 
  10.       ,ROW_NUMBER()OVER(PARTITION BY id ORDER BY id) AS level_num 
  11.    FROM EmployeT 
  12. ),mike2 AS
  13.    SELECT id,name,deptIds,CAST(deptName AS NVARCHAR(100)) AS deptName,level_num  
  14.    FROM mike 
  15.    WHERE level_num=1 
  16.    UNION ALL 
  17.    SELECT m.id,m.name,m.deptIds,CAST(m2.deptName+','+m.deptName AS NVARCHAR(100)) AS deptName,m.level_num  
  18.    FROM mike AS m 
  19.       INNER JOIN mike2 AS m2 ON m.ID=m2.id AND m.level_num=m2.level_num+1 
  20. ),maxMikeByIDT AS
  21.        SELECT id,MAX(level_num) AS level_num 
  22.        FROM mike2  
  23.        GROUP BY ID 
  24.  
  25. SELECT A.id,A.name,A.deptIds,A.deptName 
  26. FROM mike2 AS A  
  27.    INNER JOIN maxMikeByIDT AS B ON A.id=B.ID AND A.level_num=B.level_num 
  28. ORDER BY A.id OPTION (MAXRECURSION 0) 
结果如下:

全部SQL:

  1. IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Department]')) 
  2. DROP TABLE [dbo].Department 
  3. GO 
  4. --部门表 
  5. CREATE TABLE Department 
  6.     id int
  7.     name nvarchar(50) 
  8. INSERT INTO Department(id,name
  9. SELECT 1,'人事部' 
  10. UNION  
  11. SELECT 2,'工程部' 
  12. UNION  
  13. SELECT 3,'管理部' 
  14.  
  15. SELECT * FROM Department 
  16.  
  17.  
  18. IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Employee]')) 
  19. DROP TABLE [dbo].Employee 
  20. GO 
  21. --员工表 
  22. CREATE TABLE Employee 
  23.     id int,  
  24.     name nvarchar(20), 
  25.     deptIds varchar(1000) 
  26. INSERT INTO Employee(id,name,deptIds) 
  27. SELECT 1,'蒋大华','1,2,3' 
  28. UNION 
  29. SELECT 2,'小明','1' 
  30. UNION  
  31. SELECT 3,'小华','' 
  32.  
  33. SELECT * FROM Employee 
  34.  
  35. --创建一个表值函数,用来拆分用逗号分割的数字串,返回只有一列数字的表 
  36. IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fun_SplitIds]')) 
  37. DROP FUNCTION [dbo].fun_SplitIds 
  38. GO 
  39. CREATE FUNCTION dbo.fun_SplitIds( 
  40.     @Ids nvarchar(1000) 
  41. RETURNS @t_id TABLE (id VARCHAR(36)) 
  42. AS 
  43. BEGIN 
  44.     DECLARE @i INT,@j INT,@l INT,@v VARCHAR(36); 
  45.     SET @i = 0; 
  46.     SET @j = 0; 
  47.     SET @l = len(@Ids); 
  48.     while(@j < @l) 
  49.     begin 
  50.        SET @j = charindex(',',@Ids,@i+1); 
  51.        IF(@j = 0) set @j = @l+1; 
  52.        SET @v = cast(SUBSTRING(@Ids,@i+1,@j-@i-1) as VARCHAR(36)); 
  53.        INSERT INTO @t_id VALUES(@v) 
  54.        SET @i = @j; 
  55.     END 
  56.     RETURN
  57. END 
  58. GO 
  59.  
  60.  
  61. ;WITH EmployeT AS
  62. --员工的基本信息(使用OUTER APPLY将多个ID拆分开来,然后与部门表相关联) 
  63. --此时已将员工表所存的IDS分别与部门相关联,下面需要将此集合中的deptName聚合成一个记录 
  64. SELECT E.*,ISNULL(D.name,''AS deptName 
  65. FROM Employee AS E  
  66.    OUTER APPLY dbo.fun_SplitIds(E.deptIds) AS DID 
  67.    LEFT JOIN Department AS D ON DID.ID=D.id 
  68. ),mike AS
  69.    SELECT id,name,deptIds,deptName 
  70.       ,ROW_NUMBER()OVER(PARTITION BY id ORDER BY id) AS level_num 
  71.    FROM EmployeT 
  72. ),mike2 AS
  73.    SELECT id,name,deptIds,CAST(deptName AS NVARCHAR(100)) AS deptName,level_num  
  74.    FROM mike 
  75.    WHERE level_num=1 
  76.    UNION ALL 
  77.    SELECT m.id,m.name,m.deptIds,CAST(m2.deptName+','+m.deptName AS NVARCHAR(100)) AS deptName,m.level_num  
  78.    FROM mike AS m 
  79.       INNER JOIN mike2 AS m2 ON m.ID=m2.id AND m.level_num=m2.level_num+1 
  80. ),maxMikeByIDT AS
  81.        SELECT id,MAX(level_num) AS level_num 
  82.        FROM mike2  
  83.        GROUP BY ID 
  84.  
  85.  
  86. SELECT A.id,A.name,A.deptIds,A.deptName 
  87. FROM mike2 AS A  
  88.    INNER JOIN maxMikeByIDT AS B ON A.id=B.ID AND A.level_num=B.level_num 
  89. ORDER BY A.id OPTION (MAXRECURSION 0) 

分享:SQL Server字符串切割
结果: 645002*01 --1 45854 --2 183677

来源:未知//所属分类:Mssql数据库教程/更新时间:2012-07-20
相关Mssql数据库教程
闂佹眹鍩勯崹閬嶆偤閺囶澁缍栭柛鈩冪⊕閳锋帗銇勯弴妤€浜惧銈忕秶閹凤拷
濠电偛顕慨顓㈠磻閹炬枼妲堥柡鍌濇硶婢ф稒淇婇懠顒夆偓婵嬫煟閵忊晛鐏查柟鍑ゆ嫹
濠电姷顣介埀顒€鍟块埀顒勵棑缁辩偛顓兼径瀣閻庣懓瀚竟鍡欐崲娑斾線鏌i姀鈺佺伈闁瑰嚖鎷�
濠电姷顣介埀顒€鍟块埀顒勵棑缁辩偛顓兼径濠勵吋闂佽鍨庨崟顓фК闂佽閰eḿ褍螞濞戙垺鍋夐柨鐕傛嫹
闂備胶枪缁绘劙骞婃惔銊ョ劦妞ゆ帒鍊哥敮鍫曞箹鐎涙ḿ鐭掔€规洘绻堥弫鎾绘晸閿燂拷
闂備胶枪缁绘劙骞婃惔銊ョ劦妞ゆ巻鍋撻柛姘儑缁﹪鏁傞崜褏鐓撻柣搴岛閺呮繈鎯屽▎鎴犵=濞撴艾锕ョ€氾拷
闂備浇銆€閸嬫挻銇勯弽銊р槈闁伙富鍣i弻娑樷攽閹邦亞鑳虹紓浣靛妽濡炶棄顕i妸鈺婃晬婵炲棙鍨电粭锟犳⒑閸濆嫬鈧骞婇幘鑸殿潟闁跨噦鎷�
闂備礁鎼崯鐗堟叏妞嬪海绀婂鑸靛姈閻擄綁鎮规潪鎷岊劅婵炲眰鍊曢湁闁挎繂妫欑粈鈧梺鍛娚戦悧鐘茬暦閹扮増鏅搁柨鐕傛嫹
婵犵妲呴崹顏堝礈濠靛棭鐔嗘俊顖氬悑鐎氱粯銇勯幘瀵哥畺閻庢熬鎷�
濠电姷顣介埀顒€鍟块埀顒勵棑缁辩偛顓奸崶銊ヮ伕濡炪倖鎸荤换鍐偓姘虫珪娣囧﹪顢涘Δ鈧晶鍙夌節椤喗瀚�
婵犵妲呴崹顏堝礈濠靛棭鐔嗘慨妞诲亾鐎规洦鍓熼、娆撳礂閻撳簶鍋撻悽鍛婄厸闁割偅绻勫瓭婵犳鍣幏锟�
婵犵妲呴崹顏堝礈濠靛棭鐔嗘慨妞诲亾闁哄苯鎳橀崺鈧い鎺嗗亾闁宠閰i獮鎴﹀箛闂堟稒顔嗛梻浣告惈鐎氭悂骞忛敓锟�
婵犵妲呴崹顏堝礈濠靛棭鐔嗘慨妞诲亾鐎规洩缍侀獮瀣攽閸偂绱�
濠电姷顣介埀顒€鍟块埀顒勵棑缁辩偛顓兼径濠勭厬闂佺懓鐡ㄧ换鍕敂鐎涙ü绻嗘い鏍殔婢у弶绻濋~顔藉
闂佽楠搁崢婊堝礈濠靛鍋嬮柟鎯版閻鈹戦悩鎻掓殭闁奸潧缍婇弻銈夋嚍閵夈儱顫嶉梺缁樼壄缂嶄礁鐣峰▎鎾存櫢闁跨噦鎷�
UB闂備礁婀辩划顖炲礉濡ゅ懐宓侀柛銉㈡櫆鐎氭岸鎮楀☉娅虫垿锝為敓锟�
闂備浇澹堟ご绋款潖婵犳碍鐒鹃悗鐢电《閸嬫捇鐛崹顔句痪濠电姭鍋撻柨鐕傛嫹
闂佽楠哥粻宥夊垂閸濆嫸鑰块柛銏㈠殰
闂備礁鎲″缁樻叏妞嬪海绀婂璺虹灱閸楁碍绻涢崱妤€顒㈤柛鐐差槹缁绘稓绱欓悩鍝勫帯闂佺ǹ楠忛幏锟�
缂傚倸鍊烽悞锕傛偡閿曞倸鍨傛繝濠傚椤╅攱銇勯幒宥囶槮缂佹彃婀遍埀顒傚仯閸婃繄绱撳棰濇晩闁跨噦鎷�
©2017 www.mb5u.com婵犵妲呴崹顏堝礈濠靛棭鐔嗘慨妞诲亾鐎殿噮鍣i幃鈺呭箵閹烘挸鐦�
闂備浇銆€閸嬫捇鏌熼婊冾暭妞ゃ儻鎷�&闂備礁鎲$敮鎺懳涢弮鍫燁棅闁跨噦鎷�