我正在使用 SqlBulkCopy 以编程方式将数据从 Excel 导出到 SQL Server 2005。它很好用,我唯一的问题是它没有保留我在 Excel 文件中的行序列。我没有要排序的列,我只想按照它们在 Excel 电子表格中出现的顺序插入记录。
我无法修改 Excel 文件,必须使用我所拥有的。按任何现有列排序将破坏序列。
请帮忙。
PS 最终将 ID 列插入电子表格,看起来在导出/导入期间无法保持订单
我正在使用 SqlBulkCopy 以编程方式将数据从 Excel 导出到 SQL Server 2005。它很好用,我唯一的问题是它没有保留我在 Excel 文件中的行序列。我没有要排序的列,我只想按照它们在 Excel 电子表格中出现的顺序插入记录。
我无法修改 Excel 文件,必须使用我所拥有的。按任何现有列排序将破坏序列。
请帮忙。
PS 最终将 ID 列插入电子表格,看起来在导出/导入期间无法保持订单
我不认为 SQL 指定或保证行排序,除非您使用“ORDER BY”子句。
来自 Bill Vaughn 的一篇文章(http://betav.com/blog/billva/2008/08/sql_server_indexing_tips_and_t.html):
使用 Order By:即使表具有聚集索引(以物理顺序存储数据),SQL Server 也不保证行将按该(或任何特定)顺序返回,除非使用 ORDER BY 子句。
另一个信息链接:
http://sqlblogcasts.com/blogs/simons/archive/2007/08/21/What-is-the-position-of-a-row--.aspx
经过大量研究后,显然没有办法使用 Microsoft 提供的 Bulk Insert 命令来保留行顺序。您必须自己将 ID 列直接添加到导入文件中,使用 shell 或其他外部脚本,或者不这样做。微软似乎需要(而且很容易)添加一个功能,但是在他们十多年没有任何东西之后,它不会发生。
然而,我需要在导入后保留导入文件中的实际记录顺序,因为如果一组列具有相同的值,较高的记录将取代较低的记录。
于是我走了一条不同的路。我的限制是:
我喜欢使用 Powershell 为每一行创建有序插入语句,然后在 Sql 中运行的逻辑。它本质上是将每条记录排队等待单独插入而不是批量插入。是的,它会起作用,但它也会很慢。我经常有包含 500K+ 行的文件。我需要快速的东西。
所以我遇到了 XML。将文件直接批量上传到单个 XML 变量中。这将保留记录的顺序,因为每个记录都添加到 XML。然后解析 XML 变量并将结果插入到表中,同时添加一个标识列。
假设导入文件是标准文本文件,每条记录以换行符结尾 (Char(13)+Char(10))
我的方法有两个步骤:
执行 IMPORT SQL 语句(使用 OPENROWSET),用 XML 标记封装每条记录。将结果捕获到 XML 变量中。
通过 XML 标记将变量解析为表,添加递增的 [ID] 列。
---------------------------------
Declare @X xml;
---------------------------------
SELECT @X=Cast('<X>'+Replace([BulkColumn],Char(13)+Char(10),'</X><X>')+'</X>' as XML)
FROM OPENROWSET (BULK N'\\FileServer\ImportFolder\ImportFile_20170120.csv',SINGLE_CLOB) T
---------------------------------
SELECT [Record].[X].query('.').value('.','varchar(max)') [Record]
,ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [ID]
--Into #TEMP
FROM @X.nodes('X') [Record](X);
---------------------------------
XML 标记替换每个换行符。
如果文件以换行结尾,这将导致在末尾添加一个空白行。只需删除最后一行。
我使用动态 sql 将其写入我的过程中,因此我可以传入 FileName 并将 ID 设置为从 1 或 0 开始(以防有标题行)。
我能够在大约 5 秒内针对 300K 记录的文件运行它。
如果您可以将 excel 电子表格保存为 CSV,则很容易使用任何脚本语言生成一个 INSERT 语句列表,这些语句将以与电子表格完全相同的顺序执行。这是 Groovy 中的一个简单示例,但任何脚本语言都可以轻松地做到这一点,如果不是更容易的话:
def file1 = new File('c:\\temp\\yourSpreadsheet.csv')
def file2 = new File('c:\\temp\\yourInsertScript.sql')
def reader = new FileReader(file1)
def writer = new FileWriter(file2)
reader.transformLine(writer) { line ->
fields = line.split(',')
text = """INSERT INTO table1 (col1, col2, col3) VALUES ('${fields[0]}', '${fields[1]}', '${fields[2]}');"""
}
然后,您可以针对您的数据库执行“yourInsertScript.sql”,您的订单将与您的电子表格相同。
您还可以在表中定义一个在数据加载期间自动递增的标识列。这样,当您希望记录再次以相同顺序排列时,您可以对其进行排序。