我正在使用类似于以下形式的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:这些实际上是我加入的视图。不幸的是,它们位于我无法控制的数据库上,因此我无法(轻松)对索引进行任何更改。我倾向于同意这是一个索引问题。我会等一会儿才接受答案,以防有一些神奇的方法来调整我不知道的查询。否则,我会接受当前的答案之一,并尝试找出另一种方法来做我想做的事情。感谢大家的帮助。
答案 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_uid
和period
都有覆盖索引吗?
如果没有,请添加它们并重试。
查看执行计划,看看查询实际在做什么。
另外:字段的数据类型是什么?两张桌子都一样吗?隐式演员阵容可以让事情变慢。
答案 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子句之间的差异非常重要。