左外连接在两列性能问题上

时间:2009-01-14 21:45:12

标签: sql oracle join oracle10g left-join

我正在使用类似于以下形式的SQL查询:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period

它要么太慢,要么死机,因为它至少需要4分钟才能返回。如果我要将其更改为:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period

然后它工作正常(尽管没有返回正确数量的列)。有什么方法可以加快速度吗?

UPDATE :如果我切换后一个查询的最后两行,它会做同样的事情:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid

更新2:这些实际上是我加入的视图。不幸的是,它们位于我无法控制的数据库上,因此我无法(轻松)对索引进行任何更改。我倾向于同意这是一个索引问题。我会等一会儿才接受答案,以防有一些神奇的方法来调整我不知道的查询。否则,我会接受当前的答案之一,并尝试找出另一种方法来做我想做的事情。感谢大家的帮助。

8 个答案:

答案 0 :(得分:16)

请记住,陈述2和陈述与第一陈述不同。

如何?好吧,你正在做一个左外连接,你的WHERE子句没有考虑到这一点(就像ON子句那样)。至少,尝试:

SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)

看看你是否遇到了同样的性能问题。

您对这些表有哪些索引?这种关系是由外键约束定义的吗?

你可能需要的是person_uid和period(在两个表上)的综合索引。

答案 1 :(得分:5)

我认为您需要了解为什么后两个查询与第一个查询不同。如果你做左连接然后添加一个where子句引用连接右侧表格中的一个字段(可能并不总是有一个记录与第一个表匹配),那么你已经有效地将连接更改为内部联接。有一个例外,那就是你引用类似

的东西
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null

在这种情况下,您要求在第二个表中没有记录的记录。但除了这种特殊情况之外,如果在where子句中反对table2中的字段,则将左连接更改为内连接。

如果您的查询速度不够快,我会查看您的索引。

答案 2 :(得分:4)

根据您提供的信息,任何人都会告诉您这一点。

查看查询的执行计划。如果您没有看到计划缓慢的原因,请在此处发布计划。

http://download.oracle.com/docs/cd/B28359_01/server.111/b28274/ex_plan.htm#PFGRF009

答案 3 :(得分:3)

这两个表的person_uidperiod都有覆盖索引吗?

如果没有,请添加它们并重试。

查看执行计划,看看查询实际在做什么。

另外:字段的数据类型是什么?两张桌子都一样吗?隐式演员阵容可以让事情变慢。

答案 4 :(得分:2)

这些表是否包含您要加入的列的索引?安装Oracle的免费SQLDeveloper产品并使用它来对该查询进行“解释”,看看它是否正在对两个表进行顺序扫描。

答案 5 :(得分:2)

在左连接中,您将扫描table1以获取(person_uid,period)的每个唯一组合,然后在table2中搜索所有相应的记录。如果table2没有合适的索引,这也可能涉及扫描整个表。

我最好的猜测是,在没有看到执行计划的情况下,第一个查询(唯一一个似乎是正确的查询)是必须表扫描table2以及table1。

正如您所说无法更改索引,您需要更改查询。据我所知,只有一个现实的替代方案......

SELECT
   col1, col2
FROM
   table2
FULL OUTER JOIN
   table1
      ON table1.person_uid = table2.person_uid
      AND table1.period = table2.period
WHERE
   table1.person_uid IS NOT NULL

这里的希望是你扫描table2以获得(person_uid,period)的每个唯一组合,但是在table1上使用索引。 (而不是扫描table1并使用table2上的索引,这是我对你的查询所期望的。)

如果table1没有合适的索引,那么你根本不可能看到任何性能提升......

民主党。

答案 6 :(得分:0)

在其中一个更新中,OP声明他实际上是在查询视图而不是表格。在这种情况下,通过直接查询所需的表可以很好地提高性能,特别是如果视图很复杂并且连接到许多其他不包含所需信息的表,或者它们是调用视图的视图。

答案 7 :(得分:0)

ANSI连接语法在JOIN条件和FILTER谓词之间提供了非常明确的区别;这在编写外连接时非常重要。使用emp / dept表,查看以下两个外连接的结果

Q1

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
RESEARCH               20                       DALLAS
SALES                  30                       CHICAGO
OPERATIONS             40                       BOSTON

====

Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
OPERATIONS             40                       BOSTON

第一个例子,Q1显示的是"加入常量"的例子。实质上,在执行外连接之前应用过滤条件。因此,您将消除行,这些行随后作为外部联接的一部分添加回来。它不一定是错的,但那是你真正要求的查询吗?通常情况下,Q2中显示的结果是必需的,其中过滤器在(外部)连接之后应用。

对于大型数据集,也存在性能影响。在许多情况下,优化器必须通过创建横向视图在内部解析连接常量,通常只能通过嵌套循环连接而不是散列连接进行优化

对于熟悉Oracle外连接语法的开发人员,查询可能已写为

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
        ,emp e
where  d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )

此查询在语义上等同于上面的Q2。

总而言之,在编写ANSI外连接时理解JOIN子句和WHERE子句之间的差异非常重要。