sql where子句中的列顺序
Posted
技术标签:
【中文标题】sql where子句中的列顺序【英文标题】:column order in sql where clause 【发布时间】:2013-01-23 07:49:44 【问题描述】:全部
sql where 子句中的列顺序有什么有效的问题吗?我搜索了网络,然后发现了一些建议,首先说的是加入,然后是其他过滤器。 例如,
select t1.a, t2.b
from table1 t1, table2 t2
where t1.a = t2.a and t1 > 1 and t2 > 2;
如果我把t1 > 1
改成t1.a = t2.a
前面有什么问题吗?
我想知道exists子句中的列顺序。 例如,
select t1.a, t2.b
from table1, table2 t2
where t1.a = t2.a
and not exists (select 1 from table3 t3 where t1.a = t3.a and t1.b = t3.b)
如果我把t1.b = t3.b
改成t3.b = t1.b
有什么影响,我会受伤吗?
列顺序是否重要?
-
谁能解释一下sql select执行步骤,并给出一些有用的教程链接?
【问题讨论】:
也许您可以在真实数据环境中为两个 SELECT 运行EXPLAIN PLAN
。
【参考方案1】:
要摆脱这种情况,请改用 JOIN
关键字使用显式 ANSI SQL-92 语法:
select t1.a, t2.b
from table1 t1
inner join table2 t2 on t1.a = t2.a
where t1 > 1 and t2 > 2;
这样,WHERE
子句中将不再有连接条件,您可以轻松添加更多条件。
对于您的第二个查询,您可以使用OUTER JOIN
:
select t1.a, t2.b
from table1
inner join table2 t2 on t1.a = t2.a
LEFT JOIN table3 t3 on t1.a = t3.a and t1.b = t3.b
where t3.a IS NULL;
我知道这不是在回答您的问题,但是这种旧的连接语法是 where 子句中条件模糊的问题。不推荐,尽量避免:
Bad habits to kick : using old-style JOINs.
Bad habits to kick : using table aliases like (a, b, c) or (t1, t2, t3).
【讨论】:
我一直了解到,您将 where 子句按最大还原能力到最小还原能力排序。它们按呈现的顺序执行,因此每个子句可以清除的记录越多,每个后续子句的速度就越快。如果这是错误的,我会很想知道为什么 @JasonSperske 您不需要在 where 子句中对谓词进行排序,因为 Oracle 会根据需要重写它们。唯一需要这样做的情况是,如果您使用的是基于 RULE 的优化器(如果是,您可能使用的是非常旧的数据库版本) @DazzaL,还有另一个过时的优化,我可以摆脱我的工具带。那么这些天 DBA 到底在做什么呢? “旧式”Oracle 连接语法不是一个坏习惯。在幕后,Oracle 将 ANSI 语法映射到“旧”语法,因为这是优化器所理解的。这可能导致次优的执行计划。 jonathanlewis.wordpress.com/2010/12/03/ansi-argh【参考方案2】:人们说 WHERE 子句中谓词的顺序不会影响性能是正确的大部分时间。然而,我们经常对系统中的数据了解更多,尤其是它的分布和偏斜,而不是优化器从其统计数据中得出的结论。在这种情况下,为优化器提供我们所能提供的最佳信息对我们来说很重要。
这是一个从我上周遇到的真实情况中提取的示例(我现在没有时间运行测试用例,但我稍后会)。
情况:三张桌子,一个父母和两个孩子。任务是通过在其子项中查找非唯一行来选择父项中的唯一行。所有表都有数百万行。
我的第一次尝试是这样的:
select parent.*
from child1
, child2
, parent
where child1.col_a = 'whatever'
and child2.col_n = 9999
and child1.parent_id = child2.parent_id
and parent.id = child2.parent_id
查询返回了正确的结果集,但性能很差。解释计划显示查询正在离开 CHILD2。这是错误的,因为 CHILD1 上的过滤器更具选择性。所以我重新编写了这样的查询:
select parent.*
from child1
, child2
, parent
where child1.col_a = 'whatever'
and child2.col_n = 9999
and child2.parent_id = child1.parent_id
and parent.id = child1.parent_id
现在查询驱离了 CHILD1,性能提高了一个数量级以上。这不会一直发生,但我说摆弄 WHERE 子句仍然是一种有效的调优技术。
CBO 是一款非常智能的软件。然而,我们不能只是向它扔一个杂乱无章的 WHERE 子句,并期望它每次都能产生最好的解释计划。查询越复杂(复杂性由连接数定义),我们以有意义的方式组织 WHERE 子句就越重要。它不会造成任何伤害,它可能只会带来一些好处。
顺便说一句,那些说 ANSI 连接语法不会再次影响性能的人是正确的大多数时候。有时它确实会导致优化器产生较差的执行计划。 Find out more。
【讨论】:
根据我的经验,这个答案比 Mahmoud Gamal 的答案要好得多,也更有用。感谢 APC。 这有点不同。根据海报,他在问您是否更改了 where 子句的顺序以在范围过滤器之后或之前进行连接,或者翻转t1.b = t3.b to t3.b = t1.b
等)。您更改了 JOIN 顺序(parent 到 child1 vs child2)。这可能是一种非常有效的调优方法,并且会对计划产生影响。在您的示例中,出现在 where 子句 and child2.col_n = 9999
中的哪个位置并不重要,您的 and child2.parent_id = child1.parent_id
与 and child1.parent_id = child2.parent_id
也不会有所不同。
您的意思是加入 child1 x child2 x parent 与 parent x child1 x child2 有不同的计划? @Viscroad 询问了 where 子句中的列顺序,而不是三向连接...【参考方案3】:
在基于成本的优化器 (CBO) 下,where 子句中谓词的顺序并不重要,因为 CBO 会很高兴地按照它认为合适的方式重新排列谓词(除非您在其中插入了提示告诉 oracle不要这样做)。
对于基于规则的优化器来说,确实应该注意谓词的顺序,但希望您使用的不是仍然使用 RBO 的旧版 Oracle。
【讨论】:
【参考方案4】:where 子句中的顺序并不重要。 ANSI JOIN 语法提高了可读性,但不影响性能。为了证明这一点:
create table t1 (a number, x number);
create table t2 (a number, b number);
insert into t1 (a, x) select level, mod(level,100) from dual connect by level <= 100000;
insert into t2 (a, b) select level, mod(level,10) from dual connect by level <= 100000;
exec dbms_stats.gather_table_stats(user,'t1');
exec dbms_stats.gather_table_stats(user,'t2');
set autotrace trace explain
所有四个查询
select t1.a, t2.b from t1, t2 where t1.a=t2.b and t1.x > 5 and t2.b > 5;
select t1.a, t2.b from t1, t2 where t1.a=t2.b and t2.b > 5 and t1.x > 5;
select t1.a, t2.b from t1, t2 where t1.x > 5 and t2.b > 5 and t1.a=t2.b;
select t1.a, t2.b from t1 join t2 on t1.a=t2.b where t1.x > 5 and t2.b > 5;
产生完全相同的查询计划
Plan hash value: 282751716
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 44444 | 434K| 88 (7)| 00:00:02 |
|* 1 | HASH JOIN | | 44444 | 434K| 88 (7)| 00:00:02 |
|* 2 | TABLE ACCESS FULL| T2 | 44444 | 130K| 42 (5)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| T1 | 94946 | 649K| 44 (5)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."A"="T2"."B")
2 - filter("T2"."B">5)
3 - filter("T1"."X">5 AND "T1"."A">5)
【讨论】:
您的表没有任何索引,并且数据分布不切实际。因此,尽管您已经证明了四个不同排列的 WHERE 子句可以产生相同的解释计划,但我认为您的结论没有意义。 您的表没有索引。您对它们运行的任何查询都将在每个表上包含一个 FTS,然后是一个散列连接,因为还有什么其他访问路径? 为了回答我自己的 rgetorical 问题,还可以访问每个表上的 FTS 的路径,使用缓冲区排序,然后是合并连接笛卡尔。以上是关于sql where子句中的列顺序的主要内容,如果未能解决你的问题,请参考以下文章