【问题标题】:SQL - Separate string into columnsSQL - 将字符串分成列
【发布时间】:2016-03-21 13:02:39
【问题描述】:

我正在将一个 csv 文件批量插入 SQL Server 2012。数据当前是 | 管道分隔为每一行的一个长字符串。我想将数据分成每个管道的不同列。

这是导入数据后的样子:

ID|ID2|Person|Person2|City|State
"1"|"ABC"|"Joe"|"Ben"|"Boston"|"MA"
"2"|"ABD"|"Jack"|"Tim"|"Nashua"|"NH"
"3"|"ADC"|"John"|"Mark"|"Hartford"|"CT"

我想将数据分成每个管道的列:

ID  ID2 Person  Person2 City    State
1   ABC  Joe     Ben    Boston   MA
2   ABD  Jack    Tim    Nashua   NH
3   AFC  John    Mark   Hartford CT

我发现使用 charindex and substring functions 很困难,因为数据的列数我也尝试使用 ParseName,因为这是 2012 年的函数,但由于所有列都出来了,所以这也不起作用ParseName 为 NULL

该文件包含大约 300k 行,我找到了使用 xmlname 的解决方案,但速度很慢。即:需要一分钟来分离数据。

这里是慢 xml 解决方案:

CREATE TABLE #tbl(iddata varchar(200))

DECLARE @i int = 0
WHILE @i < 100000 
BEGIN

SET @i = @i + 1

INSERT INTO #tbl(iddata)
SELECT '"1"|"ABC"|"Joe"|"Ben"|"Boston"|"MA"'
UNION ALL
SELECT '"2"|"ABD"|"Jack"|"Tim"|"Nashua"|"NH"'
UNION ALL
SELECT '"3"|"AFC"|"John"|"Mark"|"Hartford"|"CT"'


END


;WITH XMLData 
AS
(
    SELECT idData,
    CONVERT(XML,'<IDs><id>'  
    + REPLACE(iddata,'|', '</id><id>') + '</id></IDs>') AS xmlname
      FROM (
            SELECT REPLACE(iddata,'"','') as iddata
            FROM #tbl
            )x
)

 SELECT xmlname.value('/IDs[1]/id[1]','varchar(100)') AS ID,
       xmlname.value('/IDs[1]/id[2]','varchar(100)') AS ID2,
       xmlname.value('/IDs[1]/id[3]','varchar(100)') AS Person,
       xmlname.value('/IDs[1]/id[4]','varchar(100)') AS Person2,
       xmlname.value('/IDs[1]/id[5]','varchar(100)') AS City,
       xmlname.value('/IDs[1]/id[6]','varchar(100)') AS State
 FROM XMLData

【问题讨论】:

  • 这个问题包含了一个好问题所需要的一切:复制'n'可粘贴的测试数据、输入、预期输出、自己的尝试和一个明确的问题。从我这边投票

标签: sql-server tsql sql-server-2012 csv


【解决方案1】:

这将为您完成。

    CREATE TABLE #Import (
            ID NVARCHAR(MAX),
            ID2 NVARCHAR(MAX),
            Person NVARCHAR(MAX),
            Person2 NVARCHAR(MAX),
            City NVARCHAR(MAX),
            State NVARCHAR(MAX))

        SET QUOTED_IDENTIFIER OFF

        BULK INSERT #Import
        FROM 'C:\MyFile.csv'
        WITH
        (
            FIRSTROW = 2,
            FIELDTERMINATOR = '|',
            ROWTERMINATOR = '\n',
            ERRORFILE = 'C:\myRubbishData.log'
        )

        select * from #Import
        DROP TABLE #Import

不幸的是,使用 BULK INSERT 不会处理文本限定符,因此您最终会得到“ABC”而不是 ABC。

要么从 csv 文件中删除文本限定符,要么在导入数据后对表运行替换。

【讨论】:

  • 您可以使用格式文件来指定文本限定符。
【解决方案2】:

为了避免您不得不处理管道的痛苦和痛苦,我强烈建议您处理输入文件以将这些管道转换为逗号,然后使用 SQL Server 的内置功能将 CSV 解析为表格。

如果您使用的是 Java,替换管道实际上只需要一行代码:

String line = "\"1\"|\"ABC\"|\"Joe\"|\"Ben\"|\"Boston\"|\"MA\"";
line = line.replaceAll("|", ",");
// then write this line back out to file

BULK INSERT YourTable
FROM 'input.csv'
WITH
(
    FIRSTROW = 2,
    FIELDTERMINATOR = ',',
    ROWTERMINATOR = '\n',
    ERRORFILE = 'C:\CSVDATA\SchoolsErrorRows.csv',
    TABLOCK
)

【讨论】:

    【解决方案3】:

    如果您无法使用 BULK 建议(由于权利),您可以通过以下方式将查询速度提高约 30%:

    SELECT AsXml.value('x[1]','varchar(100)') AS ID
          ,AsXml.value('x[2]','varchar(100)') AS ID2
          ,AsXml.value('x[3]','varchar(100)') AS Person
          ,AsXml.value('x[4]','varchar(100)') AS Person2
          ,AsXml.value('x[5]','varchar(100)') AS City
          ,AsXml.value('x[6]','varchar(100)') AS State
    FROM #tbl
    CROSS APPLY(SELECT CAST('<x>' + REPLACE(SUBSTRING(iddata,2,LEN(iddata)-2),'"|"','</x><x>') + '</x>'  AS XML)) AS a(AsXml)
    

    【讨论】:

      猜你喜欢
      • 2017-04-16
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      • 2020-03-28
      • 1970-01-01
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      相关资源
      最近更新 更多