用户定义的函数分页速度慢,左加入 oracle 10g
pagination slow with userdefined function and left join oracle 10g
我正在尝试通过使用分页并每页显示 60 条记录来加快查询(大约 60k 行的用户列表)
这是我的查询 (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 table 并尝试 select ps_fio(sr.personid, 1),使用我的查询执行时间为 8.5 秒,而只有左连接和 sr.personid.
时为 0.6 秒
我确实在 ad_users ps_fio(u.personid, 1) 和 fc_studentrecords ps_fio(sr.personid(sr.personid, 1)
fc_studentrecords table 有大约 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 开发人员
的自动跟踪输出
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
我错过了什么吗?
从 SQL 查询内部调用 PL/SQL 函数可能会非常昂贵,因为它会导致每一行从 SQL 切换到 PL/SQL。
ad_users.person_id
似乎可以为空?如果 u.personid
或 sr.personid
是 NULL
,我将排除对 ps_fio
的调用,具体取决于这些空值的频率。
接下来,我将从函数 ps_fio
中删除 gender_int
的计算,因为它似乎不会被使用。
接下来,我将尝试用 table persons
的联接替换函数调用,并直接在查询中计算全名。丑陋,但可能会更快。
最后,您对 trim(familyname)
等的调用表明 table ps_persons 的名称列中有尾随 and/or 前导空格。速度优势微乎其微,但是否可以在 table 中完成一次清理,这样就不需要 trim 函数调用了?
您在两个 FULL TABLE SCANS
执行计划上使用了 HASH JOIN
,这不是分页的好选择。
为什么?较小的 table 必须在散列 table 中进行完整扫描和转换,并且必须在显示第一页之前探测第二个 table 的第一个块。因此,您在第一页 之前有 时间惩罚。没有人这么有耐心。
你应该有一个快速显示第一页并惩罚较高页面的执行计划。
您使用的典型值
- 访问驱动的索引table(避免排序)
- 嵌套循环连接到内部 table
- 索引访问内部table
这是一个示例(注意我使用提示来强制执行所需的访问权限)
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 |
---------------------------------------------------------------------------------------------------------
此计划return 立即访问第一页,更高的页面需要更多时间,因为您必须遍历索引。所以我的测试设置中的第 10.000 页需要几秒钟。但是,如果您有足够的耐心翻页到此为止,这将不是问题;)
请注意,我消除了函数调用 - 在大多数查询中首先调用它们是一个明智的想法,即仅针对选定的行 - 不针对跳过的行.
另请注意,在 12g 中您可以使用 Oracle supported pagination。
我正在尝试通过使用分页并每页显示 60 条记录来加快查询(大约 60k 行的用户列表) 这是我的查询 (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 table 并尝试 select ps_fio(sr.personid, 1),使用我的查询执行时间为 8.5 秒,而只有左连接和 sr.personid.
时为 0.6 秒我确实在 ad_users ps_fio(u.personid, 1) 和 fc_studentrecords ps_fio(sr.personid(sr.personid, 1)
fc_studentrecords table 有大约 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 开发人员
的自动跟踪输出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
我错过了什么吗?
从 SQL 查询内部调用 PL/SQL 函数可能会非常昂贵,因为它会导致每一行从 SQL 切换到 PL/SQL。
ad_users.person_id
似乎可以为空?如果 u.personid
或 sr.personid
是 NULL
,我将排除对 ps_fio
的调用,具体取决于这些空值的频率。
接下来,我将从函数 ps_fio
中删除 gender_int
的计算,因为它似乎不会被使用。
接下来,我将尝试用 table persons
的联接替换函数调用,并直接在查询中计算全名。丑陋,但可能会更快。
最后,您对 trim(familyname)
等的调用表明 table ps_persons 的名称列中有尾随 and/or 前导空格。速度优势微乎其微,但是否可以在 table 中完成一次清理,这样就不需要 trim 函数调用了?
您在两个 FULL TABLE SCANS
执行计划上使用了 HASH JOIN
,这不是分页的好选择。
为什么?较小的 table 必须在散列 table 中进行完整扫描和转换,并且必须在显示第一页之前探测第二个 table 的第一个块。因此,您在第一页 之前有 时间惩罚。没有人这么有耐心。
你应该有一个快速显示第一页并惩罚较高页面的执行计划。
您使用的典型值
- 访问驱动的索引table(避免排序)
- 嵌套循环连接到内部 table
- 索引访问内部table
这是一个示例(注意我使用提示来强制执行所需的访问权限)
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 |
---------------------------------------------------------------------------------------------------------
此计划return 立即访问第一页,更高的页面需要更多时间,因为您必须遍历索引。所以我的测试设置中的第 10.000 页需要几秒钟。但是,如果您有足够的耐心翻页到此为止,这将不是问题;)
请注意,我消除了函数调用 - 在大多数查询中首先调用它们是一个明智的想法,即仅针对选定的行 - 不针对跳过的行.
另请注意,在 12g 中您可以使用 Oracle supported pagination。