11

我正在使用类似于以下形式的 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

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

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

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

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

4

8 回答 8

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(在两个表上)的复合索引。

于 2009-01-14T21:52:16.253 回答
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 中的字段,则将左连接更改为内连接。

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

于 2009-01-15T01:35:46.333 回答
4

任何人根据您提供的信息告诉您的任何事情都是猜测。

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

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

于 2009-01-15T18:16:37.390 回答
3

您是否在两个表person_uid上都有覆盖索引?period

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

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

另外:字段的数据类型是什么?它们在两个表中是否相同?隐式转换确实可以减慢速度。

于 2009-01-14T21:48:51.393 回答
2

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

于 2009-01-14T21:50:11.097 回答
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 没有适当的索引,那么您根本看不到任何性能改进......

民主党。

于 2009-01-15T16:50:21.357 回答
0

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

于 2016-10-19T16:36:15.753 回答
0

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

第一季度

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

于 2017-01-08T18:45:52.327 回答