C#+ Sql Server - 执行大量存储过程。最好的办法?

时间:2009-06-29 15:36:35

标签: c# sql-server sql-server-2005 stored-procedures

我有一个存储过程将数据插入3个表(UPSERTS),并且有一些逻辑。 (IF-THEN-ELSE)

我需要使用不同的参数执行此Sproc数百万次(来自C#应用程序),我需要它快速。

最好的方法是什么?

除了Lucene或Sql Server FTS之外,有没有人知道开源(或不是)现成的文档索引器?

*我正在尝试构建一个文档word-index。对于文档中的每个单词,我将单词,docID和单词位置插入DB中。

例如,对于100个文档,这发生了100000次。

The Sproc :要插入3个表,每个表都要进行一次UPSERT。

C#app

using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();
                SqlTransaction trans = con.BeginTransaction();
                SqlCommand command = new SqlCommand("add_word", con, trans);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                string[] TextArray;
                for (int i = 0; i < Document.NumberOfFields; i++)
                {
                  ...
                 Addword(..., command);  <---- this updates parameters with new values and ExecuteNonQuery.
                }

            }

我忘了提一下,这段代码在Sql Server中产生死锁。我不知道为什么会这样。

10 个答案:

答案 0 :(得分:3)

  1. 删除正在加载的表上的所有索引,然后在加载完成后重新添加它们。这将阻止每次更改的大量颠簸/重新索引。

  2. 确保数据库在加载之前已经分配了足够的物理文件空间,因为在加载时不必花时间从文件系统中不断地抓取它。通常,数据库设置为在完全时增长10%,此时sql server会阻止查询,直到分配更多空间。当你加载你正在谈论的数据量时,sql将不得不做很多阻塞。

  3. 如果可能,请查看批量加载/批量复制。

  4. 在代码中执行所有IF THEN ELSE逻辑。只需将存储的实际值发送到s'proc即可。你甚至可以运行两个线程。一个用于评估数据并将其排队,另一个用于将队列写入DB服务器。

  5. 查看Off The Shelf程序,这些程序完全按照索引文档进行操作。他们很可能已经解决了这些问题。

  6. 尽可能摆脱交易要求。尽量保持s'proc调用尽可能简单。

  7. 查看您是否可以限制要存储的字词。例如,如果您不关心单词“it”,“as”,“I”等,则在调用s'proc之前将其过滤掉。

答案 1 :(得分:2)

如果要快速从C#批量INSERT数据,请查看SqlBulkCopy类(从.NET 2.0开始)。

答案 2 :(得分:1)

这可能是一个非常通用的要求 - 为了使程序快速,我们需要查看它并了解你的db-schema。

另一方面,如果你想知道尽可能快地执行相同(非优化或优化)程序的最佳方法,通常最好的方法是做某种缓存< / strong>在客户端上,尽可能少地调用该过程来批量操作。

如果这是循环,人们通常做的是 - 而不是每次迭代调用过程 - 构建/填充一些缓存数据结构,当循环退出时(或任何给定数量的循环)将调用存储过程如果你需要更经常地发生这种情况)批处理你缓存的操作(即你可以将一个xml字符串传递给你的sp然后解析它,将这些东西放在临时表中然后从那里去 - 你可以节省一个整体很多这样的开销)。

另一种常见的解决方案是使用SqlServer 批量操作。

回到存储过程 - 请注意优化T-SQL和db-schema(带索引等)可以对您的性能产​​生光辉的影响。

答案 3 :(得分:1)

这似乎是一种基本的方法,但它应该有效,而且应该很快。您可以使用SQL语句列表生成一个巨大的文本文件,然后从命令行运行它。如果我没有弄错,应该可以使用GO语句批处理命令。或者,您可以直接从应用程序将多个SQL命令连接为字符串并批量执行它们。看起来你要做的是一次性任务,数据不是直接作为用户输入。所以你应该能够自己处理逃避。

我确信有更复杂的方法可以做到这一点(SqlBulkCopy看起来是一个好的开始),所以请将此视为一个建议。我会花一些时间来调查是否有更优雅的方式更好的方法。

另外,我会确保存储过程中的逻辑尽可能简单,并且表中没有任何索引。它们应该在以后添加。

答案 4 :(得分:1)

尝试使用XML来做到这一点。

你只需执行一次:

示例:

DECLARE @XMLDoc XML
SET @XMLDoc = '<words><word>test</word><word>test2</word></words>'

CREATE PROCEDURE add_words
(
    @XMLDoc XML
)
AS

DECLARE @handle INT

EXEC sp_xml_preparedocument @handle OUTPUT, @XMLDoc

INSERT INTO TestTable
SELECT * FROM OPENXML (@handle, '/words', 2) WITH 
  (
    word varchar(100)
  )
EXEC sp_xml_removedocument @handle

答案 5 :(得分:0)

如果您正在尝试优化速度,请考虑简单地升级SQL Server硬件。在服务器中放置一些RAM和超快的RAID可能是提高查询速度的最具成本效益的长期解决方案。与开发人员时间相比,硬件相对便宜。

听取杰夫阿特伍德的话:

Coding Horror: Hardware is Cheap, Programmers are Expensive

答案 6 :(得分:0)

在这种情况下,与数据库的通信可能是瓶颈,特别是如果数据库在另一台机器上。我建议将整个文档发送到数据库并编写一个将其拆分为单词的sproc,或者使用sql-server托管的托管代码。

答案 7 :(得分:0)

假设这是一个不会在多个用户之间发生争用的应用,请尝试使用此方法:

  • 将参数插入为此目的设置的表格
  • 将您的SP更改为循环遍历该表并在每行上执行其工作
  • 拨打SP一次
  • 让SP在完成时截断输入表

这将消除调用SP数百万次的开销,并且可以连接表中参数的插入(“INSERT INTO foo(v)VALUE('bar'); INSERT INTO foo(v)VALUE ('bar2'); INSERT INTO foo(v)VALUE('bar3');“)。

缺点:SP需要很长时间才能执行,并且不会有任何进度反馈,这不是非常用户友好的。

答案 8 :(得分:0)

要将大量数据移动到服务器,如果您在2008年,请使用SqlBulkCopy或表值参数。如果您需要速度,请不要每行执行一次存储过程,开发一个基于集合的处理所有数据的集合(或一大批)行。

答案 9 :(得分:-1)

- 自问题编辑以来编辑过。

最大的问题是确保正确调整存储过程。您的C#代码与您获得它的速度一样快。