簡體   English   中英

LINQ更新大量記錄的最快方式(> 2m)

[英]LINQ fastest way to update lots of records (>2m)

我有這個循環:

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查詢中。 這個對嗎?

好吧,看到SQL Server支持散列,您可以通過編寫SQL查詢來一次性完成整個表,從而避免將任何數據帶到客戶端:

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

如果散列存儲為字符串,則sys.fn_varbintohexsubstring可能很方便。

以下內容應該更快,因為它將select限制為僅返回所需的列。

更改:

var q = db.tblInternalURLs;

至:

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

更快的方法是使用本機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表並將哈希列轉換為計算列。 在這種情況下,哈希列將始終與URL同步。

ALTER TABLE dbo.tblInternalURLS DROP COLUMN hash

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

我建議將您的查詢分頁。 現在你一次全部撤下所有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)添加對查詢的調用,您應該至少看到一些改進。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM