【问题标题】:Oracle Selecting Index OptimisationOracle 选择索引优化
【发布时间】:2020-01-22 16:15:59
【问题描述】:

我需要使用索引来优化这个查询。我试图索引一些列,但它没有帮助。有没有人有想法?

我需要优化的查询:

Select vintage, wine_no, wname, pctalc, grade, price, wine.vid, vname, wine.cid, cname
from vineyard, class, wine
where wine.vid = vineyard.vid
and wine.cid = class.cid
and wine.cid = 'SHIRAZ' and grade = 'A';

我尝试创建以下索引: '''在 wine(vid) 上创建索引 wine_vid_idx; 在 wine(cid) 上创建索引 wine_cid_idx; 在 wine(grade) 上创建索引 wine_grade_idx;```

我对原始查询的执行计划是:

----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |              |    42 |  9114 |    10   (0)| 00:00:01 |
|*  1 |  HASH JOIN                    |              |    42 |  9114 |    10   (0)| 00:00:01 |
|   2 |   NESTED LOOPS                |              |    42 |  6930 |     2   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID| CLASS    |     1 |    50 |     1   (0)| 00:00:01 |
|*  4 |     INDEX UNIQUE SCAN         | SYS_C0027457 |     1 |       |     1   (0)| 00:00:01 |
|*  5 |    TABLE ACCESS CLUSTER       | WINE    |    42 |  4830 |     1   (0)| 00:00:01 |

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                           
---------------------------------------------------------------------------------------------
|   6 |   TABLE ACCESS FULL           | VINEYARD |   160 |  8320 |     8   (0)| 00:00:01 |

【问题讨论】:

  • 数据库性能取决于许多不同的因素,仅仅查看查询并“调整它”是不可能的。当然,乐于助人的人会尝试猜测(并且已经有两个人这样做了),但他们只是在猜测。你需要尝试一下,看看什么对你有用。我敦促您阅读this response on optimizing Oracle queries。它将解释我们在提供知情建议之前需要的详细信息范围。它还可以让您有信心自己调整查询。
  • 如果可能,您应该考虑将CHAR 列类型更改为VARCHARVARCHAR2,以避免在查询固定宽度的列时可能遇到的各种意外。此外,根据表中的数据量,全表扫描有时可能比索引访问更快。添加索引也会使 DML 稍微变慢,因为索引需要保持最新。您是否遇到了要解决的性能问题?

标签: sql oracle query-performance


【解决方案1】:

这是您的查询:

select w.vintage, w.wine_no, w.wname, w.pctalc, w.grade,
       w.price, w.vid, v.vname, w.cid, c.cname
from wine w join
     vineyard v
     on v.vid = w.vid join
     class c
     on c.cid = w.cid
where w.cid = 'SHIRAZ' and
      w.grade = 'A';

所有joins 都是内连接,所有过滤都在一张表上,条件是相等条件。因此,该表应该是第一个访问的表。然后,您需要 joins 过滤条件和其他其他表的相应 join 键:

  • wine(cid, grade, vid)(前两个键可以是任意顺序)。

其他表vineyard(vid)class(cid)join 键已经被索引,因为它们被声明为主键。因此,以上是您需要的唯一附加索引。

【讨论】:

    【解决方案2】:

    首先:您使用的是旧的连接语法(实际上是 1980 年代的)。这就是我们今天编写查询的方式:

    Select
       w.vintage, w.wine_no, w.wname, w.pctalc, w.grade, w.price, w.vid, v.vname, w.cid, c.cname
    from wine w
    join vineyard v on v.vid = w.vid
    join class c on c.cid = w.cid
    where w.cid = 'SHIRAZ' 
    and w.grade = 'A';
    

    这里仅仅看一眼WHERE 子句就足以看出您正在寻找与等级和等级相匹配的葡萄酒。因此,在两列上都有一个索引。顺序可能很重要,因此请提供两个索引。用 vineyard ID 扩展它,以便快速到达 vineyard 表。

    至于类和葡萄园,您应该已经在它们的 ID 上有索引。您可能希望为您选择的每个表添加一列,以便 DBMS 可以直接从索引中获取值。

    create index idx01 on wine ( cid, grade, vid );
    create index idx02 on wine ( grade, cid, vid );
    create index idx03 on class ( cid, cname );
    create index idx04 on vineyard ( vid, vname );
    

    使用执行计划检测未使用的索引(查询将只使用 idx01 或 idx02 或什至不使用这些,而不是两者)并删除它们。

    【讨论】:

      【解决方案3】:

      索引是优化查询的一种经常有效的措施,但是,您需要执行进一步的步骤。基于文本的搜索通常会比较慢,因此强烈建议修改您的 class 表,这样它将有一个数字 primary key 并避免在您的葡萄酒表中存储诸如 SHIRAZ 之类的文本,而是一个数字 @ 987654324@ 到 class table 并将文本 SHIRAZ 存储一次,用于其 class 记录,该记录将通过数值从 wine table 引用。此外,您应该对grade 执行类似操作。如果您还没有 grade table,请创建一个,其中包含数字 primary key 和一个用于存储值的字段,例如 A。

      最后,您的查询正在计算笛卡尔乘法,正如我们从集合论中知道的那样,该乘法对问题拓扑中的每个坐标进行匹配。此外,正如我们从关系代数中知道的那样,您的 where 子句将适用于您的三维(葡萄园、类别、葡萄酒)问题空间中的所有点。据我所知,如果您将查询重构为使用连接,它应该会变得更快,因为连接有一些优化以避免计算拓扑中的所有点。

      让我们重构您当前的查询:

      Select vintage, wine_no, wname, pctalc, grade, price, wine.vid, vname, wine.cid, cname
      from wine
      join class
      on wine.cid = class.cid and wine.cid = 'SHIRAZ' and wine.grade = 'A'
      join vineyard
      on wine.vid = vineyard.vid;
      

      在我建议的结构更改之后,让我们重构此查询以与您的架构兼容:

      Select vintage, wine_no, wname, pctalc, grade, price, wine.vid, vname, wine.cid, cname
      from wine
      join class
      on wine.class_id = class.id and class.cid = 'SHIRAZ'
      join grade
      on wine.grade_id = grade.id and grade.value = 'A'
      join vineyard
      on wine.vid = vineyard.vid;
      

      此外,由于可以安全地假设只有几个等级和葡萄园,我们可以更改将表引入查询的顺序:

      Select vintage, wine_no, wname, pctalc, grade, price, wine.vid, vname, wine.cid, cname
      from grade
      join wine
      on wine.grade_id = grade.id and grade.value = 'A'
      join class
      on wine.class_id = class.id and class.cid = 'SHIRAZ'
      join vineyard
      on wine.vid = vineyard.vid;
      

      如果这还不够,还有进一步的步骤要做,如果需要进一步的步骤,请告诉我。

      【讨论】:

      • 我不明白“笛卡尔乘法”这个东西。 OP 正在挑选葡萄酒,每种葡萄酒都有其等级和葡萄园。您的重构查询如何改变这一点?您只是使用当前的语法来表达同样的事情。这不应该导致执行计划的任何差异。当然,and wine.cid = 'SHIRAZ' and wine.grade = 'A' 不属于join class 的连接子句,因为它们指的是wine 表。要么将它们放在 WHERE 子句中,要么在 FROM 子句中切换 wineclass
      • 这个答案中有很多“两美元的词”。
      • @ThorstenKettner 优化是将某些事物更改为其他事物的行为,本质上相同,但性能更好,因此我不认为新旧逻辑中的等效性是一个问题。此外,您可以将这些比较放在 on 中,重点是在维度数较小时强制引擎执行它们,而不是在最后。请在此处阅读有关笛卡尔或笛卡尔乘法/乘积的信息:jonathancrabtree.com/mathematics/…
      • @ThorstenKettner 同样,对您对查询计划没有改变的批评做出回应是件好事:我确信如果架构按照建议进行更改,查询计划将会发生重大变化,如果使用某些列和表的查询的查询计划与为不存在这些列和表的模式编写的查询的查询计划相同。
      • 我认为您在这里做出了一些错误的估计。 (1) 在应用WHERE 子句之前,您认为Oracle 会构建所有表的所有行的笛卡尔积吗?那是错误的。它将使用WHERE 子句连接表。显式连接更易于阅读并在其 ON 子句中显示连接条件,但对于 Oracle 而言,您是否在 ONWHERE 中指定内部连接条件并不重要。
      猜你喜欢
      • 2010-09-17
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 1970-01-01
      相关资源
      最近更新 更多