将一列拆分为多行
Posted
技术标签:
【中文标题】将一列拆分为多行【英文标题】:Split one column into multiple rows 【发布时间】:2010-11-22 21:38:47 【问题描述】:谁能告诉我如何做到这一点?在某些情况下,我的表中的一列包含逗号分隔值。如果是这样,我需要为这些值创建新行。
此外,例如,一个表包含 1 行和 4 列 Col1 | Col2 | Col3 | Col4 具有以下值 A |乙| C |分别为 1,2,3。所以,Col4 包含字符串 '1,2,3',我需要分解逗号分隔的值并将它们放在自己的行上,这样表格就会包含 1 行,其中 1 2 和 3 在自己的行上在 Col4 中。
【问题讨论】:
这是在 SQL Server 中吗?请问哪个版本? 单列中的逗号分隔值错误。请告诉我您正在修复架构? 您有一个更大的问题,就是。您的数据库存在重大设计缺陷。 @Randy:听起来 OP 正试图通过将它们拆分出来来修复设计缺陷。 【参考方案1】:我认为你可以这样做:
SELECT
T.id, RIGHT(LEFT(T.csv,Number-1),
CHARINDEX(',',REVERSE(LEFT(','+T.csv,Number-1))))
FROM
master..spt_values,
your_table T
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(T.csv)+1
AND
(SUBSTRING(T.csv,Number,1) = ',' OR SUBSTRING(T.csv,Number,1) = '')
代码从this site被无耻窃取。
【讨论】:
虽然它不适用于一些大的字段值,但想法很棒。(使用临时表替换spt_values)。【参考方案2】:您可以编写一个表函数,并使用CROSS APPLY
将您的列加入其中。这是我的版本。
CREATE FUNCTION dbo.Splitter(@text nvarchar(max), @separator nvarchar(100))
RETURNS @result TABLE (i int, value nvarchar(max))
AS
BEGIN
DECLARE @i int
DECLARE @offset int
SET @i = 0
WHILE @text IS NOT NULL
BEGIN
SET @i = @i + 1
SET @offset = charindex(@separator, @text)
INSERT @result SELECT @i, CASE WHEN @offset > 0 THEN LEFT(@text, @offset - 1) ELSE @text END
SET @text = CASE WHEN @offset > 0 THEN SUBSTRING(@text, @offset + LEN(@separator), LEN(@text)) END
END
RETURN
END
【讨论】:
运行脚本会产生错误“无效的对象名称'splitter'”。您建议更改哪个拆分器功能? 好收获!我已将更改更改为创建【参考方案3】:许多字符串拆分函数中的另一个。这有点类似于@Byron Whitlock 的answer,但不是使用 master..spt_values 而是使用 cte 来生成数字表。 SQL Server 2005 及更高版本。
CREATE TABLE dbo.Table1
(
Col1 CHAR(1),
Col2 CHAR(1),
Col3 CHAR(1),
Col4 VARCHAR(50)
)
GO
INSERT INTO dbo.Table1 VALUES ('A','B','C','1,2,3')
GO
SELECT * FROM dbo.Table1;
GO
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT Col1, Col2, Col3,
LTRIM(RTRIM(SUBSTRING(valueTable.Col4, nums.n, charindex(N',', valueTable.Col4 + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.Col4)) AND SUBSTRING(N',' + valueTable.Col4, n, 1) = N','
【讨论】:
+1 哇,感谢可运行的代码。我也喜欢它作为最优雅的解决方案 作为一个侧边栏,这实际上并不是一个递归 CTE,它会慢很多很多。 我真的要研究这个来弄清楚它是如何工作的。干得好。【参考方案4】:我知道这是一篇较旧的帖子,但我想我会添加更新。基于 Tally Table 和 cteTally table 的拆分器都有一个主要问题。他们使用连接的分隔符,当元素变宽并且字符串变长时,这会降低速度。
我已经解决了这个问题并写了一篇关于它的文章,可以在以下 URL 中找到。 http://www.sqlservercentral.com/articles/Tally+Table/72993/
我还要告诉你,一个名叫“Peter”的家伙甚至改进了该代码(在本文的讨论中)。这篇文章仍然很有趣,我将在接下来的一两天内使用 Peter 的增强功能更新附件。在我的主要改进和 Peter 所做的 tweek 之间,我不相信您会找到更快的 T-SQL-Only 解决方案来拆分 VARCHAR(8000)。我还解决了这种类型的 VARCHAR(MAX) 拆分器的问题,并且正在为此写一篇文章。
【讨论】:
以上是关于将一列拆分为多行的主要内容,如果未能解决你的问题,请参考以下文章