Sql 在结果集中为输入字符串中的每个换行符创建新行

Sql 在结果集中为输入字符串中的每个换行符创建新行,sql,sql-server,Sql,Sql Server,我正在尝试将逗号分隔的值字符串转换为表。到目前为止,我已经能够根据输入的字符串创建表,但我也尝试为每个换行添加一个新行。我认为这可以通过循环字符串和在每条换行线上拆分来实现。不幸的是,不能使用string\u SPLIT,然后为每个换行添加一个联合到动态选择中。然而,这听起来可能是最没有效率的方法 这是我目前掌握的代码: DECLARE @ps_String varchar(max), @ps_Delimiter char(1), @ls_XML xml,

我正在尝试将逗号分隔的值字符串转换为表。到目前为止,我已经能够根据输入的字符串创建表,但我也尝试为每个换行添加一个新行。我认为这可以通过循环字符串和在每条换行线上拆分来实现。不幸的是,不能使用string\u SPLIT,然后为每个换行添加一个联合到动态选择中。然而,这听起来可能是最没有效率的方法

这是我目前掌握的代码:

DECLARE @ps_String varchar(max),
        @ps_Delimiter char(1),
        @ls_XML xml,
        @ln_Count INT,
        @ls_SQL VARCHAR(MAX) = ''

SELECT @ps_String = 'ab,cd,ef,gh,ij,
kl, mn, op, qr,st',
       @ps_Delimiter = ','

SET @ls_SQL = '
DECLARE @ls_XML XML
SELECT @ls_XML = CONVERT(xml,'' <root> <myvalue>'' +
REPLACE('''+@ps_String+''','''+@ps_Delimiter+''',''</myvalue> <myvalue>'') + ''</myvalue>   </root> '')
SELECT DISTINCT'

SET @ln_Count = 1
WHILE (@ln_Count <= LEN(@ps_String) - LEN(REPLACE(@ps_String, @ps_Delimiter, '')) + 1 ) BEGIN
    SET @ls_SQL = @ls_SQL + '
    T.c.value(''(/root/myvalue)['+CONVERT(VARCHAR, @ln_Count)+']'',''VARCHAR(20)''),'
    SET @ln_Count = @ln_Count + 1
END

SET @ls_SQL = LEFT(@ls_SQL, LEN(@ls_SQL) - 1)

SET @ls_SQL = @ls_SQL + '
FROM @ls_XML.nodes(''/root/myvalue'') T(c)'

PRINT @ls_SQL
--EXEC(@ls_SQL)

SQL Server 2014

这里有一个明确的黑客,您可以尝试:

DECLARE @ps_String VARCHAR(MAX) = 'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

/* Clean-up and divide the provided string into rows based on CR/LF */
SET @ps_String = (
    SELECT 
        REPLACE ( cleanString, ',', '</val><val>' ) AS myXml
    FROM (
            SELECT '<rows><row><val>' + REPLACE ( REPLACE ( @ps_String, CHAR(13), '' ), CHAR(10), '</val></row><row><val>' ) + '</val></row></rows>' AS rws
    ) AS getRows
    CROSS APPLY ( SELECT REPLACE ( getRows.rws, ',<', '<' ) AS cleanString ) AS stripTrailingComma
);

/* Add a unique row id to each row */
DECLARE @id INT = 0;
WHILE CHARINDEX ( '<row>', @ps_String ) > 0 
BEGIN
    SET @id = @id + 1;
    SET @ps_String = STUFF ( @ps_String, CHARINDEX ( '<row>', @ps_String ) + 4, 0, FORMATMESSAGE ( ' id="%i"', @id ) ); 
END

/* Show results */
SELECT
    r.v.value( '../@id', 'INT' ) AS row_id,
    ROW_NUMBER() OVER ( PARTITION BY r.v.value( '../@id', 'INT' ) ORDER BY r.v.value( '../@id', 'INT' ) ) AS val_id,
    LTRIM( RTRIM( r.v.value( '.', 'VARCHAR(255)' ) ) ) AS val
FROM (
    SELECT CAST ( @ps_String AS XML ) AS rws
) AS myXml
CROSS APPLY rws.nodes( '//rows/row/val' ) r(v);

这里有一个明确的方法,你可以试试:

DECLARE @ps_String VARCHAR(MAX) = 'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

/* Clean-up and divide the provided string into rows based on CR/LF */
SET @ps_String = (
    SELECT 
        REPLACE ( cleanString, ',', '</val><val>' ) AS myXml
    FROM (
            SELECT '<rows><row><val>' + REPLACE ( REPLACE ( @ps_String, CHAR(13), '' ), CHAR(10), '</val></row><row><val>' ) + '</val></row></rows>' AS rws
    ) AS getRows
    CROSS APPLY ( SELECT REPLACE ( getRows.rws, ',<', '<' ) AS cleanString ) AS stripTrailingComma
);

/* Add a unique row id to each row */
DECLARE @id INT = 0;
WHILE CHARINDEX ( '<row>', @ps_String ) > 0 
BEGIN
    SET @id = @id + 1;
    SET @ps_String = STUFF ( @ps_String, CHARINDEX ( '<row>', @ps_String ) + 4, 0, FORMATMESSAGE ( ' id="%i"', @id ) ); 
END

/* Show results */
SELECT
    r.v.value( '../@id', 'INT' ) AS row_id,
    ROW_NUMBER() OVER ( PARTITION BY r.v.value( '../@id', 'INT' ) ORDER BY r.v.value( '../@id', 'INT' ) ) AS val_id,
    LTRIM( RTRIM( r.v.value( '.', 'VARCHAR(255)' ) ) ) AS val
FROM (
    SELECT CAST ( @ps_String AS XML ) AS rws
) AS myXml
CROSS APPLY rws.nodes( '//rows/row/val' ) r(v);

下面的方法是检索动态SQL语句构造所需的任何字符串拆分信息的一种方法。它由2个递归公共表表达式组成,首先拆分行,然后拆分列

以防你更愿意看到它的行动

declare @data nvarchar(100) = N'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

declare @rowDelimiter nvarchar(1) = char(10);
declare @colDelimiter nvarchar(1) = ',';

with cte_rows as
(
  select 1 as RowNum,
         @data as Data,
         1 as Starts,
         charindex(@rowDelimiter, @data) as Pos
    union all
  select cr.RowNum + 1,
         cr.Data,
         cr.Pos+1,
         charindex(@rowDelimiter, cr.Data, cr.Pos+1)
  from cte_rows cr
  where cr.Pos > 0
),
cte_cols as
(
  select cr.RowNum,
         1 as ColNum,
         substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end) as RowData,
         1 as Starts,
         charindex(@colDelimiter,  substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end)) as Pos
  from cte_rows cr
    union all
  select cc.RowNum,
         cc.ColNum + 1,
         cc.RowData,
         cc.Pos+1,
         charindex(@colDelimiter, cc.RowData, cc.Pos+1)
  from cte_cols cc
  where cc.Pos > 0
)
select cc.RowNum,
       cc.ColNum,
       rtrim(ltrim(substring(cc.RowData, cc.Starts, case when cc.Pos>0 then cc.Pos-cc.Starts else len(cc.RowData) end))) as ColVal
from cte_cols cc
order by cc.RowNum, cc.ColNum;
结果:

RowNum ColNum ColVal
------ ------ ------
1      1      ab
1      2      cd
1      3      ef
1      4      gh
1      5      ij
1      6      
2      1      kl
2      2      mn
2      3      op
2      4      qr
2      5      st
第1行有6列,第2行有5列,因为第1行后面有逗号,第2行没有。在ColVal上筛选此数据集,或在cte_cols RowData中删除子字符串选择后的尾随逗号

在创建动态SQL语句后,在上一次查询后添加固定轴以预览潜在输出:

declare @data nvarchar(100) = N'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

declare @rowDelimiter nvarchar(1) = char(10);
declare @colDelimiter nvarchar(1) = ',';

-- split rows + columns and pivot
with cte_rows as
(
  select 1 as RowNum,
         @data as Data,
         1 as Starts,
         charindex(@rowDelimiter, @data) as Pos
    union all
  select cr.RowNum + 1,
         cr.Data,
         cr.Pos+1,
         charindex(@rowDelimiter, cr.Data, cr.Pos+1)
  from cte_rows cr
  where cr.Pos > 0
),
cte_cols as
(
  select cr.RowNum,
         1 as ColNum,
         substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end) as RowData,
         1 as Starts,
         charindex(@colDelimiter,  substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end)) as Pos
  from cte_rows cr
    union all
  select cc.RowNum,
         cc.ColNum + 1,
         cc.RowData,
         cc.Pos+1,
         charindex(@colDelimiter, cc.RowData, cc.Pos+1)
  from cte_cols cc
  where cc.Pos > 0
),
cte_values as
(
  select cc.RowNum,
         cc.ColNum,
         rtrim(ltrim(substring(cc.RowData, cc.Starts, case when cc.Pos>0 then cc.Pos-cc.Starts else len(cc.RowData) end))) as ColVal
  from cte_cols cc
)
select p.RowNum,
       p.[1] as Col1,
       p.[2] as Col2,
       p.[3] as Col3,
       p.[4] as Col4,
       p.[5] as Col5,
       p.[6] as Col6
from cte_values cv
pivot (max(cv.ColVal) for cv.ColNum in ([1], [2], [3], [4], [5], [6])) p
order by p.RowNum;
这使得:

RowNum Col1    Col2    Col3    Col4    Col5    Col6
------ ------- ------- ------- ------- ------- -------
1      ab      cd      ef      gh      ij      
2      kl      mn      op      qr      st      null

下面的方法是检索动态SQL语句构造所需的任何字符串拆分信息的一种方法。它由2个递归公共表表达式组成,首先拆分行,然后拆分列

以防你更愿意看到它的行动

declare @data nvarchar(100) = N'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

declare @rowDelimiter nvarchar(1) = char(10);
declare @colDelimiter nvarchar(1) = ',';

with cte_rows as
(
  select 1 as RowNum,
         @data as Data,
         1 as Starts,
         charindex(@rowDelimiter, @data) as Pos
    union all
  select cr.RowNum + 1,
         cr.Data,
         cr.Pos+1,
         charindex(@rowDelimiter, cr.Data, cr.Pos+1)
  from cte_rows cr
  where cr.Pos > 0
),
cte_cols as
(
  select cr.RowNum,
         1 as ColNum,
         substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end) as RowData,
         1 as Starts,
         charindex(@colDelimiter,  substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end)) as Pos
  from cte_rows cr
    union all
  select cc.RowNum,
         cc.ColNum + 1,
         cc.RowData,
         cc.Pos+1,
         charindex(@colDelimiter, cc.RowData, cc.Pos+1)
  from cte_cols cc
  where cc.Pos > 0
)
select cc.RowNum,
       cc.ColNum,
       rtrim(ltrim(substring(cc.RowData, cc.Starts, case when cc.Pos>0 then cc.Pos-cc.Starts else len(cc.RowData) end))) as ColVal
from cte_cols cc
order by cc.RowNum, cc.ColNum;
结果:

RowNum ColNum ColVal
------ ------ ------
1      1      ab
1      2      cd
1      3      ef
1      4      gh
1      5      ij
1      6      
2      1      kl
2      2      mn
2      3      op
2      4      qr
2      5      st
第1行有6列,第2行有5列,因为第1行后面有逗号,第2行没有。在ColVal上筛选此数据集,或在cte_cols RowData中删除子字符串选择后的尾随逗号

在创建动态SQL语句后,在上一次查询后添加固定轴以预览潜在输出:

declare @data nvarchar(100) = N'ab,cd,ef,gh,ij,
kl, mn, op, qr,st';

declare @rowDelimiter nvarchar(1) = char(10);
declare @colDelimiter nvarchar(1) = ',';

-- split rows + columns and pivot
with cte_rows as
(
  select 1 as RowNum,
         @data as Data,
         1 as Starts,
         charindex(@rowDelimiter, @data) as Pos
    union all
  select cr.RowNum + 1,
         cr.Data,
         cr.Pos+1,
         charindex(@rowDelimiter, cr.Data, cr.Pos+1)
  from cte_rows cr
  where cr.Pos > 0
),
cte_cols as
(
  select cr.RowNum,
         1 as ColNum,
         substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end) as RowData,
         1 as Starts,
         charindex(@colDelimiter,  substring(cr.data, cr.Starts, case when cr.Pos > 0 then cr.Pos-cr.Starts else len(cr.data) end)) as Pos
  from cte_rows cr
    union all
  select cc.RowNum,
         cc.ColNum + 1,
         cc.RowData,
         cc.Pos+1,
         charindex(@colDelimiter, cc.RowData, cc.Pos+1)
  from cte_cols cc
  where cc.Pos > 0
),
cte_values as
(
  select cc.RowNum,
         cc.ColNum,
         rtrim(ltrim(substring(cc.RowData, cc.Starts, case when cc.Pos>0 then cc.Pos-cc.Starts else len(cc.RowData) end))) as ColVal
  from cte_cols cc
)
select p.RowNum,
       p.[1] as Col1,
       p.[2] as Col2,
       p.[3] as Col3,
       p.[4] as Col4,
       p.[5] as Col5,
       p.[6] as Col6
from cte_values cv
pivot (max(cv.ColVal) for cv.ColNum in ([1], [2], [3], [4], [5], [6])) p
order by p.RowNum;
这使得:

RowNum Col1    Col2    Col3    Col4    Col5    Col6
------ ------- ------- ------- ------- ------- -------
1      ab      cd      ef      gh      ij      
2      kl      mn      op      qr      st      null

我正在尝试将逗号分隔的值字符串转换为一个表。这个想法是相同的,只需将分隔符定义为CHAR10而不是逗号,然后删除CHAR13`characters,或者将CHAR10替换为,然后再次删除CHAR13 characters。SQL Server是一种基于集合的语言,您应该使用基于集合的解决方案。我个人推荐一种基于集合的解决方案,例如真正的基于集合的XML拆分器,甚至是CLR函数。@Larnu我对SQL编码还是相当陌生,所以我认为那篇文章的大部分内容都超出了我的理解范围,但这仍然是一本很棒的读物!谢谢你的链接!很高兴你花时间读了它。我很感激这不是入门级的,但是听到有人在这个网站上花时间学习和做进一步的阅读,真是令人耳目一新;你忘了有些是存在的;我正在尝试将逗号分隔的值字符串转换为一个表。这个想法是相同的,只需将分隔符定义为CHAR10而不是逗号,然后删除CHAR13`characters,或者将CHAR10替换为,然后再次删除CHAR13 characters。SQL Server是一种基于集合的语言,您应该使用基于集合的解决方案。我个人推荐一种基于集合的解决方案,例如真正的基于集合的XML拆分器,甚至是CLR函数。@Larnu我对SQL编码还是相当陌生,所以我认为那篇文章的大部分内容都超出了我的理解范围,但这仍然是一本很棒的读物!谢谢你的链接!很高兴你花时间读了它。我很感激这不是入门级的,但是听到有人在这个网站上花时间学习和做进一步的阅读,真是令人耳目一新;你忘了有些是存在的;