【问题标题】:TSQL/SQL Server - table function to parse/split delimited string to multiple/separate columnsTSQL/SQL Server - 将分隔字符串解析/拆分为多个/单独列的表函数
【发布时间】:2017-09-07 20:15:52
【问题描述】:

所以,我的第一篇文章不是一个问题,而是一个声明!对不起。

我需要将存储在 VarChar 表列中的分隔字符串转换为同一记录的多个/单独列。 (它是 COTS 软件;所以请不要费心告诉我该表是如何设计错误的。)在互联网广告 nauseum 搜索如何创建通用单行调用来做到这一点之后 - 并找到了很多不这样做的方法 -我创建了自己的。 (这个名字没有真正的创意。)

返回:具有以 [Col1] 开头的按顺序编号/命名的列的表。如果未提供输入值,则返回空字符串。如果提供的值少于 32 个,则最后一个值之后的所有值都将返回为 null。如果提供的值超过 32 个,则会被忽略。

先决条件:数字/计数表(幸运的是,我们的数据库已经包含“dbo.numbers”)。

假设:不超过 32 个分隔值。 (如果您需要更多,请更改“WHERE tNumbers.Number BETWEEN 1 AND XXX”,并添加更多预先命名的列“,[Col33]...,[ColXXX]”。)

问题:第一列总是被填充,即使 @InputString 为 NULL。

--======================================================================
--SMOZISEK 2017/09 CREATED
--======================================================================
CREATE FUNCTION dbo.fStringToPivotTable 
        (@InputString   VARCHAR(8000)
        ,@Delimiter     VARCHAR(30)         =   ','
        )
    RETURNS TABLE AS RETURN
    WITH    cteElements AS  (
        SELECT  ElementNumber       =   ROW_NUMBER() OVER(PARTITION BY @InputString ORDER BY (SELECT 0))
                ,ElementValue       =   NodeList.NodeElement.value('.','VARCHAR(1022)')
        FROM        (SELECT TRY_CONVERT(XML,CONCAT('<X>',REPLACE(@InputString,@Delimiter,'</X><X>'),'</X>')) AS InputXML)   AS InputTable
        CROSS APPLY InputTable.InputXML.nodes('/X')                                                                         AS NodeList(NodeElement)
    )
    SELECT  PivotTable.*
        FROM    (
            SELECT  ColumnName          =   CONCAT('Col',tNumbers.Number)
                    ,ColumnValue        =   tElements.ElementValue
            FROM        DBO.NUMBERS         AS  tNumbers                --DEPENDENT ON ANY EXISTING NUMBER/TALLY TABLE!!!
            LEFT JOIN   cteElements         AS  tElements
                ON      tNumbers.Number     =   tElements.ElementNumber
            WHERE       tNumbers.Number     BETWEEN 1 AND 32
        )   AS  XmlSource
    PIVOT (
        MAX(ColumnValue)
        FOR ColumnName
        IN  ([Col1] ,[Col2] ,[Col3] ,[Col4] ,[Col5] ,[Col6] ,[Col7] ,[Col8]
            ,[Col9] ,[Col10],[Col11],[Col12],[Col13],[Col14],[Col15],[Col16]
            ,[Col17],[Col18],[Col19],[Col20],[Col21],[Col22],[Col23],[Col24]
            ,[Col25],[Col26],[Col27],[Col28],[Col29],[Col30],[Col31],[Col32]
            )
    )   AS  PivotTable
    ;
    GO

测试:

SELECT  * 
FROM    dbo.fStringToPivotTable ('|Height|Weight||Length|Width||Color|Shade||Up|Down||Top|Bottom||Red|Blue|','|')   ;

用法:

SELECT  1       AS ID,'Title^FirstName^MiddleName^LastName^Suffix' AS Name
INTO    #TempTable
UNION SELECT    2,'Mr.^Scott^A.^Mozisek^Sr.'
UNION SELECT    3,'Ms.^Jane^Q.^Doe^'
UNION SELECT    5,NULL
UNION SELECT    7,'^Betsy^^Ross^'
;

SELECT  SourceTable.*
        ,ChildTable.Col1        AS  ColTitle
        ,ChildTable.Col2        AS  ColFirst
        ,ChildTable.Col3        AS  ColMiddle
        ,ChildTable.Col4        AS  ColLast
        ,ChildTable.Col5        AS  ColSuffix
FROM    #TempTable              AS  SourceTable
OUTER APPLY dbo.fStringToPivotTable(SourceTable.Name,'^')       AS  ChildTable
;

不,我没有测试任何计划(我只是需要它来工作)。 哦,是的:SQL Server 2012 (12.0 SP2)

评论?更正?增强功能?

【问题讨论】:

  • 你的问题到底是什么?

标签: sql-server tsql parsing csv


【解决方案1】:

这是我的 TVF。轻松扩展至 32 个(模式非常清晰)。

这是一个没有 PIVOT 成本的直接 XML。

示例 - 注意 OUTER APPLY --- 使用 CROSS APPLY 排除 NULLs

Select A.ID
      ,B.*
 From #TempTable A
 Outer Apply [dbo].[tvf-Str-Parse-Row](A.Name,'^') B

退货

有兴趣的 UDF

CREATE FUNCTION [dbo].[tvf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
          ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
          ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
          ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
          ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
          ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
          ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
          ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
          ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
    From  (Select Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
    Where @String is not null
)
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[tvf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[tvf-Str-Parse-Row]('John <test> Cappelletti',' ')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 2011-11-28
    相关资源
    最近更新 更多