拆分字符串并为 SQL Server 2008 R2 中表中的所有记录填充表

Posted

技术标签:

【中文标题】拆分字符串并为 SQL Server 2008 R2 中表中的所有记录填充表【英文标题】:Split a string and populate a table for all records in table in SQL Server 2008 R2 【发布时间】:2013-06-11 21:37:20 【问题描述】:

我有一张桌子EmployeeMoves

| EmployeeID         | CityIDs    
+------------------------------
| 24                 | 23,21,22 
| 25                 | 25,12,14 
| 29                 | 1,2,5 
| 31                 | 7 
| 55                 | 11,34 
| 60                 | 7,9,21,23,30 

我试图弄清楚如何扩展 EmployeeMoves.CityIDs 列中的逗号分隔值以填充 EmployeeCities 表,该表应如下所示:

| EmployeeID         | CityID    
+------------------------------
| 24                 | 23 
| 24                 | 21 
| 24                 | 22 
| 25                 | 25 
| 25                 | 12 
| 25                 | 14 
| ... and so on

我已经有一个名为SplitADelimitedList 的函数,它将一个以逗号分隔的整数列表拆分为一个行集。它将分隔列表作为参数。下面的 SQL 将在Value 列下给我一个带有拆分值的表:

select value from dbo.SplitADelimitedList ('23,21,1,4');

| Value   
+----------- 
| 23          
| 21        
| 1       
| 4  

问题是:如何使用单个(即使是复杂的)SQL 语句从 EmployeeMoves 填充 EmployeeCities,使用逗号分隔列表中的每一行中的 CityIDs EmployeeMoves 表,但在 T-SQL 中没有任何游标或循环?我可以在 EmployeeMoves 表中为 100 位不同的员工提供 100 条记录。

【问题讨论】:

埃里克 - 感谢您的编辑。以后我会记住你的建议。 【参考方案1】:

这就是我试图解决这个问题的方法。它似乎可以工作并且性能非常快。

INSERT INTO EmployeeCities
SELECT
    em.EmployeeID,
    c.Value
FROM EmployeeMoves em
CROSS APPLY dbo.SplitADelimitedList(em.CityIDs) c;

更新 1:

本次更新提供了用户自定义函数 dbo.SplitADelimitedList 的定义。该函数用于上述查询中,将逗号分隔的列表拆分为整数值表。

  CREATE FUNCTION dbo.fn_SplitADelimitedList1
   (
      @String NVARCHAR(MAX)
   )
  RETURNS @SplittedValues TABLE(
   Value INT
  )
 AS
 BEGIN
  DECLARE @SplitLength INT
  DECLARE @Delimiter VARCHAR(10) 
  SET @Delimiter = ',' --set this to the delimiter you are using

  WHILE len(@String) > 0
   BEGIN
    SELECT @SplitLength = (CASE charindex(@Delimiter, @String)
         WHEN 0 THEN
           datalength(@String) / 2
         ELSE
           charindex(@Delimiter, @String) - 1
       END)

   INSERT INTO @SplittedValues
   SELECT cast(substring(@String, 1, @SplitLength) AS INTEGER)
   WHERE
   ltrim(rtrim(isnull(substring(@String, 1, @SplitLength), ''))) <> '';

   SELECT @String = (CASE ((datalength(@String) / 2) - @SplitLength)
         WHEN 0 THEN
           ''
         ELSE
           right(@String, (datalength(@String) / 2) - @SplitLength - 1)
       END)

  END

 RETURN

END

【讨论】:

认为您可以分享您的功能? :) @Spidermain50,您应该在上述答案的 UPDATE 1 下看到此功能。 感谢分享功能!【参考方案2】:

前言

这不是正确的做法。不应在 SQL Server 中创建逗号分隔的列表。这违反了 first normal form,对你来说这听起来像是令人难以置信的卑鄙脏话。

客户端应用程序选择员工行和相关城市并将其显示为逗号分隔的列表是微不足道的。它不应该在数据库中完成。请尽一切可能避免将来发生这种结构。如果可能的话,你应该重构你的数据库。

正确答案

要从包含城市列表的表格中获取适当扩展的城市列表,您可以这样做:

INSERT dbo.EmployeeCities
SELECT
    M.EmployeeID,
    C.CityID
FROM
   EmployeeMoves M
   CROSS APPLY dbo.SplitADelimitedList(M.CityIDs) C
;

错误的答案

由于误解了您想要的内容,我写了这个答案:我认为您正在尝试查询正确存储的数据以生成逗号分隔的 CityID 列表。但我现在意识到您想要相反的结果:使用已存储在列中的现有逗号分隔值查询城市列表。

WITH EmployeeData AS (
   SELECT
      M.EmployeeID,
      M.CityID
   FROM
      dbo.SplitADelimitedList ('23,21,1,4') C
      INNER JOIN dbo.EmployeeMoves M
         ON Convert(int, C.Value) = M.CityID
)
SELECT
   E.EmployeeID,
   CityIDs = Substring((
      SELECT ',' + Convert(varchar(max), CityID)
      FROM EmployeeData C
      WHERE E.EmployeeID = C.EmployeeID
      FOR XML PATH (''), TYPE
   ).value('.[1]', 'varchar(max)'), 2, 2147483647)
FROM
   (SELECT DISTINCT EmployeeID FROM EmployeeData) E
;

我难以理解的部分原因是您的问题有点杂乱无章。下次,清楚地标记您的示例数据,并展示您拥有的内容以及您正在努力实现的目标。由于您将 EmployeeCities 的数据放在最后,看起来这就是您想要实现的目标。当问题没有很好地提出时,就不能很好地利用人们的时间。

【讨论】:

Erik - 我完全同意你的观点,但我的问题是我正在维护一个现有的应用程序,我无法更改它。

以上是关于拆分字符串并为 SQL Server 2008 R2 中表中的所有记录填充表的主要内容,如果未能解决你的问题,请参考以下文章

拆分逗号分隔参数传递给SQL Server 2008 R2中的存储过程

SQL Server 2008 T-SQL UDF Split() 剪裁

SQL Server 2008 R2 将一行结果拆分为两行

获取 MS SQL Server 2008 的连接字符串

没有 SQL Management Studio 的远程 SQL Server 2008 的连接字符串

SQL Server (2008) UNION 与 ROLL UP