Fel*_*lix 6 sql oracle performance where having
所以我被分配来评估两个查询的性能并得出了令人惊讶的结果.我之前被告知HAVING比较慢,WHERE因为它只在访问行后过滤结果.这似乎是合理的,这个关于SQL子句执行顺序的问题强调了这一点.
但是,我估计以下查询的性能有一些假设,似乎使用HAVING执行实际上更快!
SELECT status, count(status)
FROM customer
GROUP BY status
HAVING status != 'Active' AND status != 'Dormant'
SELECT status, count(status)
FROM customer
WHERE status != 'Active' AND status != 'Dormant'
GROUP BY status
Run Code Online (Sandbox Code Playgroud)
假设是:
CUSTOMER有100 000条记录基于此,我的估计是:
First query:
Accessing all rows, FROM: 100 000 * 0.01ms = 1000ms
GROUP BY: 100 000 * 0.005ms = 500ms
HAVING (2 conditions, 3 groups): 2 * 3 * 0.005ms = 0.03ms
SELECT and COUNT results: 15 000 * 0.01ms = 150ms
Total execution time: 1.65003s
Second query:
Accessing all the rows, FROM: 1000ms
WHERE: 2 * 100 000 * 0.005ms = 1000ms
GROUP BY: 15 000 * 0.005ms = 75ms
SELECT and COUNT results: 15 000 * 0.01ms = 150ms
Total execution time: 2.225s
Run Code Online (Sandbox Code Playgroud)
结果来自于这样一个事实:GROUP BY只产生三组,这些组很容易过滤,而WHERE必须逐个过滤并过滤记录.
由于我天真地依赖权威,我假设我在某处犯了错误或提供的假设是错误的.
因此,没有GROUP BY这样的表现与HAVING导致减少的执行时间?
PLAN_TABLE_OUTPUT /* With HAVING */
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 35 | 4 (25)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | HASH GROUP BY | | 5 | 35 | 4 (25)| 00:00:01 |
| 3 | TABLE ACCESS STORAGE FULL| CUSM | 5 | 35 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("STATUS"<>'Active' AND "STATUS"<>'Dormant')
PLAN_TABLE_OUTPUT /* With WHERE */
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 4 (25)| 00:00:01 |
| 1 | HASH GROUP BY | | 1 | 7 | 4 (25)| 00:00:01 |
|* 2 | TABLE ACCESS STORAGE FULL| CUSM | 1 | 7 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - storage("STATUS"<>'Active' AND "STATUS"<>'Dormant')
filter("STATUS"<>'Active' AND "STATUS"<>'Dormant')
Run Code Online (Sandbox Code Playgroud)
您的假设之一是错误的:HAVING 比 WHERE 慢,因为它仅在访问和散列行后过滤结果。
正是哈希部分使得 HAVING 条件比 WHERE 条件更昂贵。散列需要写入数据,这在物理上和算法上都可能更加昂贵。
散列需要写入和读取数据。理想情况下,散列数据将及时运行O(n)。但实际上会出现哈希冲突,这会减慢速度。实际上,并非所有数据都适合内存。
这两个问题可能是灾难性的。在最坏的情况下,由于内存有限,散列需要多次传递并且复杂性接近O(n^2)。写入临时表空间中的磁盘比写入内存慢几个数量级。
这些是您需要担心数据库的性能问题。与读取、写入和连接数据的时间相比,运行简单条件和表达式的恒定时间通常无关紧要。
在您的环境中尤其如此。该操作TABLE ACCESS STORAGE FULL意味着您正在使用 Exadata。根据平台的不同,您可能会利用芯片中的 SQL。这些高级条件可以完美地转换为在存储设备上执行的低级指令。这意味着您对执行条款的成本的估计可能高出几个数量级。
创建一个包含 100,000 行的示例表:
create table customer(id number, status varchar2(100));
insert into customer
select
level,
case
when level <= 15000 then 'Deceased'
when level between 15001 and 50001 then 'Active'
else 'Dormant'
end
from dual
connect by level <= 100000;
begin
dbms_stats.gather_table_stats(user, 'customer');
end;
/
Run Code Online (Sandbox Code Playgroud)
循环运行代码可以看出,WHERE版本的速度大约是版本的两倍HAVING。
--Run times (in seconds): 0.765, 0.78, 0.765
declare
type string_nt is table of varchar2(100);
type number_nt is table of number;
v_status string_nt;
v_count number_nt;
begin
for i in 1 .. 100 loop
SELECT status, count(status)
bulk collect into v_status, v_count
FROM customer
GROUP BY status
HAVING status != 'Active' AND status != 'Dormant';
end loop;
end;
/
--Run times (in seconds): 0.39, 0.39, 0.39
declare
type string_nt is table of varchar2(100);
type number_nt is table of number;
v_status string_nt;
v_count number_nt;
begin
for i in 1 .. 100 loop
SELECT status, count(status)
bulk collect into v_status, v_count
FROM customer
WHERE status != 'Active' AND status != 'Dormant'
GROUP BY status;
end loop;
end;
/
Run Code Online (Sandbox Code Playgroud)