2

我有一个看起来像这样的表:

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中没有循环是否可能?

4

7 回答 7

9

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

MS SQL 拆分函数
更好的拆分函数

作者编辑: 效果很好。最终代码如下所示(在创建 split 函数之后):

select pv.productid, colortable.items as color
from product p 
    cross apply split(p.color, ',') as colortable
于 2009-03-31T20:49:20.413 回答
5

根据您的表格:

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)

这与您想要的顺序相同...

于 2009-03-31T21:25:37.223 回答
4

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

声明@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
于 2009-03-31T21:15:19.837 回答
0

如果可能的话,修复你的数据库。数据库单元格中的逗号分隔列表在 99% 或更多的情况下表示存在缺陷的架构。

于 2009-03-31T20:53:16.910 回答
0

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

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

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

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

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

于 2009-03-31T20:53:45.787 回答
0

只需将您的列转换为 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)
于 2017-10-09T12:21:10.207 回答
0

我在帖子发布 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;
于 2019-09-28T06:36:51.883 回答