【问题标题】:how to return number of records efficiently in SQL?如何在 SQL 中有效地返回记录数?
【发布时间】:2011-04-08 14:46:21
【问题描述】:

我在 Windows Server 2008 Enterprise 上使用 SQL Server 2008 Enterprise。我正在使用以下代码返回查询的一部分数据以在我的 Web 应用程序(我使用 pageCount 作为数字每页显示的结果个数,startPos 为结果的起始编号)。例如,pageCount 10 表示每页显示 10 个结果,startPos = 0 表示第一页,startPos = 1 表示第二页,以此类推

我的问题是如何在我的场景中有效地获得结果总数?我主要关心的是如何实现分页(即只触摸结果的一部分)并同时检索结果总数?

SELECT *
 FROM   (SELECT
    t.foo, t.goo, ROW_NUMBER() OVER (order by t.zoo DESC ) AS rowNum
   FROM
    dbo.mycorp  t

   WHERE
    (t.foo LIKE '%'+@search+'%'
    or t.foo  LIKE '%'+@search+'%'
    )
    ) tt
    WHERE  tt.rowNum between @startPos and  @pageCount + @startPos-1

提前致谢, 乔治

【问题讨论】:

  • 如果你的 SQL 查询包含LIKE '%something%' :-)
  • 这里有一篇很好的文章比较了计算分页总数的技术:sqlservercentral.com/articles/paging/70120
  • 我的意思是在like 子句中使用%xx% 几乎肯定会阻止使用索引来定位行。这意味着全表(或索引,如果列被索引)扫描,这是一个性能杀手。
  • 感谢@paxdiablo,如果不使用该模式,您如何实现具有“包含”功能的查询?我要搜索 aaa 列中包含“xxx”的所有记录?
  • 对于任意“包含”,这很困难。对于单词边界“包含”,您可以将字段拆分为单词,丢弃干扰词('and'、'or'、'the' 等)并将其他单词存储在引用该行的单独表中.然后,您只需在单独的表中查询特定单词。这可以作为插入/更新触发器来提高效率:请参阅stackoverflow.com/questions/3521627/…

标签: sql sql-server tsql sql-server-2008 pagination


【解决方案1】:

这意味着一个冗余列,每一行的值都相同:

WITH cte AS (
    SELECT t.foo, 
           t.goo, 
           ROW_NUMBER() OVER (ORDER BY t.zoo DESC ) AS rowNum,
           (SELECT COUNT(*)
              FROM dbo.mycorp
             WHERE t.foo LIKE '%'+@search+'%'
                OR t.foo  LIKE '%'+@search+'%') AS total_count
      FROM dbo.mycorp  t
     WHERE t.foo LIKE '%'+@search+'%'
        OR t.foo  LIKE '%'+@search+'%')
SELECT c.foo,
       c.goo,
       c.total_count
  FROM cte c
 WHERE c.rowNum BETWEEN @startPos 
                    AND @pageCount + @startPos-1

一种更有效的文本搜索方式是使用SQL Server's Full Text Search (FTS) functionality

【讨论】:

  • +1 分 - 你不认为你还需要将 where 子句添加到 total_count 查询吗?
  • @In Sane:本来不打算包含它,但后来决定提及全文搜索...已更新。
  • @George2:我不明白你在说什么——我在 40 多分钟前将 WHERE 过滤添加到 COUNT 部分。
  • @George2:如果这就是您所需要的,请获取从结果集中返回的行数。
  • @George2:全文搜索 (FTS) 功能可以返回更有效的结果,但主要是 FTS 比您在问题中提供的方法更有效。
【解决方案2】:

如果您的数据访问技术支持,我会考虑使用多个记录集(我知道 ADO.NET 确实支持这一点)。

在查询后添加以下内容

SELECT COUNT(*) AS TotalRecordCount
 FROM   dbo.mycorp  t 
   WHERE 
    (t.foo LIKE '%'+@search+'%' 
    or t.foo  LIKE '%'+@search+'%' 
    ) 
    ) tt

在您的数据访问层中,您需要在处理完搜索结果后切换到下一个记录集以检索总计数。使用 ADO.NET,这将需要调用 dataReader.NextResult()。

最重要的是衡量绩效。我发现在某些 情况下,使用单独的选择语句而不是子查询可以提高性能,因为查询优化器能够更好地优化查询。无论哪种方式,您都需要在搜索列上建立适当的索引。

【讨论】:

  • 在 like 语句中使用 % 作为开始字符时对列进行索引如何帮助列变得可搜索?
  • 谢谢 Teevus,我注意到你在代码中添加了 count,我不确定它是否会影响性能,因为我认为 count(*) 会触及所有结果以计算总结果数,但是我只需要部分结果来实现分页(所以,我原来的帖子只会触及部分结果)。与我以前的解决方案相比,我担心您的解决方案会影响性能,因为它涉及所有结果。有cmets吗?如果我错了,请随时纠正我。
  • 您提到“在查询后包含以下内容”,您的意思是添加另一个选择还是子查询?为了减少沟通误会,能否把代码全部贴出来?
  • @daytona250:在 like 语句的前面使用 % 将无法以有效的方式使用索引(例如索引查找),但仍然可以使用索引扫描,这是比表扫描要好得多。
  • @George2,我的意思是在现有选择之后添加另一个选择,而不是子查询。至于涉及所有记录的 count() 语句,除非查询优化器决定进行表扫描,否则这种情况极不可能发生,如果您在 foo 列上有适当的索引,则不应该发生这种情况。因为它与您的 select 语句具有相同的“where”子句,所以 select count() 语句很可能会使用相同的索引,甚至可能会找到更好的索引,因为它不需要从任何mycorp 表上的列。
猜你喜欢
  • 2011-04-12
  • 2018-02-15
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 2010-11-13
相关资源
最近更新 更多