问题描述

在之前对项目中的一些查询sql进行优化时,为了减少主查询返回数据量,将主查询的排序分页逻辑移到sql内部。大致情况如下:

## 调整前sql
SELECT *
FROM (SELECT a.*, rownum AS row_id
      FROM (SELECT a.*, b.*
            FROM table_a a, table_b b
            WHERE a.id = b.aid(+)
            ORDER BY a.id) a
      WHERE rownum <= 100) a
WHERE row_id > 0
###调整后sql
SELECT a.*, b.*
FROM (SELECT *
      FROM (SELECT a.*, rownum AS row_id
            FROM (SELECT /*+ parallel(8) */
                   *
                  FROM table_a
                  ORDER BY a.id) a
            WHERE rownum <= 100) a
      WHERE row_id > 0) a,
     table_b b
WHERE a.id = b.aid(+)

调整后发现查询排序出现了混乱。(结果值正常,但结果值的排序不正常)

问题分析

查看调整后sql的执行计划

SELECT STATEMENT, GOAL = ALL_ROWS			5	100	71200	1	4	36059849	0
 PX COORDINATOR									1
  PX SEND QC (RANDOM)	SYS	:TQ20002	5	100	71200	1	4	36059849	2
   HASH JOIN OUTER BUFFERED			5	100	71200	1	4	36059849	3
    BUFFER SORT									4
     PX RECEIVE			3	100	63800	1	2	35456373	5
      PX SEND HASH	SYS	:TQ20000	3	100	63800	1	2	35456373	6
       VIEW	TAQUERY		3	100	63800	1	2	35456373	7
        COUNT STOPKEY									8
         FILTER									9
          PX COORDINATOR									10
           PX SEND QC (ORDER)	SYS	:TQ10001	3	119	75922	1	2	35456373	11
            VIEW	TAQUERY		3	119	75922	1	2	35456373	12
             SORT ORDER BY			3	119	28679	1	2	35456373	13
              PX RECEIVE			2	119	28679	1	2	26610	14
               PX SEND RANGE	SYS	:TQ10000	2	119	28679	1	2	26610	15
                PX BLOCK ITERATOR			2	119	28679	1	2	26610	16
                 TABLE ACCESS FULL	TABASE	TA_TFUNDINFO	2	119	28679	1	2	26610	17
    PX RECEIVE			2	1	74	1	2	51	4
     PX SEND HASH	SYS	:TQ20001	2	1	74	1	2	51	5
      PX BLOCK ITERATOR			2	1	74	1	2	51	6
       TABLE ACCESS FULL	TABASE	TA_TTAINFO	2	1	74	1	2	51	7

发现有这么一项
PX SEND QC (RANDOM)

QC (RANDOM)
The query coordinator (QC) consumes the input randomly. Used when the statement does not have an ORDER BY clause.

即外部没有排序时,查询协调器(QC)会将结果按随机顺序输出。

因此是由于将order by排序置于sql内侧,且增加了parallel并发执行指令所致。

解决方案

在sql外层增加相同规则排序(需要将排序字段查询至外层)

### 增加外层排序
SELECT a.*, b.*
FROM (SELECT *
      FROM (SELECT a.*, rownum AS row_id
            FROM (SELECT /*+ parallel(8) */
                   *
                  FROM table_a
                  ORDER BY a.id) a
            WHERE rownum <= 100) a
      WHERE row_id > 0) a,
     table_b b
WHERE a.id = b.aid(+)
order by a.id
01-03 14:59