将 CSV 字符串转换为 T-SQL 中的表的最简洁方法?

发布于 2024-09-19 09:20:54 字数 490 浏览 9 评论 0原文

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

After posting this, I started playing with some dynamic SQL. This seems to work, but seems like there might be some security risks by using dynamic SQL - thoughts on this?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

酷遇一生 2024-09-26 09:20:54

如果您使用 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

If you're working with SQL Server compatibility level 130 then the STRING_SPLIT function is now the most succinct method available.

Reference link: https://msdn.microsoft.com/en-gb/library/mt684588.aspx

Usage:

SELECT * FROM string_split('Pub,RegUser,ServiceAdmin',',')

RESULT:

value
-----------
Pub
RegUser
ServiceAdmin
不必在意 2024-09-26 09:20:54

请参阅我的回答 这里

但基本上您会:

在数据库中创建此函数:

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

See my answer from here

But basically you would:

Create this function in your DB:

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

and then call the function and pass in the string you want to split.

Select * From dbo.Split(@roles, ',')
请爱~陌生人 2024-09-26 09:20:54

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

Here's a thorough discussion of your options:

触ぅ动初心 2024-09-26 09:20:54

在这种情况下我所做的只是使用一些字符串替换将其转换为 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

What i do in this case is just using some string replace to convert it to json and open the json like a table. May not be suitable for every use case but it is very simple to get running and works with strings and files. With files you just need to watch your line break character, mostly i find it to be "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
愿与i 2024-09-26 09:20:54

使用 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 之前的常见方法。

Using SQL Server's built in XML parsing is also an option. Of course, this glosses over all the nuances of an RFC-4180 compliant 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)

If you are using SQL 2016+, using string_split is better, but this is a common way to do this prior to SQL 2016.

最佳男配角 2024-09-26 09:20:54

即使已接受的答案也运行良好。但即使对于数千条记录,我也能更快地获得此功能。创建以下函数并使用。

        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)

Even the accepted answer is working fine. but I got this function much faster even for thousands of record. create below function and use.

        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)
Spring初心 2024-09-26 09:20:54

我是关于您使用已接受的答案中提到的解决方案,但进行更多研究使我使用表值类型:

在此处输入图像描述

这些效率更高,并且您不需要 TVF(表值函数)只是为了从 csv 创建一个表。您可以直接在脚本中使用它,或者将其作为表值参数传递给存储过程。类型可以创建为:

CREATE TYPE [UniqueIdentifiers] AS TABLE(
    [Id] [varchar](20) NOT NULL
)

I was about you use the solution mentioned in the accepted answer, but doing more research led me to use Table Value Types:

enter image description here

These are far more efficient and you don't need a TVF (Table valued function) just to create a table from csv. You can use it directly in your scripts or pass that to a stored procedure as a Table Value Parameter. The Type can be created as :

CREATE TYPE [UniqueIdentifiers] AS TABLE(
    [Id] [varchar](20) NOT NULL
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文