我有一个看起来像这样的表:
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中没有循环是否可能?
我有一个看起来像这样的表:
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中没有循环是否可能?
看看这个功能。我已经完成了类似的技巧来拆分和转置 Oracle 中的数据。循环数据,将解码后的值插入到临时表中。修道院的事情是 MS 会让你在运行中执行此操作,而 Oracle 需要一个显式的临时表。
作者编辑: 效果很好。最终代码如下所示(在创建 split 函数之后):
select pv.productid, colortable.items as color
from product p
cross apply split(p.color, ',') as colortable
根据您的表格:
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)
这与您想要的顺序相同...
你可以试试这个,不需要任何额外的功能:
声明@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
如果可能的话,修复你的数据库。数据库单元格中的逗号分隔列表在 99% 或更多的情况下表示存在缺陷的架构。
我会为此创建一个 CLR 表定义的函数:
http://msdn.microsoft.com/en-us/library/ms254508(VS.80).aspx
这样做的原因是 CLR 代码在解析字符串(计算工作)方面会做得更好,并且可以将该信息作为一个集合传回,而这正是 SQL Server 真正擅长的(集合管理)。
CLR 函数将根据解析的值(和输入的 id 值)返回一系列记录。
然后,您将对表中的每个元素使用 CROSS APPLY。
只需将您的列转换为 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)
我在帖子发布 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;