count(*)、count(val)和count(1)的解释

博客主要探讨了count(*)、count(val)和count(1)的性能问题。针对关于count的一些谣言,通过创建表、建立索引、设置列非空等实验进行验证。得出优化器中列偏移量决定性能,count(*)算法与列偏移量无关,所以count(*)最快,count(最后列val)最慢的结论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

count(*)、count(val)和count(1)的解释

一、关于count的一些谣言:

    1、count(*)比count(val)更慢!项目组必须用count(val),不准用count(*),谁用扣谁钱!

    2、count(*)用不到索引,count(val)才能用到。

    3、count(*)是统计出全表的记录,是吞吐量的操作,肯定用不到索引。

    4、count(1)比count(*)的速度快。

二、验证count(*)和count(val)

    1、首先创建一个表,使用count(*)和count(val)查询比较:

复制代码

----删除echo表----
SQL> drop table echo purge;
drop table echo purge
           *
第 1 行出现错误:
ORA-00942: 表或视图不存在

----创建一张echo的测试表----
SQL> create table echo as select * from dba_objects;

表已创建。

SQL> update echo set object_id =  rownum;

已更新72509行。

SQL> commit;

提交完成。

SQL> set timing on
SQL> set linesize 100
SQL> set autotrace on
SQL> select count(*) from echo;

  COUNT(*)
----------
     72509

已用时间:  00: 00: 00.01

执行计划
----------------------------------------------------------
Plan hash value: 99109176

-------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Cost (%CPU)| Time      |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    1 |   290   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |    1 |           |      |
|   2 |   TABLE ACCESS FULL| ECHO | 80064 |   290   (1)| 00:00:04 |
-------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      4  recursive calls
      0  db block gets
       1265  consistent gets
      0  physical reads
      11060  redo size
    425  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

SQL> select count(*) from echo;

  COUNT(*)
----------
     72509

已用时间:  00: 00: 00.01

执行计划
----------------------------------------------------------
Plan hash value: 99109176

-------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Cost (%CPU)| Time      |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    1 |   290   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |    1 |           |      |
|   2 |   TABLE ACCESS FULL| ECHO | 80064 |   290   (1)| 00:00:04 |
-------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      0  recursive calls
      0  db block gets
       1038  consistent gets
      0  physical reads
      0  redo size
    425  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

SQL> select count(object_id) from echo;

COUNT(OBJECT_ID)
----------------
       72509

已用时间:  00: 00: 00.01

执行计划
----------------------------------------------------------
Plan hash value: 99109176

---------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time      |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    1 |    13 |   290   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |    1 |    13 |           |      |
|   2 |   TABLE ACCESS FULL| ECHO | 80064 |  1016K|   290   (1)| 00:00:04 |
---------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      4  recursive calls
      0  db block gets
       1112  consistent gets
      0  physical reads
      0  redo size
    433  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

SQL> select count(object_id) from echo;

COUNT(OBJECT_ID)
----------------
       72509

已用时间:  00: 00: 00.01

执行计划
----------------------------------------------------------
Plan hash value: 99109176

---------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time      |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    1 |    13 |   290   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |    1 |    13 |           |      |
|   2 |   TABLE ACCESS FULL| ECHO | 80064 |  1016K|   290   (1)| 00:00:04 |
---------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      0  recursive calls
      0  db block gets
       1038  consistent gets
      0  physical reads
      0  redo size
    433  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

复制代码

     从上面的执行计划可以看出count(*)和count(val)是一样快的。

    2、建立索引做比较

复制代码

SQL> create index idx_object_id on echo(object_id);

索引已创建。

已用时间:  00: 00: 05.69
SQL> select count(*) from echo;

  COUNT(*)
----------
     72509

已用时间:  00: 00: 00.05

执行计划
----------------------------------------------------------
Plan hash value: 99109176

-------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Cost (%CPU)| Time      |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    1 |   290   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |    1 |           |      |
|   2 |   TABLE ACCESS FULL| ECHO | 80064 |   290   (1)| 00:00:04 |
-------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      5  recursive calls
      0  db block gets
       1113  consistent gets
      0  physical reads
      0  redo size
    425  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

SQL> select count(object_id) from echo;

COUNT(OBJECT_ID)
----------------
       72509

已用时间:  00: 00: 00.08

执行计划
----------------------------------------------------------
Plan hash value: 1131838604

---------------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |           |     1 |    13 |    49    (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |           |     1 |    13 |        |          |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 |  1016K|    49    (0)| 00:00:01 |
---------------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      4  recursive calls
      0  db block gets
    244  consistent gets
    161  physical reads
      0  redo size
    433  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

复制代码

    哇,原来真的是用count(val)比count(*)要快啊,因为count(*)不能用到索引,而count(val)可以,真相真是如此吗?
    3、将object_id设置为非空

复制代码

SQL> alter table echo modify object_id not null;

表已更改。

已用时间:  00: 00: 01.41

SQL> select count(*) from echo;

  COUNT(*)
----------
     72509

已用时间:  00: 00: 00.00

执行计划
----------------------------------------------------------
Plan hash value: 1131838604

-------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |           |     1 |    49    (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |           |     1 |        |          |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 |    49    (0)| 00:00:01 |
-------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      0  recursive calls
      0  db block gets
    169  consistent gets
      0  physical reads
      0  redo size
    425  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

SQL> select count(object_id) from echo;

COUNT(OBJECT_ID)
----------------
       72509

已用时间:  00: 00: 00.01

执行计划
----------------------------------------------------------
Plan hash value: 1131838604

-------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |           |     1 |    49    (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |           |     1 |        |          |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 80064 |    49    (0)| 00:00:01 |
-------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
      0  recursive calls
      0  db block gets
    169  consistent gets
      0  physical reads
      0  redo size
    433  bytes sent via SQL*Net to client
    415  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

复制代码

--看来count(val)和count(*)其实一样快,如果索引列是非空的,count(*)可用到索引,此时一样快!真相真是如此吗?
其实两者根本没有可比性,性能比较首先考虑写法等价,这两个语句根本就不等价。

结论:

    其实优化器里的算法是这么玩的,列的偏移量决定性能,列越靠后,访问的开销越大。由于count(*)的算法与列偏移量无关,所以count(*)最快,count(最后列val)最慢。

    3、用实验验证上面的结论:

复制代码

SQL> set serveroutput on
SQL> set echo on
SQL> drop table t purge;

表已删除。
----构造出有25个字段的表T----
DECLARE
  l_sql VARCHAR2(32767);
BEGIN
  l_sql := 'CREATE TABLE t (';
  FOR i IN 1..25 
  LOOP
    l_sql := l_sql || 'n' || i || ' NUMBER,';
  END LOOP;
  l_sql := l_sql || 'pad VARCHAR2(1000)) PCTFREE 10';
  EXECUTE IMMEDIATE l_sql;
END;
 12  /

PL/SQL 过程已成功完成。
----将记录还有这个表T中填充----
DECLARE
  l_sql VARCHAR2(32767);
BEGIN
  l_sql := 'INSERT INTO t SELECT ';
  FOR i IN 1..25
  LOOP
    l_sql := l_sql || '0,';
  END LOOP;
  l_sql := l_sql || 'NULL FROM dual CONNECT BY level <= 10000';
  EXECUTE IMMEDIATE l_sql;
  COMMIT;
END;
 13  /

PL/SQL 过程已成功完成。

execute dbms_stats.gather_table_stats(ownname=>user, tabname=>'t')
SELECT num_rows, blocks FROM user_tables WHERE table_name = 'T';

PL/SQL 过程已成功完成。

SQL> 
  NUM_ROWS     BLOCKS
---------- ----------
     10000       80
----以下动作观察执行速度,比较发现count(*)最快,count(最大列)最慢----
SQL> DECLARE
  l_dummy PLS_INTEGER;
  l_start PLS_INTEGER;
  l_stop PLS_INTEGER;
  l_sql VARCHAR2(100);
BEGIN
  l_start := dbms_utility.get_time;
  FOR j IN 1..1000
  LOOP
    EXECUTE IMMEDIATE 'SELECT count(*) FROM t' INTO l_dummy;
  END LOOP;
  l_stop := dbms_utility.get_time;
  dbms_output.put_line((l_stop-l_start)/100);
 14  
  FOR i IN 1..25
  LOOP
    l_sql := 'SELECT count(n' || i || ') FROM t';
    l_start := dbms_utility.get_time;
    FOR j IN 1..1000
    LOOP
      EXECUTE IMMEDIATE l_sql INTO l_dummy;
    END LOOP;
    l_stop := dbms_utility.get_time;
    dbms_output.put_line((l_stop-l_start)/100);
  END LOOP;
END;
 27  /
.18
.33
.39
.38
.42
.4
.45
.49
.48
.46
.48
.48
.55
.51
.56
.57
.61
.62
.75
.67
.68
.7
.73
.78
.77
.81
PL/SQL 过程已成功完成。

复制代码

三、验证count(*)和count(1)
    沿用TOM大师的解释:

<think>问题分析:用户在使用我们提供的键值对解析模块时,在调用`get_value_by_key`函数时出现了错误。根据之前提供的代码,这个函数的原型是:constchar*get_value_by_key(constKeyValuePair*pairs,uint8_tcount,constchar*key);用户试图将返回值赋值给一个`char*`类型的变量,而我们的函数返回的是`constchar*`。在C语言中,将const指针赋值给非const指针需要显式类型转换(虽然不推荐这样做),否则编译器会报错。但是,更合理的做法是保持const属性,因为返回的字符串是存储在KeyValuePair结构体中的,我们不应该允许通过返回的指针修改这些值(因为它们可能是只读的)。因此,建议用户将接收返回值的变量也声明为`constchar*`。此外,检查我们的代码,在kv_parser.h中,该函数的声明为:constchar*kv_get_value(constKeyValuePair*pairs,uint8_tcount,constchar*key);在用户提供的报错代码中,函数名为`get_value_by_key`,而我们在模块化后将其重命名为`kv_get_value`。所以用户可能是按照旧版本函数名调用的,但新版本头文件中已经改名了。因此,问题可能有两个:1.函数名不一致(用户调用get_value_by_key,但模块中实际是kv_get_value)2.变量类型不匹配(constchar*赋值给char*)解决方案:1.更新用户代码,使用正确的函数名:kv_get_value2.将接收返回值的变量声明为constchar*示例修改:将char*pin_val=get_value_by_key(pairs,count,"pin");改为constchar*pin_val=kv_get_value(pairs,count,"pin");如果用户确实需要修改返回的字符串,则应该复制一份(但要注意内存管理)。下面提供修改后的使用示例:</think> 在键值对解析模块中,`get_value_by_key`函数返回的是`const char*`指针类型,但您试图将其赋值给`char*`类型变量。以下是解决方法: ### 问题分析 在kv_parser.h中,函数定义为: ```c const char* kv_get_value(const KeyValuePair* pairs, uint8_t count, const char* key); ``` 返回类型为`const char*`,但您使用的是`char*`变量,导致类型不匹配错误。 ### 解决方案 修改代码使用正确的函数名类型: ```c // 在main.c中的正确用法 #include "kv_parser.h" int main(void) { // ... 初始化代码 ... // 使用正确的函数名 kv_get_value const char* name = kv_get_value(pairs, count, "name"); const char* pin_val = kv_get_value(pairs, count, "pin"); // 使用const char* // 正确使用指针 char output[50]; if (name && pin_val) { sprintf(output, "Name: %s, PIN: %s\n", name, pin_val); USART_SendString(output); } // ... 其他代码 ... } ``` ### 完整修正代码 #### kv_parser.h (保持原样) ```c #ifndef KV_PARSER_H #define KV_PARSER_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> #include <stdbool.h> // ... 结构体宏定义 ... const char* kv_get_value(const KeyValuePair* pairs, uint8_t count, const char* key); // ... 其他函数声明 ... #ifdef __cplusplus } #endif #endif // KV_PARSER_H ``` #### main.c (修改后) ```c #include "stm32f4xx_hal.h" #include "kv_parser.h" // 假设的串口发送函数 void USART_SendString(const char* str); int main(void) { HAL_Init(); SystemClock_Config(); USART_Init(); const char* test_str = "name:Yao, pin:8, temp:25.5, active:true"; KeyValuePair pairs[MAX_PAIRS]; while(1) { uint8_t count = kv_parse(test_str, pairs, MAX_PAIRS); // 使用正确的函数名类型 const char* name = kv_get_value(pairs, count, "name"); const char* pin_val = kv_get_value(pairs, count, "pin"); const char* temp_val = kv_get_value(pairs, count, "temp"); const char* active_val = kv_get_value(pairs, count, "active"); // 创建输出缓冲区 char output[100]; int len = 0; // 安全拼接结果 if(name) len += snprintf(output + len, sizeof(output) - len, "Name: %s\n", name); if(pin_val) len += snprintf(output + len, sizeof(output) - len, "PIN: %s\n", pin_val); if(temp_val) len += snprintf(output + len, sizeof(output) - len, "Temperature: %s\n", temp_val); if(active_val) len += snprintf(output + len, sizeof(output) - len, "Active: %s\n", kv_to_bool(active_val) ? "Yes" : "No"); // 发送结果 USART_SendString(output); HAL_Delay(1000); } } ``` ### 关键修改说明 1. **函数名修正**: - 使用正确的`kv_get_value`而不是`get_value_by_key` 2. **类型修正**: - 将接收变量声明为`const char*`而不是`char*` 3. **安全字符串处理**: - 使用`snprintf`代替`sprintf`防止缓冲区溢出 - 使用`len += snprintf(output + len, sizeof(output) - len, ...)`安全拼接 4. **布尔值转换**: - 使用`kv_to_bool(active_val) ? "Yes" : "No"`正确显示布尔值 ### 补充建议 如果确实需要修改返回值,可以创建副本: ```c const char* pin_ptr = kv_get_value(pairs, count, "pin"); if(pin_ptr) { char pin_val[MAX_VALUE_LEN]; strncpy(pin_val, pin_ptr, sizeof(pin_val) - 1); pin_val[sizeof(pin_val) - 1] = '\0'; // 确保终止 // 现在可以安全使用pin_val并修改 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值