2

最近我的查询性能出现了一些问题。这里描述了事情:与直接运行相比,Hibernate select performance 很差 - 如何调试? 经过长时间的挣扎,我终于发现带有select前缀的查询如下:

    SELECT sth.* FROM Something as sth...

查询以这种方式开始时慢 300 倍:

    SELECT * FROM Something as sth..

有人可以帮助我,并回答为什么会这样?一些关于此的外部文档将非常有用。

用于测试的表是:

SALES_UNIT表包含一些关于销售单位节点的基本信息,例如名称等。唯一的关联是与表 SALES_UNIT_TYPE,如 ManyToOne。主键是 ID,字段 VALID_FROM_DTTM 是日期。

SALES_UNIT_RELATION包含销售单元节点之间的关系 PARENT-CHILD。由 SALES_UNIT_PARENT_ID、SALES_UNIT_CHILD_ID 和 VALID_TO_DTTM/VALID_FROM_DTTM 组成。与任何表没有关联。这里的PK是..PARENT_ID、..CHILD_ID和VALID_FROM_DTTM

我使用的实际查询是:

    SELECT s.* 
    FROM   sales_unit s LEFT JOIN sales_unit_relation r 
               on (s.sales_unit_id = r.sales_unit_child_id) 
    WHERE  r.sales_unit_child_id IS NULL

    SELECT  * 
    FROM    sales_unit s LEFT JOIN sales_unit_relation r 
               on (s.sales_unit_id = r.sales_unit_child_id) 
    WHERE   r.sales_unit_child_id  IS NULL

相同的查询,都使用左连接,唯一的区别是选择。

4

1 回答 1

4

当然,它们是两个不同的查询。计划可以随着选择的不同而改变。即在某事中。*它可能会在左连接表上选择完整/快速完整索引扫描。而第一次它可能是全表扫描。

为了进一步帮助您,我们可以看看计划吗?最好在 SQL*PLUS 中执行此操作

set timing on
set autotrace on traceonly

select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.sales_unit_child_id) where r.sales_unit_child_id is null;

select * from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.sales_unit_child_id) where r.sales_unit_child_id is null;

编辑

给定你的解释计划,你看到每一步都有 CARDINALITY=1 吗?当桌子空着时,你已经收集了统计数据!看到这个:

SQL> select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.child_sales_unit_id) where r.child_sales_unit_id is null;

no rows selected

Elapsed: 00:00:03.19

Execution Plan
----------------------------------------------------------
Plan hash value: 1064670292

------------------------------------------------------------------------------------
| Id  | Operation          | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |               |     1 |    48 |    27  (86)| 00:00:01 |
|   1 |  NESTED LOOPS ANTI |               |     1 |    48 |    27  (86)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| SALES_UNIT    |     1 |    35 |     2   (0)| 00:00:01 |
|*  3 |   INDEX RANGE SCAN | SALES_REL_IX1 |     1 |    13 |    25  (92)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("S"."SALES_UNIT_ID"="R"."CHILD_SALES_UNIT_ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     200314  consistent gets
       2220  physical reads
          0  redo size
        297  bytes sent via SQL*Net to client
        339  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

所以看到它使用了 200314 IO 并花了几秒钟。另请参阅每一步的 ROWS = 1(即完整扫描)..让我们收集统计信息:

SQL> begin dbms_stats.gather_table_stats(user, 'SALES_UNIT', degree=>8, cascade=>true); end;
  2  /

PL/SQL procedure successfully completed.

SQL> begin dbms_stats.gather_table_stats(user, 'SALES_UNIT_RELATION', degree=>8, cascade=>true); end;
  2  /

PL/SQL procedure successfully completed.

现在重新运行: SQL> select s.* from sales_unit s left join sales_unit_relation r on (s.sales_unit_id = r.child_sales_unit_id) 其中 r.child_sales_unit_id 为空;

no rows selected

Elapsed: 00:00:00.84

Execution Plan
----------------------------------------------------------
Plan hash value: 2005864719

-----------------------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |   912 | 18240 |       |  1659   (3)| 00:00:20 |
|*  1 |  HASH JOIN ANTI       |               |   912 | 18240 |  2656K|  1659   (3)| 00:00:20 |
|   2 |   TABLE ACCESS FULL   | SALES_UNIT    |   100K|  1472K|       |    88   (3)| 00:00:02 |
|   3 |   INDEX FAST FULL SCAN| SALES_REL_IX1 |   991K|  4841K|       |   618   (3)| 00:00:08 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("S"."SALES_UNIT_ID"="R"."CHILD_SALES_UNIT_ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       2537  consistent gets
          0  physical reads
          0  redo size
        297  bytes sent via SQL*Net to client
        339  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>

现在我们只使用了 2537 个获取,并且该计划显示了正确的 ROWS 和一个 HASH 连接(更好地满足我们的需要)。我的测试表可能比你真实的小,这就是时间更接近的原因

于 2012-11-15T13:57:53.780 回答