这是我的第一个问题“Porting “SQL” export to T-SQL”的后续内容。
我正在使用我无法控制的第三方程序,我无法改变。该程序将其内部数据库导出到一组.sql
,每个格式为:
INSERT INTO [ExampleDB] ( [IntField] , [VarcharField], [BinaryField])
VALUES
(1 , 'Some Text' , 0x123456),
(2 , 'B' , NULL),
--(SNIP, it does this for 1000 records)
(999, 'E' , null);
(1000 , 'F' , null);
INSERT INTO [ExampleDB] ( [IntField] , [VarcharField] , BinaryField)
VALUES
(1001 , 'asdg', null),
(1002 , 'asdf' , 0xdeadbeef),
(1003 , 'dfghdfhg' , null),
(1004 , 'sfdhsdhdshd' , null),
--(SNIP 1000 more lines)
此模式一直持续到.sql
文件达到导出期间设置的文件大小,导出文件按EXPORT_PATH\%Table_Name%\Export#.sql
分组,其中#是从1开始的计数器。
目前我有大约1.3GB的数据,我有1MB的块导出(26个表中有1407个文件,除了5个表外只有一个文件,最大的表有207个文件)。
现在我只有一个简单的C#程序,它将每个文件读入ram,然后调用ExecuteNonQuery。问题是我平均每秒60秒/文件,这意味着它需要大约23小时来完成整个导出。
我假设如果我有一些如何格式化要加载BULK INSERT而不是INSERT INTO的文件,它可以更快。有没有简单的方法可以做到这一点,还是我必须写一些Find& amp;更换并保持我的手指交叉,它在一些角落的情况下不会失败并炸毁我的数据。
关于如何加速插入的任何其他建议也将不胜感激。
更新
我最终选择了parse and do a SqlBulkCopy method。它从1档/分钟开始。到1档/秒。
答案 0 :(得分:1)
显然,您的数据始终包含在括号中,并以左括号开头。您可能希望将此规则用于split
(RemoveEmptyEntries
)每个行并将其加载到DataTable中。然后,您可以使用SqlBulkCopy
一次性将所有内容复制到数据库中。
这种方法不一定是故障安全的,但肯定会更快。
编辑:以下是获取每个表格架构的方式:
private static DataTable extractSchemaTable(IEnumerable<String> lines)
{
DataTable schema = null;
var insertLine = lines.SkipWhile(l => !l.StartsWith("INSERT INTO [")).Take(1).First();
var startIndex = insertLine.IndexOf("INSERT INTO [") + "INSERT INTO [".Length;
var endIndex = insertLine.IndexOf("]", startIndex);
var tableName = insertLine.Substring(startIndex, endIndex - startIndex);
using (var con = new SqlConnection("CONNECTION"))
{
using (var schemaCommand = new SqlCommand("SELECT * FROM " tableName, con))
{
con.Open();
using (var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
{
schema = reader.GetSchemaTable();
}
}
}
return schema;
}
然后,您只需要迭代文件中的每一行,检查它是否以(
开头并将该行拆分为Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
。然后,您可以将生成的数组添加到创建的模式表中。
这样的事情:
var allLines = System.IO.File.ReadAllLines(path);
DataTable result = extractSchemaTable(allLines);
for (int i = 0; i < allLines.Length; i++)
{
String line = allLines[i];
if (line.StartsWith("("))
{
String data = line.Substring(1, line.Length - (line.Length - line.LastIndexOf(")")) - 1);
var fields = data.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
// you might need to parse it to correct DataColumn.DataType
result.Rows.Add(fields);
}
}
答案 1 :(得分:1)
嗯,这是我的“解决方案”,用于帮助将数据转换为DataTable或其他方式(在LINQPad中运行):
var i = "(null, 1 , 'Some''\n Text' , 0x123.456)";
var pat = @",?\s*(?:(?<n>null)|(?<w>[\w.]+)|'(?<s>.*)'(?!'))";
Regex.Matches(i, pat,
RegexOptions.IgnoreCase | RegexOptions.Singleline).Dump();
每个值组应运行一次匹配(例如(a,b,etc)
)。解析结果(例如转换)留给调用者,我没有测试它[很多]。我建议首先创建正确类型的DataTable - 尽管可以将所有“作为字符串”传递给数据库吗? - 然后使用列中的信息来帮助提取过程(可能使用type converters)。对于捕获:n
为空,w
为字(例如数字),s
为字符串。
快乐的编码。