LINQ更新大量记录的最快方式(> 2m)

时间:2013-06-04 16:03:46

标签: c# asp.net sql linq

我有这个循环:

using(var db = new MainContext())
{
    var q = db.tblInternalURLs;
    foreach (var rec in q)
    {
        db.ExecuteCommand("UPDATE tblInternalURLS SET hash = '" + LoginAPI.GetSha1(rec.URL) + "' WHERE ID = " + rec.ID);
    }
}

将更新查询转换为db.ExecuteCommand大大提高了速度,但我想知道是否有更快的方法来执行这些查询,因为它仍然需要很长时间才能超过2,000,000条记录。我相信很多开销都在最初的LINQ查询中。这是对的吗?

4 个答案:

答案 0 :(得分:10)

好吧,看到SQL Server支持散列,您可以通过编写SQL查询来一次性完成整个表格,从而避免将任何数据带到客户端:

update 
 tblInternalURLS 
SET 
 hash = HASHBYTES('SHA1',CONVERT(nvarchar(4000), URL))

如果哈希值存储为字符串,sys.fn_varbintohexsubstring可能会很方便。

答案 1 :(得分:1)

以下内容应该更快,因为它限制select仅返回所需的列。

变化:

var q = db.tblInternalURLs;

要:

var q = db.tblInternalURLs.Select(x => new { URL = x.URL, ID = x.ID }).ToList();

答案 2 :(得分:1)

更快的方法是使用本机ADO.NET Prepare命令然后绑定参数而不是concat查询字符串并生成许多不同的查询(从DB的角度来看)。 每个新查询都必须由服务器解析...

这是片段

var conn = ...//get native connection from your context
var cmd = conn.CreateCommand();
cmd.CommandText = "UPDATE tblInternalURLS SET hash = @hash WHERE ID = @id";

var hashParam = cmd.CreateParameter();
//set parameter type and name

 var idParam = cmd.CreateParameter();
//set parameter type and name

cmd.Parameters.Add(hashParam);
cmd.Parameters.Add(idParam);

//prepare command
cmd.Prepare();

 foreach (var rec in q)
 {
     idParam.Value = rec.ID;
     hashParam.Value =  LoginAPI.GetSha1(rec.URL);
     cmd.ExecuteNonQuery();

 } 

<强>更新
如果您使用的是SQL Server且哈希列必须始终与URL同步,则可以修改tblInternalURLS表并将哈希列转换为 computed 列。在这种情况下,哈希列将始终与URL同步。

ALTER TABLE dbo.tblInternalURLS DROP COLUMN hash

ALTER TABLE dbo.tblInternalURLS 
 ADD hash AS 
 CAST(HASHBYTES('SHA1', URL) AS VARBINARY(20)) PERSISTED

答案 3 :(得分:1)

我建议您对查询进行分页。现在你一次全部撤下所有2,000,000条记录。这是对数据库,网络连接,客户端内存等的消耗。

通过将其分解为几个较小的查询,每个查询,例如几千个,您可能会看到一些显着的改进。

以下是一些用于对给定查询进行分页的帮助程序:

public static IEnumerable<T> Paginate<T>(this IQueryable<T> query, int pageSize)
{
    return GetPages(query, pageSize).SelectMany(x => x);
}

public static IEnumerable<IEnumerable<T>> GetPages<T>(this IQueryable<T> query, int pageSize)
{
    for (int currentPage = 0; true; currentPage++)
    {
        IEnumerable<T> nextPage = query.Skip(currentPage * pageSize)
            .Take(pageSize)
            .ToList();

        if (nextPage.Any())
            yield return nextPage;
        else
            yield break;
    }
}

如果您在查询中添加Paginate(1000)来电,则至少会看到一些改进。

相关问题