【问题标题】:Split single row across rows跨行拆分单行
【发布时间】:2012-10-08 21:16:31
【问题描述】:

给定一个Promotion 表,其中包含以下行和数据:

PromotionId    Codes
1              a,b
2              c
3              d,e,f

我如何编写一个查询来返回它:

PromotionId    Codes
1              a
1              b
2              c
3              d
3              e
3              f

基本上我想将代码字符串拆分为多行。

【问题讨论】:

    标签: sql-server tsql split


    【解决方案1】:

    您可以使用Split 表值函数,例如:

    CREATE FUNCTION [dbo].[Split]
    (
        @ItemList NVARCHAR(MAX), 
        @delimiter CHAR(1)
    )
    RETURNS @IDTable TABLE (Item VARCHAR(50))  
    AS      
    
    BEGIN    
        DECLARE @tempItemList NVARCHAR(MAX)
        SET @tempItemList = @ItemList
    
        DECLARE @i INT    
        DECLARE @Item NVARCHAR(4000)
    
        SET @tempItemList = REPLACE (@tempItemList, ' ', '')
        SET @i = CHARINDEX(@delimiter, @tempItemList)
    
        WHILE (LEN(@tempItemList) > 0)
        BEGIN
            IF @i = 0
                SET @Item = @tempItemList
            ELSE
                SET @Item = LEFT(@tempItemList, @i - 1)
            INSERT INTO @IDTable(Item) VALUES(@Item)
            IF @i = 0
                SET @tempItemList = ''
            ELSE
                SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)
            SET @i = CHARINDEX(@delimiter, @tempItemList)
        END 
        RETURN
    END  
    

    那么你可以使用CROSS APPLY加入它:

    SELECT PromotionId, CodeList.Item AS Codes
    FROM Promotion p
        CROSS APPLY dbo.Split(Codes, ',') CodeList
    

    编辑:以下是您的数据:http://sqlfiddle.com/#!3/85f8b/1/0

    结果:

    PROMOTIONID    CODES
      1             a
      1             b
      2             c
      3             d
      3             e
      3             f
    

    【讨论】:

      【解决方案2】:

      如何将 SQL 与 XML 结合使用,如下所示:

      declare @X xml
      
      SET @X= (SELECT CONVERT(xml,'<s id="'+cast(PromotionId as varchar)+'">' + REPLACE(Codes,',','</s><s id="'+cast(PromotionId as varchar)+'">') + '</s>') FROM t1
      FOR XML RAW)
      
      SELECT c.value('(@id)[1]','int') as PromotionId, c.value('.','varchar(50)') as Code
      FROM @X.nodes('/row/s') T(c)
      

      会更高效更快

      SQLFiddle Demo

      【讨论】:

        【解决方案3】:

        我相信在 T-SQL 中拆分/联系事物的最快方法是使用 XML。这是我使用的解决方案:

        ;WITH CTE(PromotionID,Codes)
        AS
        (
            SELECT  PromotionID
                   ,CAST(N'<r><![CDATA[' + REPLACE(Codes, ',', ']]></r><r><![CDATA[') + ']]></r>' AS XML) AS Codes
            FROM @SourceTable
        )
        SELECT  PromotionID
               ,Code
        FROM CTE
        CROSS APPLY (SELECT DISTINCT RTRIM(LTRIM(Tbl.Col.value('.', 'nvarchar(250)'))) AS Code FROM Codes.nodes('//r') Tbl(Col)) AS List
        

        我已使用公用表表达式将代码转换为 XML。然后我只使用一种常用方法将 XML 拆分为单个元素。这是完整的工作示例:

        SET NOCOUNT ON
        GO
        
        DECLARE @SourceTable TABLE
        (
            PromotionID INT,
            Codes NVARCHAR(20)
        )
        
        INSERT INTO @SourceTable (PromotionID,Codes)
        VALUES   (1,'a,b')
                ,(2,'c')
                ,(3,'d,e,f')
        
        ;WITH CTE(PromotionID,Codes)
        AS
        (
            SELECT  PromotionID
                   ,CAST(N'<r><![CDATA[' + REPLACE(Codes, ',', ']]></r><r><![CDATA[') + ']]></r>' AS XML) AS Codes
            FROM @SourceTable
        )
        SELECT  PromotionID
               ,Code
        FROM CTE
        CROSS APPLY (SELECT DISTINCT RTRIM(LTRIM(Tbl.Col.value('.', 'nvarchar(250)'))) AS Code FROM Codes.nodes('//r') Tbl(Col)) AS List
        
        
        SET NOCOUNT OFF
        GO
        

        【讨论】:

        • Split 的优点是您经常需要这样的功能,除了它取决于哪个更快。这表明Split 效率更高(至少有时):simple-talk.com/blogs/2012/01/05/…("拆分功能,如红线所示缩放精美,很难测量,它是如此之快。相比之下,使用基于元素的列表和这种 XQuery 语法的 XML eShred 技术表现出可怕的缩放”)
        • 我也有很多类型的“拆分”函数使用 XML。您的 split 函数非常慢,因为首先它使用 LOOP,其次它使用字符串运算符(众所周知,字符串有多慢,尤其是 SQL Server 处理的大字符串)。因此,使用 XML 更快,并且只需要几行代码。
        • 我没有测量这两种方法,我认为我的方法没有更多或难以理解:SELECT PromotionId, CodeList.Item AS Codes FROM Promotion CROSS APPLY dbo.Split(Code, ',') CodeList。但是,通常在导入等情况下通常需要这样的功能。当您可以将其存储为单独的记录时,使用逗号(或其他)分隔的字符串作为输入或将其存储在数据库中是不正常的。
        • 不管分隔符是','还是'|'或其他任何东西 - 该功能可以轻松处理这个问题。无论如何,我同意你的观点,在大多数(但不是全部)情况下,这将在导入数据时使用。
        猜你喜欢
        • 2011-02-22
        • 2013-01-14
        • 1970-01-01
        • 1970-01-01
        • 2022-06-11
        • 2021-02-17
        • 1970-01-01
        • 2021-11-17
        • 1970-01-01
        相关资源
        最近更新 更多