【问题标题】:Oracle sql query plan does not uses indices with or statement and a subqueryOracle sql 查询计划不使用带有 or 语句和子查询的索引
【发布时间】:2019-01-02 11:48:31
【问题描述】:

我有一个带有“或”语句和“内部选择”语句的查询。当我检查查询计划时,它使用全表访问,我不知道为什么会发生这种情况。

我创建了这个测试用例来显示我的问题:

CREATE TABLE PERSON (
    ID                   NUMBER(18)           NOT NULL,
    NAME                 VARCHAR2(18)         NOT NULL,
    SURNAME              VARCHAR2(18)         NOT NULL
);

BEGIN
  FOR b IN 1..500000
  LOOP
     INSERT INTO PERSON VALUES (b,'name' || to_char(b),'surname' || to_char(b));
  END LOOP;
END;

CREATE INDEX PERSON_NAME_index ON PERSON(NAME);
CREATE INDEX PERSON_SURNAME_index ON PERSON(SURNAME);

这是有问题的查询:

SELECT p.*
FROM PERSON p
WHERE p.NAME = 'name300' or  p.SURNAME in (SELECT p2.SURNAME
                                       FROM PERSON p2
                                       WHERE p2.NAME = 'name500');

当我分析查询计划时,我看到它对 person 表使用全表访问。这大大减慢了我的查询时间,我不知道为什么会这样。

如果我消除第一个约束(p.NAME = 'name300')并仅使用 subselect 语句进行查询,一切正常,查询再次使用索引。

有人能解释一下为什么第一种情况下查询没有使用我的索引吗?

【问题讨论】:

  • 您是否收集了有关表格的统计信息?
  • 经典问题。由于您的 OR,查询计划无法使用索引并陷入全表扫描。将您的 OR 更改为如下 Gordon 建议的 UNION。每当您编写 OR 时,您应该始终问自己是否可以用 UNION 替换它。
  • @BobJarvis 是的,我收集了统计数据。

标签: sql oracle indexing query-planner


【解决方案1】:

OR 数据库难以优化。您可以将其拆分为两个查询并使用UNION ALL

SELECT p.*
FROM PERSON p
WHERE p.NAME = 'name300'
UNION ALL
SELECT p.*
FROM PERSON p
WHERE p.NAME <> 'name300' AND
      p.SURNAME IN (SELECT p2.SURNAME
                    FROM PERSON p2
                    WHERE p2.NAME = 'name500'
                   );

【讨论】:

  • 一个想法虽然:) 为什么在这种情况下全部?原始查询不会返回 dups。
  • @ThomasG 。 . .没有理由产生删除重复项的开销,如果使用UNION 就会出现这种情况。即使两个条件都为真,此查询也不返回重复项。
  • 你能给我更多关于“或”优化的具体信息吗?文档会很棒。此外,甚至 oracle 也可以像您一样翻译查询(因为它是一个简单的逻辑转换),我不明白为什么它不这样做。
  • @AlperenÜretmen 。 . .查询优化是一个非常复杂的主题。您应该从文档开始:docs.oracle.com/cd/E25178_01/server.1111/e16638/optimops.htm.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
  • 1970-01-01
  • 2011-10-27
  • 1970-01-01
相关资源
最近更新 更多