一条sql语句的改进探索

昨天同事找我,让我帮忙看两个sql问题,第一个问题是一个sql语句执行频率极高,但是目前的执行速度还是比较慢,希望我看看能不能调优一下。
另外一个问题是一个查询执行速度比较慢,但是执行频率不高。
我们还是循序渐进,一个一个来看。
第一个sql语句如下
5knr1ywqugtq8
/* TransactionLog_selectByAccRangeTrx_3 */
SELECT TRANSACTION_LOG.TRANS_DATE,
       TRANSACTION_LOG.TRANS_TYPE,
       TRANSACTION_LOG.PARTITION_ID,
       TRANSACTION_LOG.ACCOUNT_ID,
       TRANSACTION_LOG.TRANSACTION_ID,
       TRANSACTION_LOG.SUB_TRANSACTION_ID,
       TRANSACTION_LOG.SYS_UPDATE_DATE,
       TRANSACTION_LOG.SYS_CREATION_DATE,
       TRANSACTION_LOG.DL_SERVICE_CODE,
       TRANSACTION_LOG.APPLICATION_ID,
       TRANSACTION_LOG.DL_UPDATE_STAMP,
       TRANSACTION_LOG.OPERATOR_ID,
       TRANSACTION_LOG.PERIOD_KEY,
       TRANSACTION_LOG.ENTITY_PERIOD_KEY
  FROM TRANSACTION_LOG
 WHERE TRANSACTION_LOG.ACCOUNT_ID = :1
   AND TRANSACTION_LOG.PARTITION_ID = :2
   AND TRANSACTION_LOG.TRANSACTION_ID >= :3
   AND TRANSACTION_LOG.TRANSACTION_ID
   AND TRANSACTION_LOG.TRANS_TYPE IN
       (SELECT /*+ cardinality(1)*/
        DISTINCT column_value as transType
          FROM table (SELECT CAST(:5 AS ar1_ Varchar2Array_tp) FROM DUAL))
执行计划如下,从执行计划来看,看起来执行计划还是不错的。索引也使用到了,而且消耗也不高。
Plan hash value: 88692238
----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                         |       |       |    93 (100)|          |       |       |
|   1 |  NESTED LOOPS                          |                         |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                         |                         |     1 |    81 |    93   (2)| 00:00:02 |       |       |
|   3 |    VIEW                                | VW_NSO_1                |     1 |    12 |    19   (0)| 00:00:01 |       |       |
|   4 |     HASH UNIQUE                        |                         |     1 |     2 |            |          |       |       |
|*  5 |      FILTER                            |                         |       |       |            |          |       |       |
|   6 |       COLLECTION ITERATOR PICKLER FETCH|                         |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|   7 |        FAST DUAL                       |                         |     1 |       |     2   (0)| 00:00:01 |       |       |
|   8 |    PARTITION RANGE MULTI-COLUMN        |                         |     7 |       |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|*  9 |     INDEX RANGE SCAN                   | TRANSACTION_LOG_2IX     |     7 |       |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 10 |   TABLE ACCESS BY LOCAL INDEX ROWID    | TRANSACTION_LOG         |     1 |    69 |    73   (0)| 00:00:01 |     1 |     1 |
----------------------------------------------------------------------------------------------------------------------------------
索引情况如下:
INDEX_NAME                     INDEX_TYPE UNIQUENES PAR COLUMN_LIST                    TABLE_TYPE STATUS   NUM_ROWS LAST_ANAL G            
------------------------------ ---------- --------- --- ------------------------------ ---------- ------ ---------- --------- -            

TRANSACTION_LOG_1IX            NORMAL     NONUNIQUE YES TRANSACTION_ID,TRANS_TYPE      TABLE      N/A     270774323 01-APR-15 N            
TRANSACTION_LOG_2IX            NORMAL     NONUNIQUE YES ACCOUNT_ID,TRANS_TYPE          TABLE      N/A     270776953 01-APR-15 N            
TRANSACTION_LOG_PK             NORMAL     UNIQUE    YES SUB_TRANSACTION_ID,PARTITION_ID,PERIOD_KEY TABLE      N/A     270772197 01-APR-15 N

可以看到索引还是用了TRANSACTION_LOG_2IX,即使用了字段ACCOUNT_ID,TRANS_TYPE来进行数据过滤。
从sql语句的执行和数据分布来看,还是合理的,没有什么问题。根据现在的执行情况,每次执行大约需要2秒以上。期望还是能够达到1秒以内甚至更低。
我们来看看索引TRANSACTION_LOG_1IX和TRANSACTION_LOG_2IX,其实会发现无论是使用第一个还是第二个,数据的分布还是基本平均的。
从集群因子也能看出。
TABLE_NAME           INDEX_NAME         CLUSTERING_FACTOR     BLOCKS   NUM_ROWS
----------------- -- -------------------- ----------------- ---------- ----------
TRANSACTION_LOG    TRANSACTION_LOG_1IX      183188039    2912269  270768896
TRANSACTION_LOG    TRANSACTION_LOG_2IX      237869001    2912269  270768896
TRANSACTION_LOG    TRANSACTION_LOG_PK       186148006    2912269  270768896
不过多多少少还是有些差别的。不过差别还是不大,我们还是用sql profile来看看能够给点什么建议。
很快生成了报告,可以看出,改进其实不大。大约只有不到20%的改进。
                           Original Plan  With SQL Profile  % Improved
                           -------------  ----------------  ----------
  Completion Status:            COMPLETE          COMPLETE
  Elapsed Time (s):             .000389            .00033      15.16 %
  CPU Time (s):                 .000399           .000299      25.06 %
  User I/O Time (s):                  0                 0
  Buffer Gets:                        0                 0
  Physical Read Requests:             0                 0
  Physical Write Requests:            0                 0
  Physical Read Bytes:                0                 0
  Physical Write Bytes:               0                 0
  Rows Processed:                     0                 0
  Fetches:                            0                 0
  Executions:                         1                 1
而且从改进的执行计划来看,其实主要的差别就在于索引扫描了。索引扫描从原来的range scan建议改为skip scan.
2- Using SQL Profile
--------------------
Plan hash value: 1415584969
---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                         |     1 |    81 |    93   (2)| 00:00:02 |       |       |
|   1 |  NESTED LOOPS                         |                         |     1 |    81 |    93   (2)| 00:00:02 |       |       |
|   2 |   VIEW                                | VW_NSO_1                |  8168 | 98016 |    19   (0)| 00:00:01 |       |       |
|   3 |    HASH UNIQUE                        |                         |     1 | 16336 |            |          |       |       |
|*  4 |     FILTER                            |                         |       |       |            |          |       |       |
|   5 |      COLLECTION ITERATOR PICKLER FETCH|                         |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|   6 |       FAST DUAL                       |                         |     1 |       |     2   (0)| 00:00:01 |       |       |
|   7 |   PARTITION RANGE MULTI-COLUMN        |                         |     1 |    69 |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|*  8 |    TABLE ACCESS BY LOCAL INDEX ROWID  | AR1_TRANSACTION_LOG     |     1 |    69 |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|*  9 |     INDEX SKIP SCAN                   | AR1_TRANSACTION_LOG_1IX |     1 |       |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
---------------------------------------------------------------------------------------------------------------------------------

这个时候,还是很有顾虑的,因为在生产中已经完全能禁用skip scan了,根据产品线的建议是需要禁用的,因为在实际使用中还是碰到了不少的问题,skip scan在有些场景中反而不如range scan,会有full index scan的效果。

所以即使在Hint中知名需要skip scan在生产中也是很可能会走range scan. 在数据库参数中,禁用skip scan的部分如下:
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
_optimizer_skip_scan_enabled         boolean     FALSE
不过对此,也不是没有办法,可以使用opt_param来启用,使用hint的实例如下:
/*+opt_param('_optimizer_skip_scan_enabled', 'true')*/
不过虽然可行,但是不一定我们需要确实值得这么去做。我们还是需要评估一下。
首先skip scan在一些场景中已经碰到了一些相关的问题,不能直接根据执行计划的情况进行调优。执行计划可以作为参考,但不是全部。
二来目前的执行情况来看,就算启用skip scan,收效也不大,提高20%左右尽管在1秒以上,但是还是存在问题。
三来从这个语句来看,这个查询其实还是一个很简单的语句,没有关联表,使用了索引,查询条件也不复杂,调优的空间确实有限。就算退一步来看看使用另外一个索引,其实改进空间也是不大的。
这个可以简单的测试证明,这个查询没有使用transaction_id相关的索引列,其实很大一部分就是因为>=,
我们可以模拟测试一下,把=改为>=   得到的执行计划就会是下面的样子
----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                    | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                         |     1 |    81 |    93   (2)| 00:00:02 |       |       |
|   1 |  NESTED LOOPS                          |                         |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                         |                         |     1 |    81 |    93   (2)| 00:00:02 |       |       |
|   3 |    VIEW                                | VW_NSO_1                |     1 |    12 |    19   (0)| 00:00:01 |       |       |
|   4 |     HASH UNIQUE                        |                         |     1 |     2 |            |          |       |       |
|*  5 |      FILTER                            |                         |       |       |            |          |       |       |
|   6 |       COLLECTION ITERATOR PICKLER FETCH|                         |  8168 | 16336 |    19   (0)| 00:00:01 |       |       |
|   7 |        FAST DUAL                       |                         |     1 |       |     2   (0)| 00:00:01 |       |       |
|   8 |    PARTITION RANGE MULTI-COLUMN        |                         |     7 |       |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|*  9 |     INDEX RANGE SCAN                   | TRANSACTION_LOG_2IX     |     7 |       |    73   (0)| 00:00:01 |KEY(MC)|KEY(MC)|
|* 10 |   TABLE ACCESS BY LOCAL INDEX ROWID    | TRANSACTION_LOG         |     1 |    69 |    73   (0)| 00:00:01 |     1 |     1 |
----------------------------------------------------------------------------------------------------------------------------------
改进空间还是很有限的。
对此我的建议还是能够从多个方面来考虑。
首先就是索引,目前存在3个索引,其实TRANSACTION_LOG_1IX和TRANSACTION_LOG_2IX都是非唯一性索引,如果能够使用TRANSACTION_LOG_PK其实还是有很大的改进空间。和开发同事的交流来看,他们认为改动难度较大,需要改动的代码量很大,可能需要的时间也较长。但是也不失为一种方式。所以这个方案也是可行但是能不能落地还是一个问号。毕竟在双方的一个权衡中也需要很多的协调。
第二个改进就是和开发的交流和自己抓取的一些信息,发现transaction_id的取值范围还是很大,这也是数据过滤较慢的一个原因。可以从执行计划的谓词信息中看出来一些。首先是根据account_id来进行定位,但是数据过滤是根据transaction_id来做的。做这个时候
   9 - access("TRANSACTION_LOG"."ACCOUNT_ID"=:1 AND "TRANSACTION_LOG"."TRANS_TYPE"="TRANSTYPE")
  10 - filter(("TRANSACTION_LOG"."TRANSACTION_ID">=:3 AND "TRANSACTION_LOG"."TRANSACTION_ID"
              "TRANSACTION_LOG"."PARTITION_ID"=:2))
所以经过讨论一种可行的效果就是能够不尽可能的缩短transaction_id的范围。使得transaction_id的范围尽量小一些,数据过滤就会更加高效。这个从前端来说还是比较容易来实现的。
经过验证发现速度还是比较理想的,调整了范围之后,速度都在毫秒。

Elapsed: 00:00:00.70

时间: 2024-08-02 12:01:25

一条sql语句的改进探索的相关文章

一条sql语句的建议调优分析

前几天开发的同事问我一个sql的问题,目前在测试环境中发现这条sql语句执行时间很长,希望我们能够给一些建议,能够尽快做一些改进.sql语句类似下面的形式.SELECT /*+ INDEX(ACCOUNT,ACCOUNT_PK)INDEX(ACCOUNT_EXT ACCOUNT_EXT_PK) */ ACCOUNT.ACCOUNT_ID, ACCOUNT.BE, ACCOUNT.CUSTOMER_NO, ACCOUNT.AR_BALANCE, ACCOUNT_EXT.CYCLE_CODE, AC

通过一条sql语句访问不同数据库服务器中的数据库对象的方法

对象|访问|服务器|数据|数据库|语句 在我们做数据库程序开发的时候,经常会遇到这种情况:需要将一个数据库服务器中的数据导入到另一个数据库服务器的表中.通常我们会使用这种方法:先把一个数据库中的数据取出来放到某出,然后再把这些数据一条条插入到目的数据库中,这种方法效率较低,写起程序来也很繁琐,容易出错.另外一种方法是使用bcp或BULK INSERT语句,将数据导入到一个文件中,再从此文件中导出到目的数据库,这种方法虽然效率稍高,但也有很多不如意的地方,单是在导入时怎样找到另外一台机器上的数据导

sql oracle-关于怎么快速执行10000条sql语句

问题描述 关于怎么快速执行10000条sql语句 由于我的数据库有几千万条数据,每一条查询都会花费0.5秒,但是10000条查询需要半个多小时,所以希望有快速一点的方法,求各位大神指点,下面是我的函数. /** * 这是一个横着的for循环,图的缩放级别是13,11*10方格,不同区域到不同区域的上车点数量 */ public static void CountListPointsOfOnetoOne() { ArrayList ListSql = new ArrayList(); double

sqlserver-求各位大神给一条sql语句,急急急!!!

问题描述 求各位大神给一条sql语句,急急急!!! 我用的是sqlserver数据库,我希望完成这样的功能,有个数据库Test,里面有三张表ABC,我对其中的一张A表进行的分区,另外两张表没有分区,我希望通过sql语句查询到已经分区的表,就是要返回表A,只需获得表A的名称就行了.

把这四条sql语句从sqlsever变成oracle的sql语句,求帮忙

问题描述 把这四条sql语句从sqlsever变成oracle的sql语句,求帮忙 1C 1.SELECT top 1 MENUID+1 from app_menu ORDER BY CAST(MENUID AS int) desc 2.SELECT top 1 idfileNamefilePathcreateTimemd5fileSize FROM VIS_file WHERE md5=#md5# 3. SELECT top 1 USERIDREALNAMEGENDEROTELOEMAILQQ

请帮忙写一条SQL语句查询前一小时信息

问题描述 请帮忙写一条SQL语句查询前一小时信息 MSSQLServer,有个字段DateTime:2015-03-09 16:08:51.617 我想查询前一小时的信息,因为我公司是45分下班的,所以前一小时应该是14:45:00~14:45:00. 请问如何写一条语句可以查到当前小时的前一小时的信息呢?谢谢帮忙. 解决方案 select * from table where DATEDIFF('h',DateTime,now())>1 and DATEDIFF('h',DateTime,no

使用一条sql语句实现多表查询

问题描述 使用一条sql语句实现多表查询 假设有这样3张表: 学生表S (id,Tid,name,sex,age) 教师表T (id,Sid,name,sex,age) 成绩表R (id(学生的id),result(成绩)) 怎样使用一条sql查询成绩大于90的男学生的男性老师的姓名 解决方案 select * from R inner join S on R.id=S.Sid inner join T on xxxxxx where result>90 你学生和老师好像没有对应关系,所以第二个

sql查询-求帮忙优化我这条sql语句

问题描述 求帮忙优化我这条sql语句 首先说一下下面sql语句要用到的表的基本情况: 一.有三张表,一张叫equipment,一张叫sensor,一张叫sensorInputData 二.三表关系是equipment下有多个sensor,sensor下有多个sensorInputdata,所以sensor下有一个equipment的主键quipmentId做外键,sensorInputData下也有一个sensor的主键sensorId做外键,其他都是各自表的属性,应该能比较清晰的看出来 下面是

sql server-求一条sql语句 sqlserver数据库

问题描述 求一条sql语句 sqlserver数据库 刷卡表,有ID,刷卡时间.时间等并和用户表形成多对一关系,使用的是sqlserver. 我要取得是"昨天,每个用户的最大刷卡时间和最小刷卡时间的刷卡数据". 我的SQL不是很好,昨天.最大刷卡时间和最小都能搞出来,但是加个每个用户就乱了: 因为每个用户都可能会有多条刷卡数据. ...求解 解决方案 select b.username, max(刷卡时间) 最大刷卡时间, min(刷卡时间) 最小刷卡时间 from 刷卡表 a inn