3

我有这个循环:

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

4 回答 4

10

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

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

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

于 2013-06-04T16:16:19.840 回答
1

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

这是片段

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
于 2013-06-04T16:17:20.460 回答
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)您应该至少会看到一些改进。

于 2013-06-04T16:18:43.257 回答
1

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

改变:

var q = db.tblInternalURLs;

至:

var q = db.tblInternalURLs.Select(x => new { URL = x.URL, ID = x.ID }).ToList();
于 2013-06-04T16:13:50.743 回答