将一列拆分为多行

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) 拆分器的问题,并且正在为此写一篇文章。

【讨论】:

以上是关于将一列拆分为多行的主要内容,如果未能解决你的问题,请参考以下文章

oracle单行多列,拆分成多行

将一串空格分隔的单词拆分为多行[重复]

T_SQL 将一列多行数据合并为一行

T SQL 将一列多行数据合并为一行

sql数据拆分

SQL 将一列多行数据合并为一行