0

我们假设没有为表 T 定义主键。在这种情况下,如何快速/有效地为这些数据库(Oracle 11g、MySql、Mssql)计算 T 中的所有行?

似乎 count(*) 和 count(column_name) 分别可能很慢且不准确。以下似乎是最快和最可靠的方法 -

select count(rowid) from MySchema.TableInMySchema;

你能告诉我上面的说法是否也有任何不足之处吗?如果它很好,那么我们是否对 mysql 和 mssql 有类似的语句?

提前致谢。

来源 - http://www.thewellroundedgeek.com/2007/09/most-people-use-oracle-count-function.html

4

3 回答 3

3

count(column_name)并非准确,它只是与 count(*) 完全不同的东西。

SQL 标准定义count(column_name)为等效于count(*) where column_name IS NOT NULL. 如果 column_name 可以为空,结果必然会有所不同。

在 Oracle(可能还有其他 DBMS)中,count(*)将使用非空列上的可用索引来计算行数(例如 PK 索引)。所以它会一样快

此外,没有什么类似于 SQL Server 或 MySQL 中的 rowid (在 PostgreSQL 中应该是ctid)。

使用count(*). 这是获取行数的最佳选择。如果有足够的索引可用,则让 DBMS 在后台进行任何优化。

编辑

关于 Oracle 如何自动使用索引(如果可用)以及如何减少数据库完成的工作量的快速演示:

测试台的设置:

create table foo (id integer not null, c1 varchar(2000), c2 varchar(2000));
insert into foo (id, c1, c2)
select lvl, c1, c1 from 
(
  select level as lvl, dbms_random.string('A', 2000) as c1
  from dual 
  connect by level < 10000
);

这会生成 10000 行,每行填充一些空间,以确保表具有实际大小。

现在在 SQL*Plus 中,我运行以下命令:

SQL> set autotrace traceonly 解释统计;
SQL> 从 foo 中选择 count(*);


执行计划
-------------------------------------------------- --------
计划哈希值:1342139204

-------------------------------------------------- -----------------
| 身份证 | 操作 | 姓名 | 行 | 成本 (%CPU)| 时间 |
-------------------------------------------------- -----------------
| 0 | 选择声明 | | 1 | 2740 (1)| 00:00:33 |
| 1 | 排序聚合 | | 1 | | |
| 2 | 表访问已满| 福 | 9999 | 2740 (1)| 00:00:33 |
-------------------------------------------------- -----------------


统计数据
-------------------------------------------------- --------
        181 次递归调用
          0 db 块获取
      10130 一致获得
          0 次物理读取
          0 重做大小
        通过 SQL*Net 向客户端发送 430 字节
        通过 SQL*Net 从客户端收到 420 字节
          2 次 SQL*Net 往返客户端
          5种(记忆)
          0 种(磁盘)
          已处理 1 行

SQL>

如您所见,对需要 10130 次“IO 操作”的表进行全表扫描(我知道这不是正确的术语,但为了演示,对于从未见过的人来说,这应该是一个足够好的解释前)

现在我在该列上创建一个索引并再次运行 count(*):

SQL> 在 foo (id) 上创建索引 i1;

已创建索引。

SQL> 从 foo 中选择 count(*);


执行计划
-------------------------------------------------- --------
计划哈希值:129980005

-------------------------------------------------- --------------------
| 身份证 | 操作 | 姓名 | 行 | 成本 (%CPU)| 时间 |
-------------------------------------------------- --------------------
| 0 | 选择声明 | | 1 | 7 (0)| 00:00:01 |
| 1 | 排序聚合 | | 1 | | |
| 2 | 索引快速全扫描| I1 | 9999 | 7 (0)| 00:00:01 |
-------------------------------------------------- --------------------


统计数据
-------------------------------------------------- --------
          1 递归调用
          0 db 块获取
         27连胜
         21 次物理读取
          0 重做大小
        通过 SQL*Net 向客户端发送 430 字节
        通过 SQL*Net 从客户端收到 420 字节
          2 次 SQL*Net 往返客户端
          0 种(内存)
          0 种(磁盘)
          已处理 1 行

SQL>

正如您所看到的,Oracle 确实使用了 (not null!) 列上的索引,并且 IO 的数量急剧下降(从 10130 到 27 - 这不是我所说的“非常低效”)。

“物理读取”源于索引刚刚创建且尚未在缓存中的事实。

我希望其他 DBMS 应用相同的优化。

于 2012-08-26T08:03:11.023 回答
1

在 Oracle 中,COUNT(*)效率最高。实际上,COUNT(rowid)COUNT(1)COUNT('fuzzy bunny')可能同样有效。但是如果有区别,COUNT(*)效率会更高。

于 2012-08-26T07:59:32.287 回答
0

曾经使用SELECT COUNT(1) FROM anything;, 而不是星号...

有些人认为,mysql 使用星号来调用查询优化器,并在使用“1”作为静态标量时忽略任何优化......

恕我直言,这是直截了当的,因为您不使用任何变量,而且很明显,您只计算所有行。

于 2012-08-26T07:59:07.050 回答