【问题标题】:How do I expand comma separated values into separate rows using SQL Server 2005?如何使用 SQL Server 2005 将逗号分隔值展开为单独的行?
【发布时间】:2009-03-31 20:42:14
【问题描述】:

我有一张如下所示的表格:

ProductId, Color
"1", "red, blue, green"
"2", null
"3", "purple, green"

我想将其扩展为:

ProductId, Color
1, red
1, blue
1, green
2, null
3, purple
3, green

实现此目的最简单的方法是什么?是否可以在 proc 中没有循环?

【问题讨论】:

  • 试试我的方法,它会运行得更快...

标签: sql sql-server sql-server-2005


【解决方案1】:

看看这个函数。我已经完成了类似的技巧来拆分和转置 Oracle 中的数据。循环数据,将解码后的值插入到临时表中。修道院的事情是 MS 会让你在运行中执行此操作,而 Oracle 需要一个显式的临时表。

MS SQL Split Function
Better Split Function

作者编辑: 这很好用。最终代码如下所示(创建拆分函数后):

select pv.productid, colortable.items as color
from product p 
    cross apply split(p.color, ',') as colortable

【讨论】:

  • 对于 SQL 2016,您可以使用内置函数:CROSS APPLY STRING_SPLIT(p.color, ',')
【解决方案2】:

根据您的表格:

create table test_table
(
     ProductId  int
    ,Color      varchar(100)
)

insert into test_table values (1, 'red, blue, green')
insert into test_table values (2, null)
insert into test_table values (3, 'purple, green')

像这样创建一个新表:

CREATE TABLE Numbers
(
    Number  int   not null primary key
)

具有包含值 1 到 8000 左右的行。

这将返回你想要的:

编辑
这是一个更好的查询,根据@Christopher Klein 的出色回答稍作修改:

我添加了“LTRIM()”,以便正确处理颜色列表中的空格:“红、蓝、绿”。他的解决方案不需要空格“红、蓝、绿”。另外,我更喜欢使用自己的 Number 表,而不是使用 master.dbo.spt_values,这也允许删除一个派生表。

SELECT
    ProductId, LEFT(PartialColor, CHARINDEX(',', PartialColor + ',')-1) as SplitColor
    FROM (SELECT 
              t.ProductId, LTRIM(SUBSTRING(t.Color, n.Number, 200)) AS PartialColor
              FROM test_table             t
                  LEFT OUTER JOIN Numbers n ON n.Number<=LEN(t.Color) AND SUBSTRING(',' + t.Color, n.Number, 1) = ','
         ) t

编辑结束

SELECT
    ProductId, Color --,number
    FROM (SELECT
              ProductId
                  ,CASE
                       WHEN LEN(List2)>0 THEN LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(',', List2, number+1)-number - 1)))
                       ELSE NULL
                   END AS Color
                  ,Number
              FROM (
                       SELECT ProductId,',' + Color + ',' AS List2
                           FROM test_table
                   ) AS dt
                  LEFT OUTER JOIN Numbers n ON (n.Number < LEN(dt.List2)) OR (n.Number=1 AND dt.List2 IS NULL)
              WHERE SUBSTRING(List2, number, 1) = ',' OR List2 IS NULL
         ) dt2
    ORDER BY ProductId, Number, Color

这是我的结果集:

ProductId   Color
----------- --------------
1           red
1           blue
1           green
2           NULL
3           purple
3           green

(6 row(s) affected)

这是你想要的相同顺序...

【讨论】:

  • 这正是我所需要的。在我的情况下,我正在处理已合并的数据(在同一可计费行项目下运行的多个工作订单,我需要在保持行项目编号的同时将它们分开)。
【解决方案3】:

你可以试试这个,不需要任何额外的功能:

声明 @t 表 (col1 varchar(10), col2 varchar(200)) 插入@t 选择“1”、“红、蓝、绿” 联合所有选择'2',NULL union all select '3', 'green,purple' 选择 col1, left(d, charindex(',', d + ',')-1) as e from ( select *, substring(col2, number, 200) as d from @t col1 left join (从 master.dbo.spt_values 中选择不同的数字,其中数字在 1 到 200 之间) col2 在 substring(',' + col2, number, 1) = ',') t

【讨论】:

  • 很好的答案,这是一个比我第一次尝试更好的查询。有关此修改版本,请参阅我的答案。您对与列名相同的表别名值的使用令人困惑,并且对数字使用系统表会迫使您使用额外的派生表。除此之外,干得好!
【解决方案4】:

我在帖子发布 10 年后提出了这个问题。 SQL server 2016 添加了 STRING_SPLIT 函数。 通过使用它,这可以写成如下。

declare @product table
(
    ProductId int,
    Color     varchar(max)
);
insert into @product values (1, 'red, blue, green');
insert into @product values (2, null);
insert into @product values (3, 'purple, green');

select
    p.ProductId as ProductId,
    ltrim(split_table.value) as Color
from @product p
outer apply string_split(p.Color, ',') as split_table;

【讨论】:

  • 哈!感谢您总结这些老问题!
【解决方案5】:

尽可能修复您的数据库。数据库单元格中的逗号分隔列表在 99% 或更多的情况下表明存在缺陷的架构。

【讨论】:

  • 我同意,但这并没有真正的帮助。
  • 请求的结果集是更好的设计,只需在选择列表和查询的 FROM 之间加上“INTO YourNewTableName”,就会创建一个新表,其中颜色被分割。
  • 如果没有这个问题的答案,你如何建议发帖者修复他的数据库? -1
  • 在发帖时并不清楚他是在修复数据库而不是在其上构建。
【解决方案6】:

我会为此创建一个 CLR 表定义函数:

http://msdn.microsoft.com/en-us/library/ms254508(VS.80).aspx

这样做的原因是 CLR 代码在解析字符串(计算工作)方面会做得更好,并且可以将这些信息作为一个集合传回,而这正是 SQL Server 真正擅长的(集合管理)。

CLR 函数会根据解析的值(和输入的 id 值)返回一系列记录。

然后,您将对表中的每个元素使用 CROSS APPLY。

【讨论】:

  • 恕我直言,这对于如此微不足道的事情来说太过分了。
  • @James:这真的很简单,比在 T-SQL 中解析字符串时必须跳过的代码要简单得多,而且解析速度总是更快。一旦你有任何解析行的函数,交叉应用是自然的选择。
  • 根据要处理的行数和 CSV 颜色的长度,CLR 不会缩放。在这个 3 行的例子中它可以正常工作,但是如果你必须每天都运行这个查询,它会很慢。像我这样的纯 SQL 查询会快得多。
  • @mike:这绝对不是真的,我挑战你展示测试来证明它。鉴于 SQL Server 管理 CLR 所需的所有内容(内存、线程),因此 CLR 的扩展性很好,因此它不会太贪婪。此外,CLR 在这样的过程代码方面总是会做得更好。
  • @mike:我愿意在解析字符串时向您展示我自己对 CLR 代码与 T-SQL 代码的测试。在处理 10000 个字符以下的字符串时,我已经看到速度提高了 30%-200%,所有方法都达到了 100000 个字符左右。
【解决方案7】:

只需将您的列转换为 xml 并查询它。这是一个例子。

select 
    a.value('.', 'varchar(42)') c
from (select cast('<r><a>' + replace(@CSV, ',', '</a><a>') + '</a></r>' as xml) x) t1
cross apply x.nodes('//r/a') t2(a)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多