使用userdefined函数加速分页并离开加入oracle 10g

时间:2018-06-06 09:52:52

标签: sql oracle pagination oracle10g

我正在尝试使用分页加速查询(大约6万行的用户列表),并且每页显示60条记录  这是我的查询(oracle 10g)

select * 
  from ( select 
  a.*, ROWNUM rnum 
      from ( select u.username,u.userfullname,u.usercomment,u.isInner,u.isTeacher,u.isEmployer,u.deleted, 
     ps_fio(u.personid, 1) teachername, 
  ps_fio(sr.personid, 1) studentname,  
                case when u.isInner=1 then '' end Inn,  
                case when u.isStudent=1 then '' end Stud,  
                case when u.isTeacher=1 then '' end Teach from AD_Users u  
                left join fc_studentrecords sr on sr.recordid=u.studentid 
    order by u.username) a 
      where ROWNUM <= 120) 
where rnum  > 60; 

问题是,一旦我将左连接放到fc_studentrecords表并尝试选择ps_fio(sr.personid,1),使用我的查询执行时间为8.5秒,相比之下只有左连接和sr为0.6秒.personid。

我确实在ad_users上添加了2个基于函数的索引ps_fio(u.personid,1)和fc_studentrecords ps_fio(sr.personid,1)

fc_studentrecords表有大约80K行

这里是ps_fio函数的代码(基本上它显示给出personid的人姓名)

  create or replace FUNCTION PS_FIO(PerId PS_PERSONS.PERSONID%Type, FioType NUMBER) RETURN VARCHAR2 DETERMINISTIC IS 
    result VARCHAR2(70);
    gender_int NUMBER ;


    BEGIN
    select decode(m.message_id,'SEX_MALE',1,0) into gender_int
    from ps_persons p
    join rb_messages m on m.message_value=p.sex
    where personid=PerId;  

    BEGIN
    select case  
    when fiotype=0 then trim(familyname)||' '||trim(firstname)||'    '||trim(secondname)
      when fiotype=1 then trim(familyname)||' '||substr(firstname, 1, 1)||'. '||substr(secondname, 1, 1)||'.'
      else '' end into result
     from ps_persons
      where personid=PerId;  
     EXCEPTION
      WHEN no_data_found then result:= '';
     END;
    return result;
    END PS_FIO;


here are the create table scripts for ad_users  and fc_studenrecords table

  CREATE TABLE "COPYREAL"."AD_USERS" 
   (    "USERNAME" VARCHAR2(25 CHAR), 
    "USERFULLNAME" VARCHAR2(100 CHAR) NOT NULL ENABLE, 
    "USERCOMMENT" VARCHAR2(200 CHAR), 
    "PWD" RAW(16), 
    "PERSONID" NUMBER(10,0), 
    "ISINNER" NUMBER(1,0) DEFAULT 1 NOT NULL ENABLE, 
    "ISTEACHER" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "ISSTUDENT" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "STUDENTID" NUMBER(10,0), 
    "DELETED" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "ISEMPLOYER" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "USERCODE" NUMBER(5,0), 
    "MOBILE_NUMBER" NUMBER(10,0), 
     CONSTRAINT "PK_AD_USERS" PRIMARY KEY ("USERNAME")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "SYSTEM"  ENABLE, 
     CONSTRAINT "CH_USER_PERSON" CHECK (personid is not null or isTeacher=0) ENABLE, 
     CONSTRAINT "CH_USER_STUDENT" CHECK (studentid is not null or isStudent=0) ENABLE, 
     CONSTRAINT "UK_USERCODE" UNIQUE ("USERCODE")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS"  ENABLE, 
     CONSTRAINT "FK_USER_PERSON" FOREIGN KEY ("PERSONID")
      REFERENCES "COPYREAL"."PS_PERSONS" ("PERSONID") ENABLE, 
     CONSTRAINT "FK_USER_STUDENT" FOREIGN KEY ("STUDENTID")
      REFERENCES "COPYREAL"."FC_STUDENTRECORDS" ("RECORDID") ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "SYSTEM" ;

  CREATE INDEX "COPYREAL"."AD_USERS_FIO_IDX" ON "COPYREAL"."AD_USERS" ("COPYREAL"."PS_FIO"("PERSONID",1)) 
  PCTFREE 10 INITRANS 2 MAXTRANS 167 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_USER_PERSON" ON "COPYREAL"."AD_USERS" ("PERSONID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_USER_STUDENT" ON "COPYREAL"."AD_USERS" ("STUDENTID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;






 CREATE TABLE "COPYREAL"."FC_STUDENTRECORDS" 
   (    "RECORDID" NUMBER(10,0), 
    "PERSONID" NUMBER(10,0) NOT NULL ENABLE, 
    "BOOKNO" VARCHAR2(15 CHAR), 
    "BOOKDATE" DATE, 
    "DIPLOMANO" VARCHAR2(15 CHAR), 
    "PLANID" NUMBER(10,0) NOT NULL ENABLE, 
    "DOCDATE" DATE, 
    "DOCNO" VARCHAR2(15 CHAR), 
    "DOCSUM" NUMBER(15,2), 
    "BRANCHID" NUMBER(4,0) DEFAULT 1 NOT NULL ENABLE, 
    "STATEMENTID" NUMBER(10,0), 
    "DOCPRIVTYPE" NUMBER(2,0), 
    "DOCPRIVPER" NUMBER(3,0), 
    "PREVDOC" VARCHAR2(30 CHAR), 
    "ENTERED" VARCHAR2(30 CHAR), 
    "STUDYFORMID" NUMBER(1,0), 
    "BUDGET" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "PLACE" VARCHAR2(40 CHAR), 
    "PAID_SUM" NUMBER(15,2), 
    "FINAL_PAYMENT_DATE" DATE, 
    "PAID_BY_CAPITAL" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE, 
    "USERNAME" VARCHAR2(25 CHAR), 
    "INDIV_STUDENTID" NUMBER(10,0), 
    "CLIENT" VARCHAR2(70 CHAR), 
     CONSTRAINT "PK_FC_STUDENTRECORDS" PRIMARY KEY ("RECORDID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 720896 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS"  ENABLE, 
     CONSTRAINT "UK_BOOKNO" UNIQUE ("BOOKNO")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 917504 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS"  ENABLE, 
     CONSTRAINT "UK_FC_STYDENTRECORDS" UNIQUE ("PERSONID", "PLANID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 2097152 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS"  ENABLE, 
     CONSTRAINT "FK_FCDSTUDRECS_RBCOMMONSTFORMS" FOREIGN KEY ("STUDYFORMID")
      REFERENCES "COPYREAL"."RB_COMMONSTUDYFORMS" ("FORMID") ENABLE, 
     CONSTRAINT "FK_STUDENTRECORDS_DEPARTMENTS" FOREIGN KEY ("BRANCHID")
      REFERENCES "COPYREAL"."RB_DEPARTMENTS" ("CODE") ENABLE, 
     CONSTRAINT "FK_PS_PERSONS" FOREIGN KEY ("PERSONID")
      REFERENCES "COPYREAL"."PS_PERSONS" ("PERSONID") ENABLE, 
     CONSTRAINT "FK_FC_STUDENTREC_PL_EDUCPLANS" FOREIGN KEY ("PLANID")
      REFERENCES "COPYREAL"."PL_EDUCPLANS" ("PLANID") ENABLE, 
     CONSTRAINT "FK_FS_STUDREC_FC_PRIVEL" FOREIGN KEY ("DOCPRIVTYPE")
      REFERENCES "COPYREAL"."FC_PRIVILEGETYPES" ("PRIVILEGEID") ENABLE, 
     CONSTRAINT "FK_FC_STUDENTRECORDS_AD_USERS" FOREIGN KEY ("USERNAME")
      REFERENCES "COPYREAL"."AD_USERS" ("USERNAME") ENABLE, 
     CONSTRAINT "FK_FCSTUDRECS_ENSTATEMENTS" FOREIGN KEY ("STATEMENTID")
      REFERENCES "COPYREAL"."EN_STATEMENTS" ("STATEMENTID") ENABLE, 
     CONSTRAINT "FK_FC_STUDREC_IP_STUDENTS" FOREIGN KEY ("INDIV_STUDENTID")
      REFERENCES "COPYREAL"."IP_STUDENTS" ("STUDENTID") ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 3145728 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;



  CREATE INDEX "COPYREAL"."FC_STUDENTRECORDS_FIO_IDX" ON "COPYREAL"."FC_STUDENTRECORDS" ("COPYREAL"."PS_FIO"("PERSONID",1)) 
  PCTFREE 10 INITRANS 2 MAXTRANS 167 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."FC_STUDENTRECORDS_FIO_IDX2" ON "COPYREAL"."FC_STUDENTRECORDS" ("COPYREAL"."PS_FIO"("PERSONID",0)) 
  PCTFREE 10 INITRANS 2 MAXTRANS 167 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FC_STUDENTRECORDS" ON "COPYREAL"."FC_STUDENTRECORDS" ("PERSONID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 2097152 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_FCDSTUDRECS_RBCOMMONSTFO" ON "COPYREAL"."FC_STUDENTRECORDS" ("STUDYFORMID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_FCSTUDRECS_ENSTATEMENTS" ON "COPYREAL"."FC_STUDENTRECORDS" ("STATEMENTID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_FC_STUDENTREC_PL_EDUCPLA" ON "COPYREAL"."FC_STUDENTRECORDS" ("PLANID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_FS_STUDREC_FC_PRIVEL" ON "COPYREAL"."FC_STUDENTRECORDS" ("DOCPRIVTYPE") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;


  CREATE INDEX "COPYREAL"."IX_FK_STUDENTRECORDS_DEPARTMEN" ON "COPYREAL"."FC_STUDENTRECORDS" ("BRANCHID") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "USERS" ;

这是sql developer

的autotrace输出
buffer is not pinned count  207278
bytes received via SQL*Net from client  880
bytes sent via SQL*Net to client    20975
calls to get snapshot scn: kcmgss   207199
consistent gets 674851
consistent gets - examination   310692
consistent gets from cache  674851
CPU used by this session    890
CPU used when call started  898
cursor authentications  4
DB time 914
enqueue releases    5
enqueue requests    5
execute count   103599
index fetch by key  103590
no work - consistent read gets  260481
opened cursors cumulative   7
OS Block input operations   872
OS Involuntary context switches 62
OS Page faults  2
OS Page reclaims    1714
OS System time used 159
OS User time used   739
OS Voluntary context switches   39
parse count (hard)  5
parse count (total) 7
parse time cpu  1
parse time elapsed  1
recursive calls 103634
recursive cpu usage 687
rows fetched via callback   103512
session logical reads   674851
session pga memory  -327680
shared hash latch upgrades - no wait    78
sorts (memory)  3
sorts (rows)    53897
sql area evicted    1
SQL*Net roundtrips to/from client   9
table fetch by rowid    103600
table fetch continued row   78
table scan blocks gotten    260393
table scan rows gotten  1948362
table scans (short tables)  51797
user calls  11
workarea executions - optimal   7

explain plan output

autotrace output

我错过了什么吗?

2 个答案:

答案 0 :(得分:1)

从SQL查询中调用PL / SQL函数可能非常昂贵,因为它会导致每行从SQL切换到PL / SQL。

ad_users.person_id似乎可以为空?如果ps_fiou.personidsr.personid,我会排除对NULL的来电,具体取决于这些空值的频率。

接下来,我会从函数gender_int中删除ps_fio的计算,因为它似乎没有被使用。

接下来,我尝试用表persons的连接替换函数调用,并直接在查询中计算全名。丑陋,但可能会更快。

最后,您对trim(familyname)等的调用表明表ps_persons的名称列中有尾随和/或前导空格。速度优势很小,但是可以在表格中进行一次清理,这样修剪函数调用不是必需的吗?

答案 1 :(得分:0)

您在两个HASH JOIN执行计划中使用FULL TABLE SCANS分页

为什么呢?必须在哈希表中完整扫描并转换较小的表,并且必须探测第二个表的第一个块,以便显示第一页。因此,您在第一页之前有时间惩罚。没有人这么耐心。

您应该有一个执行计划,可以快速快速显示第一页并惩罚较高页面。

典型的你使用

  • 访问驱动表的索引(以避免排序)
  • 嵌套循环连接到内部表
  • 对内部表的索引访问

这是一个例子(注意我使用提示来强制执行所需的访问)

select * 
  from ( select 
  a.*, ROWNUM rnum 
      from ( select /*+ ordered use_nl(u sr) index(u AD_Users_idx) */ u.username,u.userfullname,u.usercomment,u.isInner,u.isTeacher,u.isEmployer,u.deleted, 
--  
                case when u.isInner=1 then '' end Inn,  
                case when u.isStudent=1 then '' end Stud,  
                case when u.isTeacher=1 then '' end Teach from AD_Users u  
                left join fc_studentrecords sr on sr.recordid=u.studentid 
    order by u.username) a 
      where ROWNUM <= 120) 
where rnum  > 60; 

确保列username已编入索引且 NOT NULL 能够(索引访问所需)。 列recordid也必须编入索引。

预期的执行计划如下:

---------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name                  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                       |   100K|    16M|  2022K  (1)| 06:44:26 |
|*  1 |  VIEW                           |                       |   100K|    16M|  2022K  (1)| 06:44:26 |
|*  2 |   COUNT STOPKEY                 |                       |       |       |            |          |
|   3 |    VIEW                         |                       |  1000K|   150M|  2022K  (1)| 06:44:26 |
|   4 |     NESTED LOOPS OUTER          |                       |  1000K|   182M|  2022K  (1)| 06:44:26 |
|   5 |      TABLE ACCESS BY INDEX ROWID| AD_USERS              |  1000K|   169M| 19871   (1)| 00:03:59 |
|   6 |       INDEX FULL SCAN           | AD_USERS_IDX          |  1000K|       |   272   (1)| 00:00:04 |
|*  7 |      INDEX RANGE SCAN           | FC_STUDENTRECORDS_IDX |     1 |    13 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

此计划立即返回第一页,较高的页面需要更多时间,因为您必须遍历索引。所以我的测试设置中的页面10.000需要几秒钟。但是如果你有足够的耐心去寻找到目前为止,这将是没有问题的;)

请注意,我删除了函数调用 - 在大多数查询中首先调用它们是明智的,即仅针对选定的行 - 不适用于跳过的行

另请注意,在12g中,您可以使用Oracle supported pagination