1

这是我的第一个问题“将“SQL”导出到 T-SQL ”的后续内容。

我正在使用我无法控制且无法更改的第 3 方程序。该程序会将其内部数据库导出为一组,.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 加载的文件,它可能会更快。有没有简单的方法可以做到这一点,或者我必须编写某种查找和替换并保持我的手指交叉,它不会在某些极端情况下失败并炸毁我的数据。

任何其他关于如何加快插入速度的建议也将不胜感激。


更新:

我最终进行了解析并执行了 SqlBulkCopy 方法。它从 1 个文件/分钟开始。1 文件/秒。

4

2 回答 2

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);
    }
}
于 2012-04-03T22:43:50.103 回答
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 —— 尽管可以将所有内容“作为字符串”传递给数据库?-- 然后使用列中的信息来帮助提取过程(可能使用类型转换器)。对于捕获:n为空,w为单词(例如数字),s为字符串。

快乐编码。

于 2012-04-03T23:19:13.590 回答