【问题标题】:Most succinct way to transform a CSV string to a table in T-SQL?将 CSV 字符串转换为 T-SQL 表的最简洁方法?
【发布时间】:2021-06-20 13:15:26
【问题描述】:
-- Given a CSV string like this:

declare @roles varchar(800)
select  @roles = 'Pub,RegUser,ServiceAdmin'

-- Question: How to get roles into a table view like this:

select  'Pub'
union
select  'RegUser'
union
select  'ServiceAdmin'

发布后,我开始使用一些动态 SQL。这似乎可行,但使用动态 SQL 似乎可能存在一些安全风险 - 对此有何想法?

declare @rolesSql varchar(800)
select  @rolesSql = 'select ''' + replace(@roles, ',', ''' union select ''') + ''''
exec(@rolesSql)

【问题讨论】:

    标签: tsql csv


    【解决方案1】:

    如果您使用的是 SQL Server 兼容级别 130,那么 STRING_SPLIT 函数现在是最简洁的可用方法。

    参考链接:https://msdn.microsoft.com/en-gb/library/mt684588.aspx

    用法:

    SELECT * FROM string_split('Pub,RegUser,ServiceAdmin',',')
    
    RESULT:
    
    value
    -----------
    Pub
    RegUser
    ServiceAdmin
    

    【讨论】:

    • 我像这样使用这个函数,所以无论输入是否有前导逗号,它都会产生相同的输出: SELECT * FROM string_split(@csv_string,',') where value ''跨度>
    【解决方案2】:

    查看我来自here的回答

    但基本上你会:

    在你的数据库中创建这个函数:

    CREATE FUNCTION dbo.Split(@origString varchar(max), @Delimiter char(1))     
    returns @temptable TABLE (items varchar(max))     
    as     
    begin     
        declare @idx int     
        declare @split varchar(max)     
    
        select @idx = 1     
            if len(@origString )<1 or @origString is null  return     
    
        while @idx!= 0     
        begin     
            set @idx = charindex(@Delimiter,@origString)     
            if @idx!=0     
                set @split= left(@origString,@idx - 1)     
            else     
                set @split= @origString
    
            if(len(@split)>0)
                insert into @temptable(Items) values(@split)     
    
            set @origString= right(@origString,len(@origString) - @idx)     
            if len(@origString) = 0 break     
        end 
    return     
    end
    

    然后调用函数并传入你要拆分的字符串。

    Select * From dbo.Split(@roles, ',')
    

    【讨论】:

    • 谢谢,这个功能很棒。
    • TVF 是吗?不会是 SELECT * FROM dbo.Split(@roles, ',') 吗??
    【解决方案3】:

    以下是对您的选择的全面讨论:

    【讨论】:

      【解决方案4】:

      使用 SQL Server 的内置 XML 解析也是一种选择。当然,这掩盖了符合 RFC-4180 的 CSV 的所有细微差别。

      -- Given a CSV string like this:
      declare @roles varchar(800)
      select  @roles = 'Pub,RegUser,ServiceAdmin'
      
      -- Here's the XML way
      select split.csv.value('.', 'varchar(100)') as value
      from (
           select cast('<x>' + replace(@roles, ',', '</x><x>') + '</x>' as xml) as data
      ) as csv
      cross apply data.nodes('/x') as split(csv)
      

      如果您使用的是 SQL 2016+,则使用 string_split 更好,但这是 SQL 2016 之前的常用方法。

      【讨论】:

        【解决方案5】:

        在这种情况下,我所做的只是使用一些字符串替换将其转换为 json 并像打开表格一样打开 json。可能不适合所有用例,但运行起来非常简单,并且可以处理字符串和文件。对于文件你只需要看你的换行符,我发现它大多是“Char(13)+Char(10)”

        declare @myCSV nvarchar(MAX)= N'"Id";"Duration";"PosX";"PosY"
        "•P001";223;-30;35
        "•P002";248;-28;35
        "•P003";235;-26;35'
        
        --CSV to JSON
            --convert to json by replacing some stuff
            declare @myJson nvarchar(MAX)= '[['+  replace(@myCSV, Char(13)+Char(10), '],[' )  +']]'
                set @myJson = replace(@myJson, ';',',')         -- Optional: ensure coma delimiters for json if the current delimiter differs
            --  set @myJson = replace(@myJson, ',,',',null,')   -- Optional: empty in between
            --  set @myJson = replace(@myJson, ',]',',null]')   -- Optional: empty before linebreak
            
        SELECT
            ROW_NUMBER() OVER (ORDER BY (SELECT 0))-1 AS LineNumber, *
            FROM   OPENJSON( @myJson ) 
            with (
                 col0   varchar(255)    '$[0]'
                ,col1   varchar(255)    '$[1]'
                ,col2   varchar(255)    '$[2]'
                ,col3   varchar(255)    '$[3]'
                ,col4   varchar(255)    '$[4]'
                ,col5   varchar(255)    '$[5]'
                ,col6   varchar(255)    '$[6]'
                ,col7   varchar(255)    '$[7]'
                ,col8   varchar(255)    '$[8]'  
                ,col9   varchar(255)    '$[9]'
                --any name column count is possible
            ) csv
            order by (SELECT 0) OFFSET 1 ROWS --hide header row
        

        【讨论】:

          【解决方案6】:

          【讨论】:

          • 什么? OP 说 CSV string 不是文件
          【解决方案7】:

          即使是接受的答案也可以正常工作。但即使有数千条记录,我也能更快地获得这个功能。创建下面的函数并使用。

                  IF EXISTS (
                      SELECT 1
                      FROM Information_schema.Routines
                      WHERE Specific_schema = 'dbo'
                          AND specific_name = 'FN_CSVToStringListTable'
                          AND Routine_Type = 'FUNCTION'
                      )
              BEGIN
                  DROP FUNCTION [dbo].[FN_CSVToStringListTable]
              END
              GO
              
              CREATE FUNCTION [dbo].[FN_CSVToStringListTable] (@InStr VARCHAR(MAX))
              RETURNS @TempTab TABLE (Id NVARCHAR(max) NOT NULL)
              AS
              BEGIN
                      ;-- Ensure input ends with comma
              
                  SET @InStr = REPLACE(@InStr + ',', ',,', ',')
              
                  DECLARE @SP INT
                  DECLARE @VALUE VARCHAR(1000)
              
                  WHILE PATINDEX('%,%', @INSTR) <> 0
                  BEGIN
                      SELECT @SP = PATINDEX('%,%', @INSTR)
              
                      SELECT @VALUE = LEFT(@INSTR, @SP - 1)
              
                      SELECT @INSTR = STUFF(@INSTR, 1, @SP, '')
              
                      INSERT INTO @TempTab (Id)
                      VALUES (@VALUE)
                  END
              
                  RETURN
              END
              GO
          
          ---Test like this.
          
              declare @v as NVARCHAR(max) = N'asdf,,as34df,234df,fs,,34v,5fghwer,56gfg,';
              SELECT Id FROM dbo.FN_CSVToStringListTable(@v)
          

          【讨论】:

            猜你喜欢
            • 2011-01-20
            • 1970-01-01
            • 2015-08-28
            • 1970-01-01
            • 2010-12-06
            • 1970-01-01
            • 2010-09-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多