TSQL/SQL Server-表函数,用于将分隔的字符串解析/拆分为多个/单独的列 [英] TSQL/SQL Server - table function to parse/split delimited string to multiple/separate columns

查看:102
本文介绍了TSQL/SQL Server-表函数,用于将分隔的字符串解析/拆分为多个/单独的列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我的第一篇文章少了一个问题,多了一个声明!抱歉.

So, my first post is less a question and more a statement! Sorry.

我需要将存储在VarChar表列中的定界字符串转换为同一记录的多个/单独的列. (这是COTS软件;因此,请不要麻烦告诉我表的设计有误.)在搜索互联网广告厅中如何创建通用单行呼叫以做到这一点-并找到了很多不这样做的方法后-我创建了自己的. (该名称不是真正的广告.)

I needed to convert delimited strings stored in VarChar table columns to multiple/separate columns for the same record. (It's COTS software; so please don't bother telling me how the table is designed wrong.) After searching the internet ad nauseum for how to create a generic single line call to do that - and finding lots of how not to do that - I created my own. (The name is not real creative.)

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

Returns: A table with sequentially numbered/named columns starting with [Col1]. If an input value is not provided, then an empty string is returned. If less than 32 values are provided, all past the last value are returned as null. If more than 32 values are provided, they are ignored.

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

Prerequisites: A Number/Tally Table (luckily, our database already contained 'dbo.numbers').

假设:不超过32个定界值. (如果需要更多,请更改"WHERE tNumbers.Number在1和XXX之间",并添加更多的预命名列,[Col33] ...,[ColXXX]".)

Assumptions: Not more than 32 delimited values. (If you need more, change "WHERE tNumbers.Number BETWEEN 1 AND XXX", and add more prenamed columns ",[Col33]...,[ColXXX]".)

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

Issues: The very first column always gets populated, even if @InputString is 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)

No, I have not tested any plan (I just needed it to work). Oh, yeah: SQL Server 2012 (12.0 SP2)

评论?更正?增强功能?

Comments? Corrections? Enhancements?

推荐答案

这是我的TVF.易于扩展到32(模式非常清晰).

Here is my TVF. Easy to expand up to the 32 (the pattern is pretty clear).

这是直接的XML,无需支付PIVOT的费用.

This is a straight XML without the cost of the PIVOT.

示例-注意外部应用---使用交叉应用排除NULL

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',' ')

这篇关于TSQL/SQL Server-表函数,用于将分隔的字符串解析/拆分为多个/单独的列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆