在参数Amounttoroll位置输入要修改的值,下面列出了所有Amounttoroll的参数解释:H=Half->按PgUp或PgDn键后,屏幕滚动半页。
F=Full->按PgUp或PgDn键后,屏幕滚动整页。
C=Cursor->按PgUp、PgDn键或F19、F20后,屏幕按照光标位置进行滚动。
D=Data->按PgUp、PgDn键或F19、F20后,屏幕按照数据位置进行滚动。
1-999->按PgUp、PgDn键或F19、F20后,屏幕按照选定的具体值进行滚动,例如,若此值为10,则进行翻页时,一次翻10行。
2.修改字体大小写输入方式。在缺省情况,PDM只可按大写方式输入,但有时要输入小写字符,可以按F13进入“ChangeSessionDefaults”
画面。修改“Uppercaseinputonly”参数为N,即大小写都准许。
3.修改行序号的步长在“Incrementofinsertrecord”位置添入行序号的步长。例如:Incrementofinsertrecord.....--2.00--0.01-999.99如下图所示:修改前的步长为1,修改后的补偿为2。4.全屏显示在“Fullscreenmode”位置输入“Y”,PDM的编辑画面变成全屏显示。
librarylist分为systemlibraries;productlibraries;currentlibrary;userlibrary四个部分。其中,systemlibraries可以用QSYSLIBL系统值决定,也可以用CHGSBSD进行修改;userlibrary可以用QUSRLIBL决定,也可以用CHGJOBD进行修改。currentLibrary一般由userprofile决定,也可以在signon时进行指定。而productlibraries是在执行CL程序的时候由系统自动引入的,例如在编译RGP程序的时候会自动引入QRPG库等。
系统作业是在系统IPL时初始化的,但是,只要OS/400系统处于活动状态,作业QWCBTCLNUP和QDCPOBJx也将与之同样保持活动。系统作业不能被用户启动,停止或改变(除了少数系统作业可以用命令CHGSYSJOB来改变,并且唯一可以由该命令改变的属性就是这些系统作业的运行优先级。)
以下就是介绍所有系统作业的详细信息:
--QALERT-AlertManager报警管理该系统作业使作业能够处理报警,包括处理从其他系统上接收到的报警,本机产生的报警,并且维护系统的控制。QALERT的作业运行优先级可以用命令CHGSYSJOB改变。该作业在OS/400R220时就被加入到操作系统中,用于支持报警信息的处理。
系统值QCMNARB决定有多少通讯仲裁作业启动。如果QCMNARB设置成*CALC,系统将为每一个处理器启动两个通讯仲裁作业,如果QCMNARB设置成0,该作业将在QSYSARB中运行。QCMNARBxx的作业运行优先级可以用命令CHGSYSJOB改变。在作业QCMNARBxx被加入OS/400R420以前,这些工作是在QSYSARB中运行的。
--QDBSRVXR-DatabaseCross-Reference数据库交叉参照该作业是用于维护QSYS中的文件级的系统交叉参照文件,这些文件中存放着数据库文件的交叉参照信息,并且这些文件是库QSYS中以QADB开头的。首要被维护的文件是文件交叉参照文件QADBXREF,它包含了数据库中每一个物理文件,逻辑文件,DDM文件,ALIAS文件的记录。当一个文件被创建,改变,删除,恢复,更名,或者改变所有权时,作业QDBSRVXR就会被激活。该作业在OS/400R310被加入操作系统。
--QDBSRV01-DatabaseServer数据库服务器该作业可以被视为数据库维护任务的分配器。通常地,当恢复一个包含数据库文件的库时,QDBSRV01将立刻被激活。它的功能包括:*发信号给SMAPP(系统管理的访问路径保护机制),告知新的访问路径被恢复,并且由AMAPP来决定是否需要被保护。*由于访问路径没有被恢复时,该作业准备需要被重新创建的访问路径的列表。该列表可以用命令EDTRBDAP浏览。
数据库服务器作业的数量是(1+2*CPU数)与(1+2*ASP数)两者中较大的那一个。最少启动的数量是5。QDBSRV01是主要的系统作业,用于分配其他作业工作,剩下的作业,一半是用来处理高优先级的请求,另一半是用来处理低优先级的请求。例如:在一个有4路CPU的系统上,应该有9个QDBSRVxx作业,由QDBSRV01来分配工作,QDBSRV02-05用来处理高优先级的作业,QDBSRV06-09用来处理低优先级的作业。该作业是在OS/400R210被加入操作系统的。
--QDBSRVxx-DatabaseServer(highpriority)这些作业维护系统的日志和提交控制。
--QDBSRVxx-DatabaseServer(lowpriority)这些作业用于维护用户的数据库文件的访问路径。这些作业通常不活动,但是,在特定的条件下,它们被激活用于访问路径的重建。这些被重建的访问路径可以用命令EDTRBDAP浏览。这些作业被激活的原因包括:*恢复那些没有保存访问路径的数据库文件;*恢复那些没有物理文件的逻辑文件;*取消正在运行的命令RGZPFM;*由于被破坏而成为无效的索引;*加速OS/400安装来完成交叉参照,或者其他DB2升级的动作;*验证约束。用命令EDTCPCST可以显示那些正在等待验证的约束。
--QDCPOBJx-DecompressSystemObject系统对象解压缩这些作业是用于对新安装的操作系统对象进行解压缩的,它们的运行是需要一定的空间的。当剩余您的系统空间低到一定程度,这些作业就会结束。系统对象解压缩作业的数量=CPU数+1。该作业是在OS/400R210被加入操作系统的。
--QFILESYS1-FileSystem文件系统该作业用于支持文件系统作业的后台运行,它保证对文件的改动都写入存储介质中,并执行常规的文件系统清除活动。该作业是在OS/400R210被加入操作系统的。
--QJOBSCD-JobSchedule作业调度该作业控制作业调度功能,并由系统监控作业调度项的计时器和调度的作业。该作业是在OS/400R220被加入操作系统的。
--QLUR-LU6.2Resynchronization(LU6.2重新同步)该作业处理两段式提交的重新同步。该作业是在OS/400R310被加入操作系统的,并作为LU6.2通讯协议的一部分。
--QLUS-LogicalUnitServices逻辑单元服务该作业处理为逻辑单元设备(通讯设备)处理的事件,它还负责将设备定位到正确的通讯子系统中去。QLUS是AS/400上原始的系统作业之一。
--QPFRADJ-PerformanceAdjustment性能调整性能调整系统作业管理存储池大小和活动级别的变化。所有要求改变存储池大小的请求都有该作业处理。此外,如果系统值QPFRADJ设为2或3,该作业将动态地改变存储池大小和活动级别,由此提高系统的性能。在OS/400R430,IPL性能调整(系统值QPFRADJ设为1或2)是由QPFRADJ来处理的,在OS/400R430前,IPL性能调整由SCPF处理。QPFRADJ是在OS/400R210被加入操作系统的。
--QSPLMAINT-SystemSpoolMaintenance假脱机管理该作业实现维护系统假脱机的功能。
--QSYSARB-SystemArbiter系统仲裁系统仲裁提供运行高优先权功能的环境,它处理系统资源并保持对系统状态的追踪。QSYSARB是操作系统核心作业。系统仲裁负责系统一级的,需要立即处理和能更有效地被一个作业处理的任务,系统仲裁还负责处理通讯请求,设备锁定,线/控制器/设备的配置。系统仲裁是OS/400最原始的作业之一。
--QSYSCOMM1-SystemCommunications系统通讯该作业处理系统通讯和I/O。QSYSCOMM1的作业优先权可以用命令CHGSYSJOB改变,它是在OS/400R320中被加入操作系统的。
--QWCBTCLNUP-JobTableCleanup作业表清空QWCBTCLNUP是用于在IPL过程中确保作业结构可以被使用。通常在IPL结束时,该作业也结束处理了,但是,如果有许多作业结构需要清除,它可以在IPL结束后继续运行。该系统作业在处理完成后就结束了。QWCBTCLNUP是在OS/400R210中当作业表的清除从SCPF中被移走后加入系统的。
--Q400FILSVR-RemoteFileSystemCommunications远程文件系统通讯该作业处理远程文件系统APPN/APPC的通用编程接口通讯。Q400FILSVR是在OS/400R360中作为远程文件支持被加入操作系统的。
--SCPF-StartControlProgramFunction启动控制程序功能该作业是操作系统IPL是的中心处理任务,提供启动OS/400的环境和所直接需要的功能。SCPF启动所有其他系统作业(除了QLUS),并将系统带入可用状态。SCPF是OS/400最原始的作业之一。
2、使用命令WRKOBJLCKOBJ(QDOC/sysobjname)OBJTYPE(*DOC),其中,sysobjname为上一条命令输出结果中的Systemobjectname值。
---------------------------------------------------------------------------------------------WorkwithActiveJobs------------------S102xxxx-----------------------------------------------------11/30/0313:24:38CPU%:.3---Elapsedtime:00:13:10---Activejobs:116
Typeoptions,pressEnter.2=Change3=Hold4=End5=Workwith6=Release7=Displaymessage8=Workwithspooledfiles13=Disconnect...
OptSubsystem/Job-User---Type--CPU%-Function------StatusQBATCH------------QSYS---SBS---.0-----------------DEQWQCMN--------------QSYS---SBS---.0------------------DEQWQCTL--------------QSYS---SBS---.0------------------DEQWQSYSSCD-----------QPGMR--BCH---.0---PGM-QEZSCNEP--EVTWQINTER------------QSYS---SBS---.0------------------DEQWQPADEV000F--------test---INT---.1---CMD-WRKACTJOB-RUNQSERVER-----------QSYS---SBS---.0------------------DEQWQPWFSERVSD-------QUSER--BCH---.0------------------SELWQSERVER-----------QPGMR--ASJ---.0------------------EVTWMore...Parametersorcommand===>F3=ExitF5=RefreshF7=FindF10=RestartstatisticsF11=DisplayelapseddataF12=CancelF23=MoreoptionsF24=Morekeys---------------------------------------------------------------------
2、强大的PF功能键使用PF24,用户可以发现,WRKACTJOB提供一些有用的PF功能:
例,PF7用于发现含某些字符的作业---------------------------------------------------------------------Findastring
String........SHANJDColumn........*USER---*SBS,*JOB,*USER,*NUMBER,--------------------------------*TYPE,*STS,*FUNCTION,*PTY--------------------------------*POOL
F8=RepeatfindF12=CancelF17=TopF18=Bottom----------------------------------------------------------------------
PF20用于只列出符合条件的作业:-----------------------------------------------------------------------------------------------SubsetActiveJobs
Typechoices,pressEnter.Sequence........*SBS--------F4forlistJobname........*ALL--------Name,*ALL,*SYS,*SBSCPUpercentlimit....*NONE------.1-99.9,*NONEResponsetimelimit...*NONE------.1-999.9,*NONESubsystems*ALL
------------------------------------------------------------------------
1.FTPAS400-IP地址2.输入用户名,回车,再输入口令.3.CD/qsys.lib/***.lib,***为AS/400存放SAVF库名.4.LCDPC机本地存放SAVF的目录5.BIN6.QUOTESITENA17.PUTpcSAVF.savfasSAVF.savf,其中:pcSAVF为PC中SAVF文件名;asSAVF为AS/400中文件名.8.QUIT
//Loopthroughallrecordsoffilereadfile;
downot%eof(file);//Processuntilendoffileif%error;dsply'Readerror:processaborting.';leave;else;pos=%scan(',':name);ifpos>0;firstname=%trimr(%subst(name:1:pos-1));updatefile;endif;readfile;enddo;
/end-freeC*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1)parm1(7S0)=276,parm2(7S2)=15.73
传递方式:CALLPGMAPARM('0000276''0001573')
2)parm1(7S0)=-276,parm2(6S2)=-15.73
传递方式:CALLPGMAPARM('-000276''-01573')
3)parm1(7P0)=276,parm2(7P2)=15.73
传递方式:CALLPGMAPARM(X'0000276F'X'0001573F')
4)parm1(6P0)=276,parm2(6P2)=15.73
传递方式:CALLPGMAPARM(X'F000276F'X'F001573F')
5)parm1(7P0)=-276,parm2(6P2)=-15.73
传递方式:CALLPGMAPARM(X'0000276B'X'F001573B')
总结:
对于S类型的参数,只要按照字符串的形式,在没有数字的位置补0,不用输入小数点,就可以得到正确的结果,如果是负数,在第一位输入负号-即可,但是会占用一个数字位,7位的负数就只能输入6位数字了。
对于P类型的参数,数字部分按照S类型的一样输入,还需要在字符串前加X,并且如果长度为奇数,需要给字符串加F后缀,如果长度为偶数,需要在字符串的前后都加F。如果是负数的,需要把字符串后面的F变为B。(X表示按16进制取值,由于P类型的数据存储方式是半个字节存储一个数字,所以需要在前后加其它的字母补齐空位。)
我应邀在全球范围内宣传SQL技巧。我相信您将至少学到表表达式的一种用法,而您所学到的东西将对您的应用产生一定的影响,所以还请继续读下去。
预过滤全外连接
最近几年来,随着工作文件的移除和并行性的加入,全外连接在性能方面已得到了增强。然而,其语法并没有得到像左连接和右连接所得到的那样的魔力,例如自动谓语下推和谓语传递闭包。图1中所显示的全外连接没有产生预期的结果,例如,给出在指定日期范围内的所有订单,不管这些订单是否具有少于40个字符的描述,以及给出所有少于40个字符的描述,而不管是否有与之对应的订单:
图1.该语句不会产生预期的结果
SELECTCOALESCE(O.ORDER_NO,'OrderNumbernotAvailable'),COALESCE(D.DESC,'DescriptionnotAvailable')FROMORDEROFULLJOINDESCRIPTIONSDONO.ORDER_NO=D.ORDER_NOWHEREO.ORDER_DATEBETWEEN'1999-01-01'ANDCURRENTDATEANDLENGTH(D.DESC<40)
相反,以上SQL的结果看上去像是一个内连接(innerjoin),因为图1中所使用的语法迫使局部过滤在第二阶段才应用——换句话说,是在连接之后才应用。这意味着很多将要参与连接操作的行其实在后面的阶段就要被删除。为了强制性地在连接(第一阶段)之前应用局部过滤,可以使用表表达式,如图2中所示。
图2.在连接之前强制执行过滤,以获得正确结果
SELECTCOALESCE(O.ORDER_NO,'OrderNumbernotAvailable'),COALESCE(D.DESC,'DescriptionnotAvailable')FROM(SELECTO.ORDERFROMORDEROWHEREO.ORDER_DATEBETWEEN'1999-01-01'ANDCURRENTDATE)ASOFULLJOIN(SELECTD.DESC,D.ORDER_NOFROMDESCRIPTIONSDWHERELENGTH(D.DESC<40)ASDONO.ORDER_NO=D.ORDER_NO
'O'表表达式显式地预过滤外部表,'D'表表达式则显式地预过滤内部表。该语法将给出指定日期范围内的所有订单,不管这些订单是否具有少于40个字符的描述,以及给出所有少于40个字符的描述,不管是否有与之对应的订单。连接前条件(beforejoincondition)可以确保只有最后的结果行才会在连接操作中被处理。
预过滤左连接和右连接的替换null的表
对于左连接和右连接,如果替换null的表放在WHERE子句中过滤,那么就是一个真正的内连接,而这可能并不是您在编写连接时所想要的。如果您真的想要一个左连接或者右连接,那么有两种选择:
将替换null的表的局部过滤放在ON子句中。
这样将把过滤放入到一个连接中条件(during-joincondition)。然而,这并不会消除最后的结果行。对于替换null的表的连接中条件,它们只对“nullout”行(保留下来的但是以null填充的行)有作用,而最后的结果行,包括来自受保护表的值,是由连接前条件预先决定的。请参阅TerryPurcell的前一期专家会谈专栏,以查看有关连接中条件的例子。
将替换null的表的局部过滤放到一个表表达式中。这将只对替换null的表中的“nullout”行有作用。图3中的查询演示了表表达式的使用,很可能,这正是开发人员所需的:
图3.使用表表达式来强制在连接之前应用局部过滤
SELECTO.ORDER_NO,COALESCE(D.DESC,'ShortDescriptionnotAvailable')FROMORDEROLEFTJOIN(SELECTD.DESC,D.ORDER_NOFROMDESCRIPTIONSDWHERELENGTH(D.DESC)<40)ASDONO.ORDER_NO=D.ORDER_NOWHEREO.ORDER_DATEBETWEEN'1999-01-01'ANDCURRENTDATE
图3中的查询返回指定日期范围内的所有订单,不管这些订单是否具有少于40个字符的描述。
分离GROUPBY操作
如果您是在DB2平台、而不是0S/390和z/0S平台上开发,那么可以略过这一节,因为要么优化器会自动重写GROUPBY语句来分离GROUPBY操作并消除排序,要么您可以构造自动总结表(automaticsummarytable,AST),这种表可以消除排序。对于所有的大型主机(mainframe),您需要考虑重写自己的查询。
一般的报告往往必须提供详细信息,以及总结信息。GROUPBY在DB2中是非常严格的子句:
GROUPBY的第一条规则是,必须首先SELECT要分组的行。第二条规则是必须用内置的列函数聚集(aggregate)所有其他的行。
这两条规则的组合通常迫使您去连接表,以便将详细信息和总结信息融合成一个结果行,如以下查询所示:
SELECTC.CUST_ID,MIN(C.CUST_NAME)ASCUST_NAME,MIN(C.CUST_PHONE)ASCUST_PHONE,SUM(S.SALES)ASTOTAL_SALESFROMCUSTOMERC,SALESSWHEREC.CUST_ID=S.CUST_IDANDS.SALES_DATEBETWEEN:date-loAND:date-hiGROUPBYC.CUST_ID
这通常意味着在处理GROUPBY操作之前,来自每个表的符合标准的详细行要预先进行连接。假设在这个例子中,有1000个来自CUSTOMER的行与200,000个来自SALES的行相连接,然后再处理GROUPBY操作。相反,您可以使用表表达式来迫使优化器一个表一个表地进行聚集操作。图4中所示的查询显式地使优化器提早了GROUPBY处理:
图4.将GROUPBY移入表表达式以减少要连接的行数
SELECTC.CUST_NAME,C.CUST_PHONE,S.TOTAL_SALESFROMCUSTOMERC,,(SELECTS.CUST_ID,SUM(S.SALES)ASTOTAL_SALESFROMSALESSWHERES.SALES_DATEBETWEEN:date-loAND:date-hiGROUPBYS.CUST_ID)ASSWHEREC.CUST_ID=S.CUST_ID
例子中表表达式'S'显式地分离了GROUPBY操作。这样往往只需在一两个表上进行聚集操作,详细行来自不同的表。该语法允许优化器搜索SALES表CUST_ID列上的索引支持,以执行GROUPBY。可能的话,排序将避免。不管是否执行了排序,这都大大减少了参与连接的行数。这里不再像上一个例子中那样将1000个来自CUSTOMER的行与200,000个来自SALES的行相连接,现在是将1000个来自CUSTOMER的行只与10,000个salessummary行相连接。GROUPBY处理将表表达式结果压缩到一些总结行。
从已有源生成数据
至此,表表达式最强大的一个用途可以引用一位客户的话来解释:“当我们需要DB2中没有提供的数据时,我们用表表达式造出这样的数据来。”图5中所示的查询包含一个表表达式,它计算出对应于在该表表达式中生成的分组的销售额。
图5.在表表达式中生成子类(subcategory)数据
SELECTC.SUBCATEGORY,SUM(S.SALES)ASS.TOTAL_SALESFROMSALESS,(SELECTC.CUST_ID,SUBSTR(C.COLX,:hvstart,:hvlngth)ASSUBCATEGORYFROMCUSTOMERCWHEREC.CUST_REGIONBETWEEN:reg-loAND:reg-high)ASCWHEREC.CUST_ID=S.CUST_IDGROUPBYC.SUBCATEGORY
该查询对于周期性的报告很有效,即使需要排序,它也可以胜任。当需要弄清楚在列值中是否存在隐含意义时,该查询也同样很棒。然而,如果终端用户要频繁地请求一个公共起始位置和长度,即SUBSTR(C.COLX,2,4),那么应该将这一部分添加到表中,作为表自己的列。这将允许为这个新列提供一个索引,以避免排序。
图6是使用表表达式为季度报表生成正确季度的例子。
图6.为季度报表生成子类数据
SELECTC.SUBCATEGORY,SUM(S.SALES)ASS.TOTAL_SALESFROMSALESS,(SELECTC.CUST_ID,(CASEWHENMONTH(SALE_DT)BETWEEN2AND4THEN'Q1'WHENMONTH(SALE_DT)BETWEEN5AND7THEN'Q2'WHENMONTH(SALE_DT)BETWEEN8AND10THEN'Q3'ELSE'Q4'END)ASSUBCATEGORYFROMCUSTOMERCWHEREC.CUST_REGIONBETWEEN:reg-loAND:reg-high)ASCWHEREC.CUST_ID=S.CUST_IDGROUPBYC.SUBCATEGORY
只要在表表达式内使用了AS标识符命名所生成的数据,就可以在表表达式外的任何地方引用生成的数据。这意味着您也可以按照那个标识符排序,可以将其放入计算中,还可以将它放在表达式可以出现的任何地方。我们可以扩展图6中的例子,使其通过使用主机变量(hostvariable)处理更多的选项,如图7所示。
图7.使用主机变量以获得扩展的灵活性
SELECTC.SUBCATEGORY,SUM(S.SALES)ASS.TOTAL_SALESFROMSALESS,(SELECTC.CUST_ID,(CASEWHENMONTH(SALE_DT)BETWEEN:hv1aAND:hv1bTHEN:hv1WHENMONTH(SALE_DT)BETWEEN:hv2aAND:hv2bTHEN:hv2WHENMONTH(SALE_DT)BETWEEN:hv3aAND:hv3bTHEN:hv3WHENMONTH(SALE_DT)BETWEEN:hv4aAND:hv4bTHEN:hv4WHENMONTH(SALE_DT)BETWEEN:hv5aAND:hv5bTHEN:hv5WHENMONTH(SALE_DT)BETWEEN:hv6aAND:hv6bTHEN:hv6WHENMONTH(SALE_DT)BETWEEN:hv7aAND:hv7bTHEN:hv7WHENMONTH(SALE_DT)BETWEEN:hv8aAND:hv8bTHEN:hv8WHENMONTH(SALE_DT)BETWEEN:hv9aAND:hv9bTHEN:hv9WHENMONTH(SALE_DT)BETWEEN:hv10aAND:hv10bTHEN:hv10WHENMONTH(SALE_DT)BETWEEN:hv11aAND:hv11bTHEN:hv11ELSE:hv12END)ASSUBCATEGORYFROMCUSTOMERCWHEREC.CUST_REGIONBETWEEN:reg-loAND:reg-high)ASCWHEREC.CUST_ID=S.CUST_IDGROUPBYC.SUBCATEGORY
现在该查询可以处理对于按月分组的无止境的请求——如今您想要GROUPBY什么呢?前4个月作为一段时期,接下来的两个月归为另一时期,第6个月加上第12个月一起作为第三段时期,剩下的月份作为第四段时期,这样如何?在构建应用程序时一定要记住这一点。模拟相同的选项需要用到几百个视图。这就是为什么在我的大多数客户机上,视图都从生产应用程序中消声匿迹了,而只允许出现在终端用户环境中。
为获得更大的威力和灵活性,可添加一个用户定义函数,如图8所示。
图8.使用UDF来允许访问非关系数据
SELECTC.SUBCATEGORY,SUM(S.SALES)ASS.TOTAL_SALESFROMSALESS,(SELECTC.CUST_ID,UDFUNC2(:hvparm1,:hvparm2)ASSUBCATEGORYFROMCUSTOMERCWHEREC.CUST_REGIONBETWEEN:reg-loAND:reg-high)ASCWHEREC.CUST_ID=S.CUST_IDGROUPBYC.SUBCATEGORY
现在,该查询可以访问大型主机平台上任何可用源中的数据。例如,UDFUNC2可以访问存储在分布式环境中的平面文件(flatfile)中的数据,在表表达式'C'中引用它,将其称为SUBCATEGORY,还可以对其使用GROUPBY。现在您真正想要对什么使用GROUPBY呢?每当您需要获得或创建DB2环境本地没有的数据,但是又需要在关系操作(join、union、subquery、ORDERBY、GROUPBY,等等)中包括这种数据并且该数据只需用于查询的一次执行的时候,就应该想起使用表表达式。对于多次执行或者迭代,我建议使用全局临时表(这就是以后某天要讲的话题了)。
使用表表达式来帮助调优查询
查询调优是一个很大的主题,所以这里我只讨论表表达式在调优中发挥的作用。首先,将视图从SYSIBM.SYSVIEWS中移出并放入到FROM子句中,这样有助于查询调优器更加清楚该查询将要达到的目标。如果您往往凌晨时还在工作,那么这一点就很重要了。移除视图的间接好处是,使用主机变量具有更大的可扩展性。
为了演示使用表表达式进行前瞻性调优,我将向您展示一个查询,并一一介绍一些性能问题,之后再给出问题的解决方案。第一个查询是以下5个表的连接:
SELECTColumns.FROMTABX,TABY,TABZ,TAB1,TAB2WHEREjoinconditions
上面查询中的问题是表连接顺序。优化器分析每个表中参与连接的行数,估计不同顺序和连接方法所需的代价,并保持以TABX、TABY和TABZ的组合开始。期望的连接顺序从TAB1和TAB2开始。对于静态查询,影响连接顺序的方法有很多。但是,动态查询就没那么幸运了。还好有表表达式这个救星!表表达式通过使用DISTINCT关键字,迫使优化器预先连接表(也要求进行一次排序),从而发挥了其作用。如下所示:
SELECTColumnsFROMTABX,TABY,TABZ,(SELECTDISTINCTCOL1,COL2.FROMTAB1,TAB2WHEREjoinconditions)ASPREJOINWHEREremainingjoinconditions
现在该查询:
连接TAB1和TAB2。排序以除去可能存在的重复行。处理其他表。
这种技术惟一的缺点就是排序以及创建和扫描一个逻辑工作文件会带来额外的开销。但是大多数情况下,这一缺点可以用优化的连接顺序来弥补。
下面的场景更可能出现在非OS/390平台上,但是这一解决方案对于所有平台都是适用的。在下面的例子中,优化器选择物化(materialize)整个表表达式T2,然后使用合并扫描连接技术与T1相连接:
SELECTColumnsfromeithersetofdataFROMtable1T1,(SELECTT2.STUDENT_YEAR,SUM(C8)ASSUM8,MAX(C9)ASMAX9FROMtable2T2GROUPBYT2.STUDENT_YEAR)AST2WHERET1.MAJOR_ID=T2.MAJOR_ID
该解决方案要求在表表达式之前插入关键字TABLE。这使得表表达式可以与外部表相互关联。这种相互关联会影响优化器倾向于嵌套循环连接,并大大减少物化。在下面的例子中,通过将连接条件移入到表表达式里面来,可以使表表达式与外部表相互关联:
SELECTColumnsfromeithersetofdataFROMtable1T1,TABLE(SELECTT2.STUDENT_YEAR,SUM(C8)ASSUM8,MAX(C9)ASMAX9FROMtable2T2WHERET1.MAJOR_ID=T2.MAJOR_IDGROUPBYT2.STUDENT_YEAR)AST2
该查询现在一次处理一个T1.MAJOR_ID。只有符合那个MAJOR_ID的那些行才会在逻辑工作文件中物化。优化器认为工作文件越小,就越会选择嵌套循环连接。如果是在OS/390或z/OSTM平台下的DB2上尝试这一解决方案,就要小心了,因为该解决方案并不总能提高性能。请检查EXPLAIN输出,使用评测工具,或者运行基准测试来验证这一调优行动是否成功。