SQL - 将字符串分隔为列

时间:2016-03-21 13:02:40

标签: sql-server tsql sql-server-2012 delimited-text

我将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

3 个答案:

答案 0 :(得分:2)

这将为你做。

    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文件中删除文本限定符,或者在导入数据后在表上运行替换。

答案 1 :(得分:1)

为了省去必须处理管道的痛苦和痛苦,我强烈建议您处理输入文件以将这些管道转换为逗号,然后使用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
)

答案 2 :(得分:0)

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