43

我有一个超过 2000 万行的 SQL 表 BookChapters。它有一个聚集的主键 (bookChapterID),没有任何其他键或索引。运行以下查询需要几毫秒

if (select count(*) from BookChapters) = 0
...

但是,像这样更改它需要10多分钟

if (select count(*) from BookChapters) = 1
...

或者

if (select count(*) from BookChapters) > 1
...

这是为什么?我怎样才能select count(*)更快地执行?

4

5 回答 5

58

Mikael Eriksson 很好地解释了为什么第一个查询很快:

SQL server 将其优化为: if exists(select * from BookChapters). 所以它会寻找一行的存在,而不是计算表中的所有行。

对于其他两个查询,SQL Server 将使用以下规则。要执行类似的查询SELECT COUNT(*),SQL Server 将使用最窄的 非聚集索引来计算行数。如果表没有任何非聚集索引,则必须扫描表。

此外,如果您的表具有聚集索引,您可以使用以下查询更快地获得计数(借自此站点Get Row Counts Fast!

--SQL Server 2005/2008
SELECT OBJECT_NAME(i.id) [Table_Name], i.rowcnt [Row_Count]
FROM sys.sysindexes i WITH (NOLOCK)
WHERE i.indid in (0,1)
ORDER BY i.rowcnt desc

--SQL Server 2000
SELECT OBJECT_NAME(i.id) [Table_Name], i.rows [Row_Count]
FROM sysindexes i (NOLOCK)
WHERE i.indid in (0,1)
ORDER BY i.rows desc

它使用 sysindexes 系统表。您可以在此处找到更多信息SQL Server 2000SQL Server 2005SQL Server 2008SQL Server 2012

这是另一个链接为什么我的 SELECT COUNT(*) 运行这么慢?用另一种解决方案。它显示了 Microsoft 用来在您右键单击表格并选择属性时快速显示行数的技术。

select sum (spart.rows)
from sys.partitions spart
where spart.object_id = object_id(’YourTable’)
and spart.index_id < 2

您应该会发现无论您有多少张桌子,它都会很快返回。

如果您使用的是 SQL 2000,您仍然可以使用 sysindexes 表来获取数字。

select max(ROWS)
from sysindexes
where id = object_id(’YourTable’)

根据 SQL 更新 sysindexes 表的频率,这个数字可能略有偏差,但它通常是正确的(或至少足够接近)。

于 2012-06-21T02:01:12.267 回答
16

如果你只想知道行数,试试这个:

exec sp_spaceused [TABLE_NAME]
于 2014-08-01T06:16:18.100 回答
9

如果您查看查询的执行计划,您会看到发生了什么。

查询优化器将您的第一个查询if (select count(*) from BookChapters) = 0识别为与if exists(select * from BookChapters). SQL Server 知道如果至少存在一行,则表达式为真,因此它会查找是否存在一行,而不是计算表中的所有行。

对于您的其他查询,它不可能那么聪明,并且必须先计算表中的行数,然后才能确定表达式的计算结果是真还是假。

于 2012-06-21T06:00:30.347 回答
6

你考虑过查询select count(BookChapterId) from BookChapterTable 吗?- 其中 `BookChapterId是非聚集索引。这应该使它运行得更快。

根据表的使用方式和行的访问方式,查询非聚集索引可能是关键点:我只是从 MDSN 中获取了一些要点:

  • 在创建非聚集索引之前,了解您的数据将如何被访问。考虑将非聚集索引用于:
  • 包含大量不同值的列,例如
    姓氏和名字的组合(如果聚集索引用于其他列)。如果只有很少的不同值,例如
    只有 1 和 0,大多数查询将不会使用索引,因为表
    扫描通常更有效。
  • 不返回大型结果集的查询。

  • 返回完全匹配的查询(WHERE 子句)的搜索条件中经常涉及的列。
  • 经常需要连接和分组的决策支持系统应用程序。在涉及连接和分组操作的列上创建多个非聚集索引,并在任何外键列上创建一个聚集索引。
  • 覆盖给定查询中一个表的所有列。这完全消除了对表或聚集索引的访问。
于 2012-06-21T01:39:11.637 回答
3

试试这个,如果你需要检测,如果表的行多于一个:

if (SELECT COUNT(*) FROM (SELECT TOP 2 * FROM BookChapters) AS b) > 1
于 2014-01-14T09:57:30.787 回答