早上好,
在我昨天编写的两个大小合适的结果集(每个结果<50k)之间的查询中,我的JOIN的一部分是一个子句,用于检查数据是否匹配或为空(以下是简化版本):
SELECT a JOIN b ON a.class = b.class OR (a.class is null AND b.class is null)但是,我注意到围绕OR语句的使用出现了一个严重的性能问题。我使用以下方法解决了这个问题:
SELECT a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')第一个查询的运行时间长得令人无法接受,而第二个查询的运行时间要快几个数量级(>45分钟与<1)。我预计由于比较次数较多,OR的运行速度会变慢,但在此特定数据集中,a.class = b.class = null的情况相对较少。
是什么导致性能时间如此显著地增加?Oracle SQL不会像许多其他语言那样简化布尔比较吗?有没有办法挽救第一个查询而不是第二个查询(用于一般的SQL,而不仅仅是Oracle)?
发布于 2012-05-05 01:16:14
您将返回一个包含任何具有空类的记录的叉积。这对你的结果合适吗?
我在11gR2中创建了两个示例查询:
WITH a as
(select NULL as class, 5 as columna from dual
UNION
select NULL as class, 7 as columna from dual
UNION
select NULL as class, 9 as columna from dual
UNION
select 'X' as class, 3 as columna from dual
UNION
select 'Y' as class, 2 as columna from dual),
b as
(select NULL as class, 2 as columnb from dual
UNION
select NULL as class, 15 as columnb from dual
UNION
select NULL as class, 5 as columnb from dual
UNION
select 'X' as class, 7 as columnb from dual
UNION
select 'Y' as class, 9 as columnb from dual)
SELECT * from a JOIN b ON (a.class = b.class
OR (a.class is null AND b.class is null))当我在这个查询上运行EXPLAIN PLAN时,它表明表(在我的例子中是内联视图)是通过嵌套循环联接的。嵌套循环联接的操作方式是扫描一个表的第一行,然后扫描另一个表的每一行以查找匹配项,然后扫描第一个表的第二行,在第二个表中查找匹配项,依此类推。由于您不是在联接的OR部分中直接比较任何一个表,因此优化器必须使用嵌套循环。
在幕后,它可能看起来像这样:
当我将SELECT语句更改为SELECT * FROM a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')时,EXPLAIN指示使用散列联接。散列连接实质上是生成较小表的每个连接键的散列,然后扫描大表,在较小的表中查找匹配的每一行的散列。在本例中,由于它是一个简单的Equijoin,优化器可以毫无问题地散列驱动表的每一行。
在幕后,它可能看起来像这样:
如果对查询运行EXPLAIN计划,您可能会找到类似的结果。
即使最终结果是相同的,因为您没有使用"OR“连接第一个查询中的表,所以优化器不能使用更好的连接方法。如果驱动表很大,或者如果您强制对大型辅助表执行全表扫描,则嵌套循环可能会非常慢。
您可以使用ANSI函数在其他数据库系统中模拟COALESCE函数。这里真正的问题是,您正在尝试连接一个null值,您实际上应该有一个"NO CLASS“或一些其他方法来标识" NULL”类,即null = nothing而不是null = unknown。
在评论中回答您的问题的附录:
对于null查询,引擎将执行以下操作:
对于'N/A‘查询,引擎将执行以下操作:
发布于 2012-05-04 22:51:33
在第一种情况下,因为每个null是不同的,所以数据库不使用优化(对于来自a的每一行,检查来自表b的每一行)。
在第二个案例数据库中,首先将所有空值更改为'N/A‘,然后只比较a.class和b.class,使用优化
比较Oracle中的空值非常耗时。null是未定义的值-一个Null与其他null不同。比较两个几乎相同的查询的结果:
select 1 from dual where null is null
select 1 from dual where null = null只有带有特殊is null子句的第一个查询才返回正确答案。因此,空值不能被索引。
发布于 2012-05-04 22:56:24
试试这个:
SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where a.class is null
union all
SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where b.class is null应该变得更快
https://stackoverflow.com/questions/10450945
复制相似问题