字段值内的分隔符 SQL 服务器
Delimiter within field value SQL Server
我有一个超过2000万行的csv文件,分隔符是竖线。问题是文件中有一个文本列,其中还包括文本中的垂直条,这会弄乱数据并导致在 SQL 服务器中导入 csv 文件时该列转移到下一个列。
文件太大无法处理,例如如果我们想添加限定符或使用花哨的文本编辑器更改分隔符类型。
有什么想法吗?理想情况下,对于此类问题有任何通用解决方案吗?有时虽然您使用限定符,但可能会有包含类似限定符的字符串、分隔符等的文本字段。
这些字段没有被引用。这些行看起来像这样:
field1|field2|field3|field4
1|000|some text|some text
2|001|some text con|taining pipe|some text
3|002|some text|some text
可以访问 bash(Linux/Unix/Cygwin 等)
为了估计问题的严重性,检查具有 4 个字段和其他字段数的记录数。
awk -F'|' '{rec[NF==4?"NF=4":"NF!=4"]++}END{for(nf in rec){print nf,rec[nf]}}' MyFile.csv
生成一个包含良好记录的文件并加载它。
awk -F'|' 'NF==4{print}' MyFile.csv > MyFile_good.csv
生成一个包含错误记录的文件并检查您是否可以手动或以其他方式修复它(如果您识别出模式)
awk -F'|' 'NF!=4{print}' MyFile.csv > MyFile_bad.csv
支持预选赛
"1"|"000"|"some text"|"some text"
"2"|"001"|"some text con|taining pipe"|"some text"
"3"|"002"|"some text"|"some text"
我们现在定义限定字段的外观,而不是定义分隔符 (awk -F'|'
) (FPAT="\"[^\"]*\""
)
awk 'BEGIN{OFS="\t";FPAT="\"[^\"]*\""}{rec[NF==4?"NF=4":"NF!=4"]++}END{for(nf in rec){print nf,rec[nf]}}' MyFile.csv
您可以将整行导入 NVARCHAR(MAX) 列并通过 T-SQL
修复或解析
CREATE TABLE MyCSV (
csv NVARCHAR(MAX) NULL -- VARCHAR(MAX) NULL
)
GO
BULK INSERT MyCSV
FROM 'data_file'
WITH (
DATAFILETYPE = 'widechar' --'char'
,FIELDTERMINATOR = '\r\n'
)
-- OR WITH (FORMATFILE='C:\t_floatformat-c-xml.xml');
GO
/*
INSERT INTO MyCSV
VALUES
('1|000|some text|some text')
,('2|001|some text con|taining pipe|some text')
,('3|002|some text|some text')
*/
ALTER TABLE MyCSV
ADD RowID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
GO
对于解析你可以使用这个函数:
-- SELECT * FROM [dbo].[Split2Column] (N'1|000|some text|some text', N'|')
CREATE FUNCTION [dbo].[Split2Column] (
@String NVARCHAR(MAX),
@SepColumn NCHAR(1)
)
RETURNS @Columns TABLE (
[1] NVARCHAR(MAX)
,[2] NVARCHAR(MAX)
,[3] NVARCHAR(MAX)
,[4] NVARCHAR(MAX)
,[5] NVARCHAR(MAX)
,[6] NVARCHAR(MAX)
,[7] NVARCHAR(MAX)
,[8] NVARCHAR(MAX)
,[9] NVARCHAR(MAX)
,[10] NVARCHAR(MAX)
)
AS
BEGIN
;WITH columns (cn, n1, n2 ) AS (
SELECT CAST(1 as int) as cn, CAST(0 as bigint) as n1, CHARINDEX(@SepColumn, @String + @SepColumn) as n2
UNION ALL
SELECT cn + 1, n2 as n1, CHARINDEX(@SepColumn, @String + @SepColumn, n2 + 1) as n2
FROM columns
WHERE n2 < LEN(@String)
)
INSERT INTO @Columns
SELECT [1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
FROM
(
SELECT cn,
SUBSTRING(@String, n1 + 1, n2 - n1 - 1) as val
FROM columns) parsed
PIVOT (
MIN(val) FOR cn IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
) pvt
OPTION (MAXRECURSION 0);
RETURN;
END
GO
结果如下:
SELECT MyCSV.RowID
,[Split2Column].*
FROM MyCSV
CROSS APPLY [dbo].[Split2Column] (MyCSV.csv, N'|')
--WHERE [Split2Column].[5] IS NOT NULL
ORDER BY MyCSV.RowID
RowID 1 2 3 4 5 6 7 8 9 10
1 1 000 some text some text NULL NULL NULL NULL NULL NULL
2 2 001 some text con taining pipe some text NULL NULL NULL NULL NULL
3 3 002 some text some text NULL NULL NULL NULL NULL NULL
我有一个超过2000万行的csv文件,分隔符是竖线。问题是文件中有一个文本列,其中还包括文本中的垂直条,这会弄乱数据并导致在 SQL 服务器中导入 csv 文件时该列转移到下一个列。
文件太大无法处理,例如如果我们想添加限定符或使用花哨的文本编辑器更改分隔符类型。
有什么想法吗?理想情况下,对于此类问题有任何通用解决方案吗?有时虽然您使用限定符,但可能会有包含类似限定符的字符串、分隔符等的文本字段。
这些字段没有被引用。这些行看起来像这样:
field1|field2|field3|field4
1|000|some text|some text
2|001|some text con|taining pipe|some text
3|002|some text|some text
可以访问 bash(Linux/Unix/Cygwin 等)
为了估计问题的严重性,检查具有 4 个字段和其他字段数的记录数。
awk -F'|' '{rec[NF==4?"NF=4":"NF!=4"]++}END{for(nf in rec){print nf,rec[nf]}}' MyFile.csv
生成一个包含良好记录的文件并加载它。
awk -F'|' 'NF==4{print}' MyFile.csv > MyFile_good.csv
生成一个包含错误记录的文件并检查您是否可以手动或以其他方式修复它(如果您识别出模式)
awk -F'|' 'NF!=4{print}' MyFile.csv > MyFile_bad.csv
支持预选赛
"1"|"000"|"some text"|"some text"
"2"|"001"|"some text con|taining pipe"|"some text"
"3"|"002"|"some text"|"some text"
我们现在定义限定字段的外观,而不是定义分隔符 (awk -F'|'
) (FPAT="\"[^\"]*\""
)
awk 'BEGIN{OFS="\t";FPAT="\"[^\"]*\""}{rec[NF==4?"NF=4":"NF!=4"]++}END{for(nf in rec){print nf,rec[nf]}}' MyFile.csv
您可以将整行导入 NVARCHAR(MAX) 列并通过 T-SQL
修复或解析CREATE TABLE MyCSV (
csv NVARCHAR(MAX) NULL -- VARCHAR(MAX) NULL
)
GO
BULK INSERT MyCSV
FROM 'data_file'
WITH (
DATAFILETYPE = 'widechar' --'char'
,FIELDTERMINATOR = '\r\n'
)
-- OR WITH (FORMATFILE='C:\t_floatformat-c-xml.xml');
GO
/*
INSERT INTO MyCSV
VALUES
('1|000|some text|some text')
,('2|001|some text con|taining pipe|some text')
,('3|002|some text|some text')
*/
ALTER TABLE MyCSV
ADD RowID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
GO
对于解析你可以使用这个函数:
-- SELECT * FROM [dbo].[Split2Column] (N'1|000|some text|some text', N'|')
CREATE FUNCTION [dbo].[Split2Column] (
@String NVARCHAR(MAX),
@SepColumn NCHAR(1)
)
RETURNS @Columns TABLE (
[1] NVARCHAR(MAX)
,[2] NVARCHAR(MAX)
,[3] NVARCHAR(MAX)
,[4] NVARCHAR(MAX)
,[5] NVARCHAR(MAX)
,[6] NVARCHAR(MAX)
,[7] NVARCHAR(MAX)
,[8] NVARCHAR(MAX)
,[9] NVARCHAR(MAX)
,[10] NVARCHAR(MAX)
)
AS
BEGIN
;WITH columns (cn, n1, n2 ) AS (
SELECT CAST(1 as int) as cn, CAST(0 as bigint) as n1, CHARINDEX(@SepColumn, @String + @SepColumn) as n2
UNION ALL
SELECT cn + 1, n2 as n1, CHARINDEX(@SepColumn, @String + @SepColumn, n2 + 1) as n2
FROM columns
WHERE n2 < LEN(@String)
)
INSERT INTO @Columns
SELECT [1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
FROM
(
SELECT cn,
SUBSTRING(@String, n1 + 1, n2 - n1 - 1) as val
FROM columns) parsed
PIVOT (
MIN(val) FOR cn IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
) pvt
OPTION (MAXRECURSION 0);
RETURN;
END
GO
结果如下:
SELECT MyCSV.RowID
,[Split2Column].*
FROM MyCSV
CROSS APPLY [dbo].[Split2Column] (MyCSV.csv, N'|')
--WHERE [Split2Column].[5] IS NOT NULL
ORDER BY MyCSV.RowID
RowID 1 2 3 4 5 6 7 8 9 10
1 1 000 some text some text NULL NULL NULL NULL NULL NULL
2 2 001 some text con taining pipe some text NULL NULL NULL NULL NULL
3 3 002 some text some text NULL NULL NULL NULL NULL NULL