2009-01-14 170 views
11

我使用的是SQL查询类似于以下形式:LEFT OUTER JOIN

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

请为此查询提供执行计划 – squadette 2009-01-14 21:48:51

回答

16

请记住,语句2和3与第一个语句不同。

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

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

并查看您是否得到相同的性能问题。

你对这些表有什么指标?这种关系是否由外键约束定义?

您可能需要的是person_uid和period(两个表)上的组合索引。

+0

查询错误,我不能混合ANSI外部联接和旧式外部联接。 – 2009-01-14 21:58:43

3

您是否对这两个表的person_uidperiod都有索引?

如果不是,请添加它们并重试。

查看执行计划并查看查询实际正在执行的操作。

另请参见:字段的数据类型是什么?两张桌子都一样吗?隐含的演员可以真的放慢速度。

+0

嗯,问题标记为oracle,所以我不认为他使用的是SQL Server。 – cletus 2009-01-14 21:57:44

+0

啊:)我没有看到......无所谓......执行计划也可在oracle中使用......我将编辑答案。 – 2009-01-14 21:59:26

2

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

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

在左连接中你会扫描表1为(person_uid,周期),每个独特组合,那么对于所有相应的记录有搜索表2。如果table2没有合适的索引,则可能涉及扫描整个表格。

我最好的猜测,没有看到一个执行计划,是第一个查询(这似乎是正确的唯一一个)是有表扫描表2和表1。

正如你说,你不能改变的指标,您需要更改查询。据我所知,只有一个现实的选择...

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 

这里的希望是,你扫描(person_uid,周期)的每个唯一组合表2,但要在表1索引的使用。 (而不是扫描table1,并使用table2上的索引,这是我期望从您的查询中得到的结果)

但是,如果table1没有合适的索引,那么您将不太可能看到任何性能改进所有...

Dems。

0

在更新之一的OP说他实际上是查询视图中不表。在这种情况下,可以通过直接查询他需要的表来增加性能,特别是在视图非常复杂的情况下,可以加入许多其他不包含所需信息的表或者他们是调用视图的视图。

0

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

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子句之间的差异非常重要。