将一个完整的“INSERT INTO xxx VALUES”文件转换为Bulk Insert可以解析的内容

时间:2012-04-03 22:20:18

标签: c# sql sql-server-2008 bulkinsert insert-into

这是我的第一个问题“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档/秒。

2 个答案:

答案 0 :(得分:1)

显然,您的数据始终包含在括号中,并以左括号开头。您可能希望将此规则用于splitRemoveEmptyEntries)每个行并将其加载到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为字符串。

快乐的编码。