【问题标题】:Performing two queries in a single round trip to the database在对数据库的一次往返中执行两个查询
【发布时间】:2016-07-10 06:46:33
【问题描述】:

我有以下代码来执行全文搜索。它创建一个查询,获取该查询返回的总行数,然后仅检索当前页面的实际行数。

// Create IQueryable
var query = from a in ArticleServerContext.Set<Article>()
            where a.Approved
            orderby a.UtcDate descending
            select a;

// Get total rows (needed for pagination logic)
int totalRows = query.Count()

// Get rows for current page
query = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage);

这很好用,但需要两次往返数据库。为了优化代码,有没有办法重新处理这个查询,使它只往返数据库一次?

【问题讨论】:

  • 在我看来不是。当然,如果您不想将所有行都存储在内存中。
  • 我不认为你可以在一次往返中做到这一点,我能想到的唯一方法是重新考虑你的分页工作而不必得到总数
  • 完全同意@JanR。实现这一目标的唯一方法是在一个查询中使用分页。如果结果查询返回null,则没有数据可显示。
  • 您写道,分页逻辑需要totalRows 变量,但您似乎从未使用过它。
  • 一般来说,除非网络有问题 - 我会说可能有其他地方可以优化成本/效益,而不是担心额外的数据库往返。尤其是您可以轻松地缓存这样的值以避免每次都调用它。

标签: c# sql linq iqueryable


【解决方案1】:

是的,您可以通过对数据库的唯一一个查询来执行这两个操作:

// Create IQueryable
var query = from a in ArticleServerContext.Set<Article>()
            where a.Approved
            orderby a.UtcDate descending
            select new { a, Total = ArticleServerContext.Set<Article>().Where(x => x.Approved).Count() };

//Get raw rows for current page with Total(Count) field
var result = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage).ToList();

//this data you actually will use with your logic 
var actualData = result.Select(x => x.a).ToList();

// Get total rows (needed for pagination logic)
int totalRows = result.First().Total;

如果你使用 MSSQL query 会这样看:

SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[UtcDate] AS [UtcDate], 
    [Extent1].[Approved] AS [Approved],     
    [GroupBy1].[A1] AS [C1]
    FROM  [dbo].[Articles] AS [Extent1]
    CROSS JOIN  (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Articles] AS [Extent2]
        WHERE [Extent2].[Approved] ) AS [GroupBy1]
    WHERE [Extent1].[Approved]
    ORDER BY [Extent1].[UtcDate] DESC

【讨论】:

  • 嗯,是的。让我们通过网络提取所有数据只是为了将其丢弃。这样的代码是一些程序非常慢的原因。
  • 所有数据,你确定吗?我只多写一列。并且查询不使用每一行的子查询来计算计数 - 而是与唯一一行进行简单连接。
  • 我看不到您在哪里加载所有数据。不过,我不得不想知道 SQL Server 如何处理计算每一行的整个集合的计数。也许它能够优化它。
  • var result = ...- 这是我加载所有数据的地方。并且 SQL Server 不会为 set 中的每一行单独计算 Count(),它会只计算一次 (SELECT COUNT(1) AS [A1] FROM [dbo].[Articles]),然后将这个数字连接到 set 中的所有行,请仔细查看我的答案末尾的 t-sql 脚本。
  • 这个的主要问题是它不等于原来的 - 你在计算整个表,而原来的计算查询。
【解决方案2】:

我不确定它是否足够值得,但在以下限制条件下是可行的:

(1) CurrentPageRowsPerPage 不受 totalRows 值的影响。
(2) 查询在应用分页参数后实现。

诀窍是使用group by 常量值,EF 支持该值。代码如下所示:

var query = 
    from a in ArticleServerContext.Set<Article>()
    where a.Approved
    // NOTE: order by goes below
    group a by 1 into allRows
    select new
    {
        TotalRows = allRows.Count(),
        PageRows = allRows
            .OrderByDescending(a => a.UtcDate)
            .Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage)
    };

var result = query.FirstOrDefault();
var totalRows = result != null ? result.TotalRows : 0;
var pageRows = result != null ? result.PageRows : Enumerable.Empty<Article>();

【讨论】:

    猜你喜欢
    • 2019-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多