【问题标题】:Improving the query processing performance of SUM and JOIN SQL提高 SUM 和 JOIN SQL 的查询处理性能
【发布时间】:2021-11-03 06:02:38
【问题描述】:
SELECT SUM(C_QUANTITY)
FROM CARS JOIN ORDERS
ON C_ORDERKEY = O_ORDERKEY;

我有这个查询从 JOIN 表中聚合 L_QUANTITY 的总和。使用EXPLAIN PLAN 的查询成本为12147。目标是通过实现更高效的SELECT 语句来改进此SELECT 语句,从而获得相同的结果。

我试过了

SELECT SUM(C_QUANTITY)
FROM CARS

它返回了相同的结果,但查询成本与原始的完全相同。我认为通过删除JOINSELECT 查询将会改进。

有没有办法通过只修改SELECT 语句来降低成本?

编辑:

原始查询计划

PLAN_TABLE_OUTPUT                                                               
--------------------------------------------------------------------------------
Plan hash value: 2287326370                                                     
                                                                                
------------------------------------------------------------------------------- 
| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     | 
------------------------------------------------------------------------------- 
|   0 | SELECT STATEMENT   |          |     1 |     3 | 12147   (1)| 00:00:01 | 
|   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          | 
|   2 |   TABLE ACCESS FULL|   CARS   |  1800K|  5273K| 12147   (1)| 00:00:01 | 
------------------------------------------------------------------------------- 

9 rows selected. 

使用第二个查询

PLAN_TABLE_OUTPUT                                                               
--------------------------------------------------------------------------------
Plan hash value: 2287326370                                                     
                                                                                
------------------------------------------------------------------------------- 
| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     | 
------------------------------------------------------------------------------- 
|   0 | SELECT STATEMENT   |          |     1 |     3 | 12147   (1)| 00:00:01 | 
|   1 |  SORT AGGREGATE    |          |     1 |     3 |            |          | 
|   2 |   TABLE ACCESS FULL|   CARS   |  1800K|  5273K| 12147   (1)| 00:00:01 | 
------------------------------------------------------------------------------- 

9 rows selected. 

【问题讨论】:

  • 我同意这很奇怪。第一个查询当然比第二个查询复杂得多。你确定你没有看错? (顺便说一句,查询或数据模型看起来也很奇怪。一辆汽车有一个数量,每个订单都订购该数量,所以你将汽车的数量乘以它的订单数量?真的吗?)
  • 您好,Thorsten,感谢您的回复。语义从原来的改变也许这就是为什么你觉得数据模型很奇怪。我在两个查询处理中都没有弄错 - 我已经运行了几次,结果是一样的。因此,在这里发布这个问题,希望得到一些反馈。
  • 解释计划看起来和我一模一样。两个查询之间的性能可能不相同,但非常接近。
  • 谢谢蒂姆。我认为仅通过修改SELECT 来改进上述查询是不可能的。我会将您的答案标记为正确的答案,因为index 确实改善了此类查询。
  • 我花了一些时间才明白这一点。订单表是父表。汽车始终是一个订单的一部分。因此,为了汇总汽车数量,Oracle 不必读取订单表。所有需要的信息都在汽车表中。所以优化器决定根本不读取订单表。这很好,因此无论如何都是最好的计划。您的查询尽可能简单,无法优化。使用包含数量的索引,Oracle可以决定进行全索引扫描而不是读取表,从而减少读取驱动器扇区/块。

标签: sql oracle performance query-optimization sql-execution-plan


【解决方案1】:

我建议添加以下索引:

CREATE INDEX idx ON ORDERS (O_ORDERKEY, C_QUANTITY);

据推测,ORDERS 表会比CARS 大得多。如果是这样,Oracle 可能会通过扫描CARS 来满足查询,然后将能够使用上述索引在ORDERS 表中进行查找。我将 C_QUANTITY 列添加到索引的末尾,以覆盖 select 子句中的总和。

【讨论】:

  • 谢谢蒂姆。我知道添加index 绝对可以加快查询速度,但是没有可以做到吗?
  • 我在我的编辑中包含了解释计划。只是大声思考,这可能是由于我的机器造成的,因此两个查询都给了我相同的成本?
  • 我认为orders 表中没有c_quantity 列。我能想到加速这些查询的唯一合理 (?) 索引是 create index idx on cars (c_quantity) DBMS 可能 决定进行全索引扫描以节省一些磁盘读取。
【解决方案2】:

如果你有两个表carsorders没有连接,你会得到并普通加入执行计划如下。

--------------------------------------------------------------------------------------
| Id  | Operation           | Name   | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |        |     1 |    15 |       |   297   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE     |        |     1 |    15 |       |            |          |
|*  2 |   HASH JOIN         |        |   100K|  1464K|  1664K|   297   (2)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| ORDERS |   100K|   488K|       |    47   (3)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| CARS   |   100K|   976K|       |    62   (2)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("C_ORDERKEY"="O_ORDERKEY")

cars 显然是一个 表或orders,即你有这个约束

alter table orders add primary key (O_ORDERKEY);
alter table cars add constraint cars_fk foreign key(C_ORDERKEY) references orders(O_ORDERKEY);

Oracle 足够聪明知道它不需要访问orders 表来获取总和

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    10 |    63   (4)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| CARS |   100K|   976K|    63   (4)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("C_ORDERKEY" IS NOT NULL)

请注意过滤器C_ORDERKEY IS NOT NULL,如果C_ORDERKEY 列可以为空,则仍需要该过滤器才能获得正确的总和。 (这些行将在连接中被消除)。

如果不是,这可能是有意义的

 alter table cars modify C_ORDERKEY not null;

你只需要在C_QUANTITY列上定义一个索引就可以得到最优方案

create index car_idx on cars(C_QUANTITY);

---------------------------------------------------------------------------------
| Id  | Operation             | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |     1 |     5 |    63   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |         |     1 |     5 |            |          |
|   2 |   INDEX FAST FULL SCAN| CAR_IDX |   100K|   488K|    63   (2)| 00:00:01 |
---------------------------------------------------------------------------------

请注意,INDEX FAST FULL SCAN 使用一种索引作为表全扫描访问(即不使用指针直接访问索引块)所以它(在索引小于表的情况下)要快得多表全扫描访问。

【讨论】:

  • 您不需要not null 约束,而是可以将其作为过滤器添加到查询中,因为null 值无论如何都不会影响sum
  • 我没有抓住它@AndrewSayer - not null 约束在 C_ORDERKEY 上而不是在 C_QUANTITY 上。当然你也可以在查询中加上where C_ORDERKEY is not null,效果一样……
  • 啊,我读得太快了,假设您正在创建约束,以便可以使用索引而不是表扫描。看来甲骨文很聪明,知道它只关心 not null 自己的值。
  • 好吧,真正的问题在于具有C_ORDERKEY is nullC_QUANTITY is not null 的行必须从SUM 中排除,因为它们将在连接中被丢弃(这不是执行)@AndrewSayer
猜你喜欢
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多