自动产生数据库查询的系统和方法

文档序号:6469972阅读:813来源:国知局

专利名称::自动产生数据库查询的系统和方法
技术领域
:本说明书涉及数据库,更确切地说,涉及自动产生数据库查询的系统和方法。
背景技术
:结构化查询语言(SQL)是一种用于数据库交互操作的ANSI标准语言。SQL允许用户从数据库中许多不同的记录集(表格)组合信息,以便从数据库检索数据。不过,SQL的能力也有某些缺点。例如,用户能够在一个SQL查询中组合不同的表格,它可能产生数据库引擎不可能解决的或者耗用大量系统资源的查询。数据库系统往往包括性能调节的系统或方法。例如,Oracle7提供了多种脚本(如UTILBSTAT和UTLESTAT),能够用于通过在一个或多个报告中总结数据库的操作状态,协助性能调节。这些脚本是一组SQL脚本,对于捕捉全系统的数据库性能的统计瞬态情况和产生有助于操作员优化数据库性能的报告十分有用。数据库操作员能够使用这些报告对数据库的性能进行精细调节以及对数据库进行预防性的维护。报告可能包括例如有关数据库存储器目标的信息,包括库缓存、词典缓存、闭锁使用、文件I/O和回退统计。现有系统的一个问题是,它们往往限制了产生最优执行计划所花费的时间长短。此外,熟悉的用户可能对数据了解得比目标统计表现出的更多,如果确实有目标统计的话。现有系统往往不利用用户的知识。此外,尽管在恰当地提供和分析时统计可能有用,如果目标还没有被分析则可能根本没有统计结果,或者统计结果可能过时了。在具体的事例中,可能没有洞察数据分布的特定统计结果。相反,用户倒可能具有在调节数据库查询中有用的第一手知识,而这可能是无法通过其他途径得到的。例如,用户可能知道,将会在有更多的资源比如多CPU可用时执行查询,即使系统通常是满负荷的。在数据库查询调节过程期间,会证明这种信息是十分有用的。
发明内容一种调节数据库查询的方法,包括选择数据库查询;分析选定的数据库查询,以确定选定的数据库查询中若干部分之间的关系;从多个可用的最优化模式中选择一个最优化模式;根据确定的关系和选定的最优化模式,通过修改选定的数据库查询中至少一部分,调节选定的数据库查询;以及显示修改后的数据库查询。这种分析可以确定数据库查询之内的标记,标记是由分隔符分开的若干单词。多个可用的最优化模式可能包括基于成本的和基于规则的模式。基于成本的模式可能包括First_Rows(第一行)模式和All_Rows(所有行)模式。这种方法可能进一步包括确定一种与使用调节后的数据库查询相关联的成本。这种方法可能进一步包括比较与使用选定的数据库查询相关联的成本和与使用调节后的数据库查询相关联的成本。这种方法可能进一步包括分析选定的数据库查询,以确定该数据库查询是否包括至少一个子查询,其中的子句包含有NOTEXISTS、NOTIN和ALL中至少一个。这种方法可能进一步包括提示用户,根据该数据库查询是否包括包含有NOTEXISTS、NOTIN和ALL中至少一个的子句,选择在调节期间使用的优先设置。这种优先设置可能包括重写优先设置,以便使用户能够选择两种转换之一一种是NOTEXISTS运算符转换为NOTIN运算符,另一种是选定的数据库查询转换为外部的连接。这种优先设置可能包括重写优先设置,以便使用户能够选择将ALL运算符连接的子查询转换为一个连接或者外部连接。这种优先设置可能包括重写优先设置,以便使用户能够选择是否使用NOTEXISTS运算符和外部连接之一,转换NOTIN运算符连接的子查询。计算机存储介质包括用于调节数据库查询的、计算机可执行的代码,其中包括用于使用户选择数据库查询的、计算机可执行的代码;用于分析选定的数据库查询以确定选定之数据库查询中若干部分之间关系的、计算机可执行的代码;用于使用户从多个可用的最优化模式中选择一个最优化模式的、计算机可执行的代码;用于根据确定的关系和选定的最优化模式,通过修改选定的数据库查询中至少一部分,调节选定的数据库查询的、计算机可执行的代码;以及用于显示修改后的数据库查询的、计算机可执行的代码。用于调节数据库查询的、编程的计算机系统包括一台显示器——用于向用户显示至少一个数据库查询;一种用户输入——使用户从显示的数据库查询之中选择一个数据库查询,从多个可用的最优化模式中选择一个最优化模式;以及一个处理器——用于分析选定的数据库查询,以确定选定的数据库查询中若干部分之间的关系,并且用于根据确定的关系和选定的最优化模式,通过修改选定的数据库查询中至少一部分,调节选定的数据库查询;修改后的数据库查询,通过该显示器向用户显示。附图简要说明下列附图仅仅描述了说明性的实例,为了更完全地评价和理解下列介绍而提供。图1是一个框图,用于介绍依据一个实施例的一个数据库查询调节过程;图2显示了包括一个SQL语句的一个对话显示;图3显示了一个选择最优化模式对话显示;图4显示了一个优先设置窗口显示;图5显示了一个检验SQL对话显示;图6显示了一个结果窗口显示;图7显示了一个编辑或者说调节窗口显示;图8显示了一个详细结果窗口显示;图9是一个计算机系统的框图,本系统和方法可以应用在其中;以及图10至图13是显示多种SQL语句中表格层次的示意图。具体实施例方式在介绍附图中展示的本公开文件的优选实施例时,为清晰起见采用了特定的技术。不过,本公开文件并非试图限于由此选择的特定技术,应当理解,每个特定的要素包括了在类似方式下运行的所有技术上的等效物。本公开文件涉及为了提高性能而自动调节数据库查询的系统和方法。例如,本系统和方法的形式可能是数据库管理系统(DBMS),它能够调节结构化查询语言(SQL)的语句,以产生最优的执行方案。在一方面,本公开文件涉及的系统和方法用于在数据库结构的上下文中分析数据库查询,并且修改和/或重构该查询,因此它能够在DBMS结构的上下文之内执行。此外,本公开文件的系统和方法能够使用数据库关系结构来使该查询使用的DBMS资源最小化,并确保该查询能够完成。实现这些结果的一种方法是将SQL语句剖分成定制的数据结构,它容许SQL语句的修改和重构。举例来说,如在某个FROM子句中的每个表格都可以检验以保证引用的完整性,并且可以对照如某个用户的WHERE条件,就可以为了正确的外部连接说明而检验这个WHERE条件。本系统分析SQL语句并保持追踪每个标记表格是如何连接的,以及SQL语句若干部分之间的关系。标记可能是SQL语句之内由分隔符分开的任何单词。表示表格列名称的标记可以用于确认SQL语句内部存在的连接和其它关系。然后,系统和方法可以改变例如连接的次序,并且可以移动子查询以调节SQL语句,达到最高性能。系统和方法的实现形式可以是在计算机系统——比如主机、个人计算机(PC)、手持计算机等等——中运行的软件应用程序。计算机系统可以连接到数据库。这种连接可以是例如通过直接连接——比如直接的硬布线或者无线连接,通过网络连接——比如局域网,或者通过因特网。图9中显示了能够实现本系统和方法之计算机系统的一个实例。该计算机系统一般地成为系统200,可能包括中央处理单元(CPU)202、存储器204、打印机接口206、显示单元208、LAN(局域网)数据传输控制器210、LAN接口212、网络控制器214、内部总线216以及一台或多台输入设备218,比如键盘、鼠标等等。如图所示,系统200可以通过连接装置222连接到数据库。图1描述了一个为提高性能而调节SQL语句的过程,依据本公开文件的一个实施例。用户首先选择他们需要调节的一个SQL语句(步骤S2)。例如,从显示单元208上显示的SQL编辑窗口,通过将光标放置在SQL语句的起点,按下鼠标的左键,拖动到该SQL语句的末端,然后释放鼠标键,用户能够选中他们需要调节的一个SQL语句。这就选中了该SQL语句。然后用户能够通过例如单击某个适当的按钮,从一个工具菜单(未显示)选择一个SQL调节器选件。然后,选中的要调节的SQL语句显示在SQL调节器窗口1中(步骤S4),如图2所示。然后用户就能够考察显示的SQL语句2,以核实它就是他们需要调节的SQL语句。然后用户可以选择单击后退按钮3、下一步按钮4、完成按钮5或者取消按钮6。后退按钮3返回前一个窗口,使用户选择一个不同的SQL语句。取消按钮6退出SQL调节器而不对显示的SQL语句2进行任何改变。下一步按钮4移动到下一个窗口(在图3中显示),使用户选择最优化模式设置,下面还要介绍。完成按钮5使用当前的最优化模式设置运行SQL调节过程。本系统能够采用数种最优化模式和使用数种不同的重写优先设置来运行。用户单击下一步按钮4之后,本系统分析该SQL语句(步骤S6)并显示选择最优化模式窗口11,如同3所示。检验该SQL语句以确定例如在该语句中存在着什么运算符、函数等等。系统也检验该SQL语句之内的任何子查询是否连接了NOTEXISTS、NOTIN和ALL子句。本系统也检验表格统计。例如,可能要检验对表格执行ANALYZE命令时由某个系统比如Oracle产生的表格统计。表格统计可能包括行数(NUM_ROWS)、最高峰之下的数据块数(如已经格式化过的、接收数据的数据块的数目,无论这些数据块当前包含着数据还是空着)(BLOCKS)、分配给表格的、还没有使用的数据块的数目(EMPTY_BLOCKS)、以字节计算的、每个数据块中的平均可用空闲空间(AVG_SPACE)、链接行的数目(CHAIN_COUNT)以及以字节计算的平均行长度,包括行的头部(AVG_ROW_LEN)。如果存在足够的信息(如对于有关的表格存在统计数据),系统就自动选择一个最优化模式。选定的最优化模式由圆按钮7-9指示。例如,可能会自动选择基于成本的最优化(FIRSTROW或ALLROW)模式,因为有关的表格存在统计数据时它将会产生很好的结果。在步骤S8中,如果在表格统计中没有足够的信息使系统自动选择一种最优化模式,或者如果用户需要改变系统自动设置的最优化模式,用户通过单击选择最优化模式窗口11中的圆按钮7-9之一,可以手工地选择最优化模式,如图3所示。用户从窗口11中通过单击优先设置按钮10,也可以修改重写优先设置设置,下面还要更详细地介绍。一种基于成本的最优化模式成为ALL_ROW模式。如果在SQL语句中至少一个引用的表格存在统计数据,系统就会默认地自动选择ALL_ROW模式。ALL_ROW模式为批量处理提供了快速的吞吐率。然后,用户通过单击圆按钮8,可以选择FIRSTROW模式,除非该SQL语句包含着集合运算符(如UNION、INTERSECT、MINUS、UNIONALL)、GROUPBY子句、FORUPDATE子句、聚合函数或者DISTINCT运算符。如果该SQL语句包含着集合运算符(如UNION、INTERSECT、MINUS、UNIONALL)、GROUPBY子句、FORUPDATE子句、聚合函数或者DISTINCT运算符,用户就不能选择FIRSTROW模式。用户通过单击圆按钮7,也可以手工选择ALL_ROW模式。当不能以其它方式确定对话所使用的最优化模式时,系统也可能自动选择FIRSTROW模式。如果对行处理需要快速响应时间,可以手工选择FIRSTROW模式。另一种模式称为基于规则的模式。如果对于任何引用的表格都没有统计数据,系统就会地自动选择基于规则的模式。通过单击圆按钮9,可以手工选择基于规则的模式。如果在分析处理(步骤S6)期间确定SQL语句包括连接了NOTEXISTS、NOTIN或ALL子句的子查询,就使窗口11(图3)中的优先设置按钮10活化。单击优先设置按钮10是用户指定重写优先设置,用于重写连接了NOTIN运算符、NOTEXISTS运算符和ALL运算符的子查询。例如,能够指定NOTEXISTS运算符的重写优先设置,使用户能够选择将NOTEXISTS运算符转换为NOTIN运算符和/或将该语句转换为外部连接。能够指定ALL运算符的重写优先设置,使用户选择将该语句转换为连接或外部连接。能够指定NOTIN运算符的重写优先设置,使用户能够选择将NOTIN运算符转换为NOTEXISTS运算符将该语句转换为外部连接。通过选择某些选件,对转换到外层的界面列规定为NOTNULL的事例进行限制,用户可以进一步规定重写优先设置。用户也可以指定本系统,如果需要,就对语句全都进行转换并增加ISNOTNULL准则。用户单击优先设置按钮10时,将显示优先设置窗口50,如图4所示。然后,通过单击调节器标签51,将显示SQL调节器优先设置页52。这个窗口中的选件,使得用户能够为连接了运算符ALL、NOTIN和NOTEXISTS的子查询设置重写优先设置,如前所述。在图4所示的实例中,要为NOTEXISTS运算符设置重写优先设置,用户就从下拉菜单53中选择NOTEXISTS运算符。这就显示了对于NOTEXISTS运算符可用的选件的列表。例如,然后通过对选件“通过NOTEXISTS运算符连接的子查询转换到NOTIN”(复选框55)和“通过NOTEXISTS运算符连接的子查询转换到外部连接”(复选框56)选择或不选择适当的复选框,用户可以指定是否使本系统能够使用NOTIN和/或外部连接来转换连接了NOTEXISTS运算符的子查询。如果选定了“通过NOTEXISTS运算符连接的子查询转换到NOTIN”(复选框55)选件,选件框57和58变为活化。选件框57使用户选择“对外部查询的连接列检验NOTNULL条件”选件,选件框58使用户选择“对子查询的连接列检验NOTNULL条件”选件。用户分别通过选择或不选择复选框59和60,可以在这些选件中选择一个或者两个都选。然后,对于每个选定的选件框,转换选件变为活化。例如,分别通过单击圆按钮61和63,可以对外部查询和子查询选择“仅当列规定为NOTNULL时转换”;分别通过单击圆按钮62和64,可以选择“如果需要,全都转换并增加“ISNOTNULL””。然后用户可以单击“OK”按钮54以保存指定的优先设置,并退出优先设置窗口。选择取消按钮66会退出窗口52,但不保存对优先设置所作的任何改变。选择帮助按钮65会向用户显示帮助菜单。以类似的方式,要为ALL运算符设置重写优先设置,用户就从下拉菜单53中选择“ALL”运算符以显示选件列表。然后,将会为用户提供某些选件,通过选择或不选择“通过ALL运算符连接的子查询转换到连接或外部连接”选件,指定是否使本系统能够把通过ALL运算符连接的子查询转换到连接或外部连接。如果用户选择了这个选件,那么将会为用户提供某些选件,“对外部查询的连接列检验NOTNULL条件”和“对子查询的连接列检验NOTNULL条件”。然后,用户可以选择这些选件中的一个,或者两个都选。然后,对于这些选定的选件中的每一个,会为用户提供两个转换选件,“仅当列规定为NOTNULL时转换”和“如果需要,全都转换并增加“ISNOTNULL””。然后,用户可以单击“OK”按钮54以保存指定的优先设置,并退出优先设置窗口。要为NOTIN运算符设置重写优先设置,用户就从下拉菜单53中选择NOTIN运算符以显示选件列表。然后用户通过选择或不选择选件“通过NOTIN运算符连接的子查询转换到NOTEXISTS”和“通过NOTIN运算符连接的子查询转换到外部连接”,可以指定是否使本系统能够使用NOTEXISTS运算符和/或外部连接来转换由NOTIN运算符连接的子查询。对于每个选定的选件,然后为用户提供一个选件,以便对外部查询的连接列检验NOTNULL条件和对子查询的连接列检验NOTNULL条件。用户可以选择这些选件中的一个,或者两个都选。然后,对于每个选定的选件,转换选件变为活化。换句话说,给用户选择权,决定是仅当列规定为NOTNULL时转换,还是如果需要,全都转换并增加“ISNOTNULL”。因此,用户可以为外部查询和/或子查询的连接列选择一个转换选件。用户单击“OK”按钮54以保存指定的优先设置,并退出优先设置窗口。退出优先设置窗口之后,显示返回选择最优化模式窗口11(图3)。步骤S8完成之后,用户对最优化模式和优先设置选择满意,通过单击下一步按钮8,就能继续处理。在步骤S10中,本系统为了引用的完整性、外部连接和/或NULL逻辑问题而检验SQL语句。如果发现错误或无效之处,就会显示改动该SQL语句以纠正这些问题的特定建议。例如,对于非法的外部连接——它可能导致非法的结果集,可能会显示建议。对于导致非法结果集的NULL逻辑问题、HAVING子句的不正确使用、笛卡尔乘积、连接准则中为了更好地使用索引的表达式、非连接准则中为了更好地使用索引的表达式以及没有索引的外关键字等等,也可能显示建议。然后在步骤S12中,向用户显示检验SQL窗口70,如图5所示。窗口部分73显示了该语句中每个子查询的图示。SQL语句中的每个表格显示为一个框,其头部包含着表格的名称和别名。对每个表格也显示主关键字、外关键字和独特外关键字。表格之间的一条绿线表示正确的连接。如果一条红线连接着表格,该连接包含着某个错误(比如违背了引用的完整性或者不适当的外部连接)。如果一条蓝线连接着表格,该连接由于缺乏引用的完整性而是无效的。对SQL语句建议的改正显示在窗口部分71中。用户可以按照需要调整检验SQL窗口70的尺寸,以观察任何窗口部分中包含的所有信息。单击“下一个SQL模块”按钮72和前一个SQL模块”按钮74,会选中蓝色SQL语句中的各个子查询,并且仅仅显示与选中子子查询相关联的图示。然后用户可以决定(步骤S14)要调节哪个SQL语句,原始语句或者修改后的语句。例如,单击“调节原始SQL”按钮76,会调节在检验SQL窗口70底部区域75中显示的原始SQL语句。单击“调节修改后SQL”按钮78,会执行检验SQL窗口70右侧区域71中显示的、建议的改正,然后调节改正后的SQL语句。在步骤S16中,通过执行一个或多个步骤使语句最优化,调节选定的SQL语句。例如,系统可以在连接准则中作出传递性的改变,可以确定所有的子查询变换,可以确定需要时非连接准则中的传递性,可以确定连接次序。系统可以产生几个变换后的SQL语句。然后系统可以确定哪个变换后的SQL语句具有最佳的性能,并以此对结果SQL语句排序。调节或变化一个SQL语句时,如果需要,本系统可以检验某个周围查询的若干列是否属于同一表格并作出传递性的改变。如果它们不属于同一表格,系统就检验是否有任何连接准则引用了连接子查询的一列,并确认等价列。本系统对连接子查询的每一列都这样做,然后在一次操作中使用传递性质来产生等价列的置换,以发现其中所有列都属于同一表格的一个组。本系统对通过NOTIN运算符连接的、非关联的子查询和与NOTIN、NOTEXISTS或ALL运算符连接的、关联的子查询,都可以作出传递性的改变。本系统通过评价每个子查询和为每个变换分配一个等级,也可以确定对SQL语法所进行的所有子查询变换。变换可以是强制的(全部或绝不),也可以是任选的。然后,这种信息用于产生多种不同的SQL版本。例如,SQL语句包含四个子查询时,本系统可能不把任何一个子查询分配为“全都变换”等级,把一个分配为“绝不变换”等级,而把三个分配为“任选”等级,以产生可能的替代SQL语句。换句话说,变换的类型决定了该变换是否应当全都进行、绝不进行或者可选地进行。例如,某些变换可能是需要的,因为进行变换之后优化器将使用更好的方案。在这种情况下,变换应当全都进行。所以,该变换会分配给最高的等级。某些变换可能是不需要的,由于进行变换之后优化器将使用效率更低的方案。在这种情况下,该变换不应当进行。所以,该变换会分配给最低的等级。某些变换被选用后可能会也可能不会产生更好的最优化方案,由于它可能还取决于其它因素。在这种情况下,该变换可以是可选地进行。所以,该变换会分配给中间的等级。进行了子查询变换之后,本系统确认非连接准则中的等价列——它们能够相互替换——并进行替换。这样做使本系统能够重写SQL语句,让数据库(比如ORACLE)利用连接的关键字索引。例如,在下列SQL语句中,对DEPENDENTS子查询的e.emp_seq准则能够连接到a.emp_seq或t.emp_seq。SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDe.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=‘SPOUSE’)如下(第5行)所示,把e.emp_seq转换到a.emp_seq使数据库能够使用连接到ASSIGNMENTS的关键字索引SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDa.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=‘SPOUSE’)在等价列中存在着等价连接准则时,不同的列都能够替换另一列。不过,本系统也可以使用同连接准则相与的非连接准则的传递性。如果非连接准则是同连接准则相或,就不应当使用它产生的传递性。如果某个多列运算对象具有传递性,本系统对该运算对象增加若干列,并将相应的列复制到选择列表。所有的多列传递性都用于每个变换,原始语句除外。如果运算对象通过某个外部连接的准则等价于另一列,不能使用等价列。如果某个FROM子句具有嵌套选择语句——它不具有GROUPBY语法或聚集——并且主查询具有引用主查询和嵌套选择之间连接列之一的非连接准则,本系统就在该SQL语句的所有拷贝中,复制嵌套选择语句中的非连接准则,包括原始语句。本系统能够使用一种ORDERED线索来确定连接次序。这种ORDERED线索迫使优化器依照表格在SQL语句中的FROM子句中出现的次序连接表格。这就使得写入的SQL语句以特定的次序连接FROM子句中引用的表格。系统首先不使用ORDERED线索来测试SQL语句。然后,本系统使用ORDERED线索来产生另外的置换,具有不同的驱动表格,以强制实现某种特定的连接次序——驱动表格是例如嵌套的循环连接运算中的某个父表格。驱动表格中的每一行都能够用于在连接的表格中发现匹配的行以完成连接运算。如果变换后SQL语句的默认连接比原始的SQL语句具有更好的成本/行值,那么最可能的情况是,ORDERED线索将产生一个比利用默认连接次序的初始变换效率更高的方案但是成本也更高的行。本系统能够显示ORDERED变换,作为一种可行的最佳解决方案。为了从产生的不同SQL语句中的每一个中确定最佳性能,本系统对原始的SQL语句和重写过程产生的每个SQL语句产生一种成本统计数据。在产生了与该SQL语句相关联的方案后,确定成本。例如,Oracle的EXPLAINPLAN语句,可以用于产生优化器执行该SQL语句所用的方案。该EXPLAINPLAN语句能够对该SQL语句运行以获得执行方案,其中描述了优化器执行该SQL语句时计划要使用的步骤。Oracle也会为该方案中的每一步骤提供一个数字(如成本)。该成本是执行方案中该步骤的一个相对数字。对于该方案中的所有步骤,可以对成本求和以获得该方案的总成本。本系统能够将数据库算出的成本除以行数,对每个SQL语句产生成本/行值。根据该方案需要的逻辑读的数目,也可以确定成本。确定了成本之后,本系统以上升的次序(从最低到最高成本)对SQL语句进行排序。在步骤18中,本系统在结果对话窗口90中,显示原始的SQL语句和成本最低的SQL语句,如图6所示。原始的SQL语句显示在窗口部分92中,建议的修改SQL语句显示在窗口部分94中。然后,用户可以比较建议的修改SQL语句和原始的SQL语句,观察变化,然后选择他们是否想要以调节后的SQL语句替换原始的SQL语句。然后单击替换SQL按纽100,就会以窗口94所示的、建议的修改SQL语句替换原始的SQL语句。系统在编辑或者说调节窗口104中,增加注释并显示调节后的SQL语句,如图7所示。如图7所示,注释可能包括日期标志、表明语句已经调节和标识所用最优化模式(在这个实例中是ALL_ROWS)的信息。单击后退按钮96返回前一个窗口。单击更多细节按钮98打开详细结果窗口106,如图8所示。调节后的SQL语句显示在窗口部分108中。行110显示了原始的SQL语句的成本统计项目,一个或多个行112显示了调节改变后的SQL语句的成本统计数据。统计项目可能包括占用时间114——执行该SQL需要的时间量(以秒记)、CPU时间116——执行该SQL需要的每秒的CPU周期数目、逻辑读取118——执行该SQL需要的逻辑读取的数目、实际读取120——执行该SQL需要的实际读取的数目、行数122——SQL返回的行数以及成本/行数124——数据库的成本除以行数。详细结果窗口106使用户能够观察每个语句的成本统计数据,以帮助用户选择最适合其需要的SQL。在包括编辑器功能的系统中,用户也可以选择打开调节后的语句,以进行进一步的编辑。如果在SQL中有某个结合变量,单击“结合”按纽126就会显示一个对话框,在执行该SQL语句之前,用户能够在其中为该变量指定一个数值。单击“执行”按纽128会产生选定语句的统计数据。单击“全部执行”按纽130会产生列出的所有SQL语句的统计数据。用户可以单击“打印”按纽132来打印当前在窗口中显示的信息。用户可以单击“保存”按纽134把这个窗口中的信息保存为文本文件,以便将来引用。用户可以在表格中选择另外的SQL条目,单击“PAFO”(为Oracle的方案分析器)按纽138在方案窗口中显示代码。单击“替换SQL”按纽140,以调节后的SQL替换原始的SQL。单击“后退”按纽136返回结果窗口90(图6)。单击“取消”按纽144退出本系统而不改变原始的SQL。以下说明列出并介绍了多种术语——本说明书自始至终使用这些术语来引用SQL语句的部分或属性——以及本系统为调节SQL语句所用的某些概念。本说明书自始至终使用这些术语来介绍本系统调节SQL语句所用的算法。A.分支和嵌套当某个查询具有子查询时,从顶端(主查询)到底部的路径为一“分支”。“嵌套的级别”是分支中子查询的数目。主查询被视为处于“级别”=1。直接在它下面的子查询处于“级别”=2,依此类推。考虑以下的SQL,为了易于引用,已经加上了行号。1.SELECT*FROMemployees2.WHEREemp_seqIN3.(SELECTemp_seq4.FROMtime_sheetst5.WHEREt.rpt_date=‘01-MAY-98’6.ANDt.proj_seq=7.(SELECTproj_seqFROMprojects8.WHEREname=‘EXPLAINSQLDEVELOPMENT’))9.ANDhiredate>SYSDATE-10分支从主查询LEVEL=1(从第1行开始)延伸到LEVEL=2(从第3行开始),再到LEVEL=3最后的子查询(从第7行开始)。在这个实例中,“嵌套的级别”为2,因为主查询具有一个子查询,这个子查询又有一个子查询。以下SQL语句包含两个分支1.SELECT*FROMemployees2.WHEREemp_seqIN3.(SELECTemp_seq4.FROMtimesheetst5.WHEREt.rpt_date=‘01-MAY-98’6.ANDt.proj_seq=7.(SELECTproj_seqFROMprojects8.WHEREname=‘EXPLAINSQLDEVELOPMENT’))9.ANDhiredate>SYSDATE-1010.ANDemp_seqIN11.(SELECTemp_seqFROMdependents12.WHEREbirthdate>‘01-JAN-67’)这个SQL语句的第一个分支与前一个实例相同。不过,这个SQL语句还具有第二个分支。主查询(从第1行开始)为第1级别。第2级别从11行开始。这个第二分支的嵌套级别等于1。B.最顶端的父级布尔运算符当WHERE子句中存在OR运算符时,在合并或者将某个子查询移入FROM子句之前,应当谨慎。如果OR是父级布尔运算符,就不应当进行合并或移动。以下的事例说明了这一点。对于SQL语句SELECTe.emp_seqFROMemployeeseWHEREhiredate>SYSDATE-155ORemp_seqIN(SELECTemp_seqFROMassignmentsWHEREproj_seq=1)如果我们不考虑OR运算符,子查询可以合并如下SELECTe.emp_seq,x.emp_seqFROMemployeese,(SELECTemp_seqFROMassignmentsWHEREproj_seq=1)xWHEREe.hiredate>SYSDATE-155ORe.emp_seq=x.emp_seq不过,在子查询中没有连接的行时,这项连接现在将变为笛卡尔乘积。例如,当准则“e.emp_seq=x.emp_seq”求值为FALSE,但是非连接准则求值为TRUE时,无论如何来自子查询的行都将被连接。对于这个特定的EMPLOYEES行,无论连接来自子查询的哪一行,非连接准则将永远求值为TRUE。这就产生了笛卡尔乘积。以下实例说明了当准则前面有NOT时,这个问题可能会变得更加困难。例如,给定SQL语句SELECTe.emp_seqFROMemployeeseWHERENOT(hiredate>SYSDATE-155ORemp_seqIN(SELECTemp_seqFROMassignmentsWHEREproj_seq=1))使用DeMorgan规则,NOT反转括号内的布尔和关系运算符。不过,本系统可以使用使用DeMorgan规则消除NOT运算符,得出以下SQL。反转的布尔和关系运算符有下划线。进行反转首先可能简化了该过程。SELECTe.emp_seqFROMemployeeseWHEREhiredate<=SYSDATE-155ANDemp_seqNOTIN(SELECTemp_seqFROMassignmentsWHEREproj_seq=1)C.关联到多个SQL模块关联的子查询能够被关联到多于一个SQL模块,如以下实例所示1.SELECT*FROMemployeese2.WHEREEXISTS3.(SELECTnullFROMassignmentsa4.WHEREa.emp_seq=e.emp_seq5.ANDEXISTS6.(SELECTnullFROMtime_sheetst7.WHEREt.emp_seq=e.emp_seq8.ANDt.proj_seq=a.proj_seq))对TIME_SHEETS(第6行)的子查询(通过第7行)既关联到外层的(对ASSIGNMENTS的)查询,又(通过第8行)关联到对EMPLOYEES的主查询。D.关联准则在上面的SQL语句中,第7行和第8行的准则称为“关联准则”。这些准则不是“连接准则”,但是在子查询执行时引用查询外部(比如“e.emp_seq”或“a.proj_seq”)像是常数的意义上,更像“非连接准则”。在例程中可以检验关联准则,以确保它们是用等价运算符。不过应当注意,以这种方式检验的关联准则应当是对应于包括某个独特索引的若干列。例如,以下查询在子查询中具有两个关联准则。不过,有影响的只有一个,即对于PROJ_SEQ的一个,由于它是具有独特索引的列。SELECT*FROMassignmentsaWHEREEXISTS(SELECTstart_dateFROMprojectspWHEREa.proj_seq=p.proj_seqANDa.start_date<p.start_date)E.非连接准则在SQL语句中,下面的代码引用某个非连接准则时,它要寻找的准则为关系运算符是而且另一个运算对象或者是一个常数或者是一个子查询。对于子查询的这种假设是,由于用户指定了作为关系运算符,子查询必须返回最多一个单行,这意味着一个常数。以下是非法WHERE子句的实例WHEREcol1=10+col2WHEREcol1>10以下是合法WHERE子句的实例WHEREcol1=lOWHEREcol1=(selectco12fromtabl)WHEREcol1=:Bind_VariableF.连接准则下面引用连接准则。在每种情况下,关系运算符都应当是并且准则的每一侧都应当是单列的名称,而不应当是表达式。G.子查询接口典型情况下,外层查询和子查询之间的接口包含至少一个“关系运算符”比如“IN”、“=”、“NOTEXISTS”等等。除了EXISTS和NOTEXISTS关系运算符以外,所有关系运算符之前都是一列或多列。运算符之前的列称为“接口列”。在子查询的对应SELECT列表中的列也被称为“接口列”。根据它们是外层查询的接口列还是子查询的接口列,能够区分这两种类型的“接口列”。这些列也可能不属于子查询之内的对象,并且不与属于子查询内表格的任何列关联,如以下代码所示1.SELECT*FROMemployeese2.WHEREemp_seqIN3.(SELECTemp_seqFROMassignmentsa4.WHERE(e.hiredate,a.proj_seq)IN5.(SELECTrpt_date,proj_seqFROMtime_sheetst))列“e.hiredate”(第4行)不属于子查询(ASSIGNMENTS表格),并且不与ASSIGNMENTS内的任何列关联。本文档将称外层查询的这些列为“外来的”。如果某列是外来的,系统将试图把它合并或者将它转换到一个非关联的子查询。不过,系统将不试图移动该子查询。H.优先设置变换SQL语句时,空逻辑会引起许多问题。如果在外层查询中特定的列可为空,变换时可能出现不同的结果集。可以对变换进行修改以确保不发生这样的情况,尽管从性能上说可能会付出某个小的代价。修改就是同某个准则进行与运算,指定该列不可为空(如列ISNOTNULL)。由于对满足原始准则的每一行,该准则必然成立,所以代价不会大。相反,在某些情况下性能还可能改善,因为这时优化器比如ORACLE的优化器就可能执行一个反连接。在下面的算法中将会引用这些优先设置。I.别名子查询合并或移动到FROM子句时,外层查询中的准则应当通过某个别名被完全识别,例如,参见以下的实例。1.SELECT*FROMemployees2.WHEREemp_seqIN3.(SELECTemp_seq4.FROMtime_sheetst,projectsp5.WHEREt.rpt_date=‘01-MAY-98’6.ANDt.proj_seq=p.proj_seq7.ANDp.stop_dateISNULL)8.ANDhiredate>SYSDATE-10在这个实例中,应当为EMPLOYEES增加一个别名,而且该别名应当在属于该表格的所有列之前。此外,子查询也应当给予一个别名。为EMPLOYEES增加一个“e”别名,那么变换会是SELECTe.*FROMemployeese,(SELECTemp_seqFROMtime_sheetst,projectspWHEEREt.rpt_date=‘01-MAY-98’ANDt.proj_seq=p.proj_seqANDp.stop_dateISNULL)xWHEREe.emp_seq=x.emp_seqANDe.hiredate>SYSDATE-10如果某个子查询包含聚集,在它被移进FROM子句之前,应当给它一个别名。例如,以下的SQL语句在子查询(第3行)中包含“max”聚集函数。1.SELECT*FROMsal_history2.WHERE(emp_seq,effective_date)IN3.(SELECTemp_seq,max(effective_date)4.FROMsal_history5.GROUPBYemp_seq)因此,该SQL语句应当进行变换,给予聚集“MAX(EFFECTIVE_DATE)”一个别名如下SELECTs.*FROMsal_historys,(SELECTemp_seq,max(effective_date)clFROMsal_historyGROUPBYemp_seq)xWHEREs.emp_seq=x.emp_seqANDs.effective_date=x.c1这就产生了一个问题,是否允许用户通过隐含的知识,为已知要返回独特集合的子查询设置旗标。例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTnullFROMprojectsp,assignmentsaWHEREp.proj_seq=a.proj_seqANDa.emp_seq=e.emp_seqANDp.name=‘EXPLAIN_SQL’)能够转换为SELECT*FROMemployeeseWHEREe.emp_seqIN(SELECTa.emp_seqFROMprojectsp,assignmentsaWHEREp.proj_seq=a.proj_seqANDp.name=‘EXPLAIN_SQL’)或者转换为SELECTe.*FROMemployeese,projectsp,assignmentsaWHEREe.emp_seq=a.emp_seqANDp.proj_seq=a.proj_seqANDp.name=‘EXPLAIN_SQL’这两种情况都是可能的,因为子查询中低级别的表格在PROJ_SEQ和EMP_SEQ上都有PK。传递性允许我们说,因为NAME——它也有一个UNQ索引——上的等价,在PROJ_SEQ上有等价。所以,保证了子查询每一EMPLOYEES行产生一行,这表明合并是可能的。既然已经解释了本系统所用的某些术语和概念,以下的实例就有助于介绍本系统变换SQL语句可能使用的算法。显示的实例分为关联的和非关联的子查询,并以此识别。非关联的子查询是不与主查询关联的子查询。也就是,在主查询考察任何行之前,就可以逻辑地执行非关联的子查询。换句话说,子查询与主查询独立,并且可以作为有其本身答案的一个查询而存在。与之相反,关联的子查询依赖于被主查询考察的行。1.以下SQL语句混合了关联的和非关联的子查询。在以下的SQL语句中,最后的子查询是一个标量子查询,并且由于外层子查询对PROJ_SEQ和RPT_DATE都有等价准则,而且EMP_SEQ列在选择列表上,该子查询能够合并,尽管对SAL_HISTORY的子查询不能。对于FROM子句中的嵌套查询是否正确地识别为标量,这是一个很好的试验。SELECT*FROMemployeesWHEREemp_seqIN(SELECTemp_seqFROMtime_sheetstWHEREproj_seq=(SELECTproj_seqFROMprojectsWHEREname=‘EXPLAINSQLDEVELOPMENT’)ANDrpt_date=(SELECTMAX(effective_date)FROMsal_historysWHEREs.emp_seq=t.emp_seq))应当变换为SELECTe.*FROMemployeese,time_sheetst,projectsp,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historysGROUPBYemp_seq)xWHEREe.emp_seq=t.emp_seqANDt.proj_seq=p.proj_seqANDp.name=‘EXPLAINSQLDEVELOPMENT’ANDt.emp_seq=x.emp_seqANDt.rpt_date=x.col12.以下实例是非关联的,并且与前一个实例略有不同。基本的差异是对SAL_HISTORY的子查询。在这种情况下该子查询初始确定为可能返回多行。这表明在原始查询中可能发生运行错误。这就防止了合并和移动该子查询。也就是,对SAL_HISTORY上的子查询应当保持原样。不过,由于对RPT_DATE的准则具有等价运算符,该子查询与TIME_SHEETS的合并仍然能够发生,并且仍然可能遇到同样的运行错误。换句话说,将不移动对SAL_HISTORY的子查询,因为不能保证产生标量集。不过,在能产生标量集的情况下,对准则“rpt_date=___”将产生一个单一的常数值。本系统将该子查询视为等价于一个常数,允许外层查询与其围绕的查询合并并携带着嵌套的选择。例如,以下SQL语句,SELECT*FROMemployeesWHEREemp_seqIN(SELECTemp_seqFROMtime_sheetstWHEREproj_seq=(SELECTproj_seqFROMprojectsWHEREname=‘EXPLAINSQLDEVELOPMENT’)ANDrpt_date=(SELECTeffective_dateFROMsal_historysWHEREsal>100)应当变换为SELECTe.*FROMemployeese,time_sheetst,projectspWHEREe.emp_seq=t.emp_seqANDt.proj_seq=p.proj_seqANDp.name=‘EXPLAINSQLDEVELOPMENT’ANDt.rpt_date=(SELECTeffective_dateFROMsal_historysWHEREsal>100)3.以下实例是关联的,并且涉及ALL运算符。如果某个子查询不返回任何行,该准则可能取值为TRUE。这表明该子查询仅仅应当移动,并且作为外部连接而进行,其中仅有外部连接的语法仅仅应用于初始关联准则。进行比较的原始列必须不考虑连接匹配。在外部连接发生之后,对增加的最后准则进行求值。例如,以下SQL语句,SELECTemp_seq,birthdateFROMemployeeseWHEREbirthdate<ALL(SELECTbirthdateFROMdependentsdWHEREe.emp_seq=d.emp_seq)能够变换为SELECTe.emp_seq,e.birthdateFROMemployeese,(SELECTd.emp_seq,MIN(birthdate)col1,1col2FROMdependentsdGROUPBYemp_seq)xWHEREe.emp_seq=x.emp_seq(+)AND(e.birthdate<x.col1ORx.col2ISNULL)系统应当检验确定任何新的外部连接的准则是否与任何运算对象进行或运算,因为这会产生分析错误。可能需要使数据库分析中间结果以判断是否应当进行过变换。如果不应当进行过变换,那么对该子查询就不应当进行任何处理。4.以下实例是非关联的,并且涉及具有GROUPBY语法的子查询。即使子查询具有GROUPBY子句,SELECT列表也不包含GROUPBY列表中的每一列。这表明复制的可能性不存在,除非DISTINCT关键字增加到该子查询的SELECT列表。例如,以下SQL语句,SELECT*FROMemployeesWHEREemp_seqIN(SELECTemp_seqFROMassignmentsGROUPBYemp_seq,proj_seq)能够变换为SELECTe.*FROMemployeese,(SELECTDISTINCTa.emp_seqFROMassignmentsaGROUPBYa.emp_seq,a.proj_seq)xWHEREe.emp_seq=x.emp_seq5.SQL语句可能仅仅包含聚集,而不包含GROUPBY子句。所以,它不需要DISTINCT关键字。以下实例是非关联的。例如,以下SQL语句,SELECT*FROMemployeesWHEREhiredate=(SELECTMIN(effective_date)FROMsal_history)能够变换为SELECTe.*FROMemployeese,(SELECTMIN(s.effective_date)col1FROMsal_historys)xWHEREe.hiredate=x.col16.以下实例是非关联的,并且说明了移动到外层查询时如何处理NOTIN运算符。基本要点在于,系统应当转换到外部连接,然后寻找“外部连接的行”(如子查询的选择列表上的不可空的列ISNULL(见下面的准则“x.col1ISNULL”))。一个解救方案是向选择列表增加一个常数,比如1,因为无论个子查询是否具有GROUPBY语法,它都会起作用。例如,以下SQL语句,SELECTemp_seqFROMemployeesWHEREhiredateNOTIN(SELECTeffective_dateFROMsal_history)能够变换为SELECTe.emp_seqFROMemployeese,(SELECTDISTINCTeffective_date,1col1FROMsal_history)xWHEREe.hiredate=x.effective_date(+)ANDx.col1ISNULL7.以下实例类似于上面的实例6,并且也是非关联的。不过,在这个实例中有多列连接到该子查询。此外,这些列表示外层查询中的多个表格。在这个实例中,可能会遇到一个问题,因为变换不做分析。可能会产生一个错误信息“ORA-01417某个表格可以外部连接到最多一个其它表格”。所以,关系运算符需要一个外部连接而连接到该子查询的列是来自多个表格时,不应当产生这条信息。例如,以下SQL语句,SELECTe.emp_seqFROMemployeese,dependentsdWHERE(e.hiredate,d.birthdate)NOTIN(SELECThiredate,d1.birthdateFROMemployeese1,dependentsd1WHEREe1.emp_seq=1001ANDe1.emp_seq=d1.emp_seqANDrelation=‘SPOUSE’)ANDe.emp_seq=d.emp_seqANDd.relation=‘SPOUSE’会不正确地变换为SELECTe.emp_seqFROMemployeese,dependentsd,(SELECTDISTINCTe1.hiredate,d1.birthdate,1col1FROMemployeese1,dependentsd1WHEREe1.emp_seq=1001ANDe1.emp_seq=d1.emp_seqANDd1.relation=‘SPOUSE’)xWHEREe.emp_seq=d.emp_seqANDd.relation=‘SPOUSE’ANDe.hiredate=x.hiredate(+)ANDd.birthdate=x.birthdate(+)ANDx.col1ISNULL8.以下的非关联实例提供了一种虽然奇怪却值得关注的情况。尽管这种情况在理论上很罕见,但是它能够发生。其原因在于,下面的子查询是非关联的但是却连接到带有EXISTS运算符的外层查询。一般说来,EXISTS运算符仅仅用于关联的子查询。在任何情况下,该变换都是简单的只须把它移动到不带有连接子句的外层查询,并在子查询中增加准则“ROWNUM=1”,如下所示。例如,以下SQL语句,SELECT*FROMemployeesWHEREEXISTS(SELECT*FROMsal_historyWHEREsal<0)能够变换为SELECTe.*FROMemployeese,(SELECT*FROMsal_historyWHEREsal<0ANDROWNUM=1)与原始的SQL语句相比,变换后的SQL语句的性能将得到极大的改善。因为现在子查询仅仅执行一次,而不是像在原始SQL语句中执行那样每行一次。9.以下实例是非关联的,因为子查询包含多于一个同名的列名称而列出。尽管列名称前面有表格别名,列名称还是应当给予一个别名。在外层查询的SELECT列表中的星号,也应当为了外层查询中的每个表格而复制。注意以下所示变换中下划线的项目。SQL语句,SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqAND(e.birthdate,d.birthdate)IN(SELECTe1.birthdate,d1.birthdateFROMemployeese1,dependentsd1WHEREe1.emp_seq=d1.emp_seqANDe1.hiredate=SYSDATE-100)能够变换为SELECTe.*.d.*FROMemployeese,dependentsd,(SELECTDISTINCTe1.birthdatecol1,d1.birthdatecol2FROMemployeese1,dependentsd1WHEREe1.eep_seq=d1.emp_seqANDe1.hiredate=SYSDATE-100)xWHEREe.emp_seq=d.emp_seqANDe.birthdate=x.col1ANDd.birthdate=x.col210.以下实例是非关联的,并且说明了标量子查询和NOTIN运算符。由于子查询是标量的,它就能够与外层查询合并为一个外部连接。关键是仅仅保留“外部连接的”行。通过把子查询中带有表格非空列的准则同ISNULL运算符进行与运算,就能够做到这一点。一个更好的解决方案可能是使用行标识(见下面第二个变换)。例如,SELECTemp_seqFROMemployeesWHEREhiredateNOTIN(SELECThiredateFROMemployeesWHEREemp_seq=999)能够变换为SELECTe.emp_seqFROMemployeese,employeese1WHEREe.hiredate=e1.hiredate(+)ANDe1.emp_seq(+)=999ANDe1.emp_seqISNULL或者变换为SELECTe.emp_seqFROMemployeese,employeese1WHEREe.hiredate=e1.hiredate(+)ANDe1.emp_seq(+)=999ANDe1.rowidISNULL11.以下的关联实例既包含关联准则,又包含非连接准则。由于该子查询保证唯一性,所以该子查询能够合并。例如,以下SQL语句,SELECT*FROMemployeeseWHEREEXISTS(SELECT*FROMprojectsp,assignmentsaWHEREp.proj_seq=a.proj_seqANDe.emp_seq=a.emp_seqANDp.name=‘EXPLAINSQLDEVELOPMENT’)能够变换为SELECTe.*FROMemployeese,projectsp,assignmentsaWHEREp.proj_seq=a.proj_seqANDe.emp_seq=a.emp_seqANDp.name=‘EXPLAINSQLDEVELOPMENT’12.以下的关联实例不同,因为SELECT列表包含不返回外层查询的列。(事实上,如果在前面一个实例11中的子查询具有选择列表中的项目,变换也将会恰恰如此。注意,在前面一个实例11中,子查询选择列表上的星号不起作用。)例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTrelationFROMdependentsdWHEREe.emp_seq=d.emp_seq)能够变换为SELECTe.*FROMemployeese,(SELECTDISTINCTemp_seqFROMdependentsd)xWHEREe.emp_seq=x.emp_seq13.这个关联的实例与前一个实例不同,因为SELECT列表列具有外层查询中的对应列。例如,SELECT*FROMsal_historys1WHEREeffectivedate>(SELECTMAX(effective_date)FROMsal_historys2WHEREs1.emp_seq=s2.emp_seq)能够变换为SELECTs1.*FROMsal_historys1,(SELECTemp_seq,MAX(effectiw_date)col1FROMsal_historys2GROUPBYemp_seq)xWHEREs1.emp_seq=x.emp_seqANDs1.effective_date>x.col114.以下的非关联实例说明了通过NOTEXISTS运算符连接的非关联子查询。例如,SELECT*FROMemployeesWHERENOTEXISTS(SELECT*FROMdependents)能够变换为SELECTe.*FROMemployeese,(SELECTcount(*)col1FROMdependentsWHEREROWNUM=1)xWHEREx.col1=0注意,原始子查询的SELECT列表改变为“COUNT(*)”,并且子查询增加了“ROWNUM=1”准则。15.以下的非关联实例类似于前一个实例14,只是该子查询包含GROUPBY。变换也类似于前一个实例14。例如,SELECT*FROMemployeesWHERENOTEXISTS(SELECTnullFROMsal_historyGROUPBYemp_seq)能够变换为SELECTe.*FROMemployeese,(SELECTcount(*)col1FROMsal_historyWHEREROWNUM=1GROUPBYemp_seq)xWHEREx.col1=0在子查询的SELECT列表中的任何内容,都交换为“COUNT(*)”。16.以下是一个使用NOTEXISTS运算符的关联实例。例如,SELECTemp_seqFROMemployeeseWHERENOTEXISTS(SELECT*FROMsal_historysWHEREe.emp_seq=s.emp_seqANDeffective_date=SYSDATE)能够变换为SELECTe.emp_seqFROMemployeese,sal_historysWHEREe.emp_seq=s.emp_seq(+)ANDeffective_date(+)=SYSDATEANDs.emp_seqISNULL17.以下的关联实例说明了以IN运算符连接的集合子查询。例如,SELECT*FROMsal_historys1WHEREeffectivedateIN(SELECTeffective_dateFROMsal_historys2WHEREs1.emp_seq=s2.emp_seqANDsal>100)能够变换为SELECTs1.*FROMsal_historys1,sal_historys2WHEREs1.effective_date=s2.effective_dateANDs1.emp_seq=s2.emp_seqANDs2.sal>10018.以下的非关联实例说明了<ALL运算符。例如,SELECT*FROMemployeesWHEREhiredate<ALL(SELECThiredateFROMemployeesWHEREbirthdate>‘01-JAN-87’)能够变换为SELECTe.*FROMemployeese,(SELECTMIN(hiredate)col1FROMemployeesWHEREbirthdate>‘01-JAN-87’)xWHERE(hiredate<x.col1ORx.col1ISNULL)注意变换中“x.col1ISNULL”的使用。其要点在于,聚集函数总会返回一个值,即使没有一行符合准则。由于子查询结果为空集时ALL运算符永远取值TRUE,系统应当仍然返回外层查询中的行。19.以下的非关联实例类似于前一个实例18。不过在这个实例中使用>ALL运算符而不是<ALL运算符。例如,SELECT*FROMemployeesWHEREhiredate>ALL(SELECThiredateFROMemployeesWHEREbirthdate>‘01-JAN-77’)能够变换为SELECTe.*FROMemployeese,(SELECTMAX(hiredate)col1FROMemployeesWHEREbirthdate>‘01-JAN-77’)xWHERE(hiredate>x.col1ORx.col1ISNULL)20.以下的非关联实例包括了可合并的集子查询。例如,SELECT*FROMemployeesWHEREemp_seqNOTIN(SELECTemp_seqFROMsal_historyWHEREeffective_date=‘5-JUL-98’)能够变换为SELECTe.*FROMemployeese,sal_historysWHEREe.emp_seq=s.emp_seq(+)ANDeffective_date(+)=‘5-JUL-98’ANDs.emp_seqISNULL21.以下的关联实例类似于前一个实例20,只是它是关联的。这个实例说明了如何把外部连接语法增加到关联准则。例如,SELECT*FROMassignmentsaWHEREstart_dateNOTIN(SELECTeffective_dateFROMsal_historysWHEREa.emp_seq=s.emp_seq)能够变换为SELECTa.*FROMassignmentsa,sal_historysWHEREa.start_date=s.effective_date(+)ANDa.emp_seq=s.emp_seq(+)ANDs.emp_seqISNULL22.以下的非关联实例类似于前一个实例21。不过在这个实例中,子查询有多个表格。下面的实例说明了如何合并到外层查询时,如何把外部连接语法放置到子查询之内的连接准则上。例如,SELECT*FROMstatus_listWHEREstatussNOTIN(SELECTp.statusFROMprojectsp,assignmentsaWHEREa.proj_seq=p.proj_seqANDp.name=‘EXPLAINSQLDEVELOPMENT’)能够变换为SELECTs.*FROMstatus_lists,projectsp,assignmentsaWHEREs.status=p.status(+)ANDp.name(+)=‘EXPLAINSQLDEVELOPMENT’ANDa.proj_seq(+)=p.proj_seqANDa.ROWIDISNULL在子查询中,在刚刚连接的表格上保证了唯一性(在这种情况下是别名“p”,因为p.name是唯一的)。以连接的次序,最多一个最低的子级能够非唯一,在这种情况下是别名“a”。连接次序是别名“s”,然后是“p”,然后是“a”。23.以下的关联实例可能颇为复杂。由于NOTIN运算符,它应当是外部连接。不过,问题是别名“e”和“a”都与子查询关联。因为子查询只包含一个表格,子查询应当是可合并的,所以我们无须保证唯一性。例如,SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqANDe.emp_seqNOTIN(SELECTt.emp_seqFROMtime_sheetstWHEREa.proj_seq=t.proj_seqANDt.rpt_date=‘20-FEB-94’)由于关联准则,该子查询既不能合并,也不能移动。如果子查询的关联准则和接口列引用外层查询中的多于一个表格,该子查询能够跳过。在这种情况下,一个关联准则引用别名“a”,外层查询的接口引用“e.emp_seq”。若是系统要变换该子查询,变换就会以TIME_SHEETS被外部连接到多于一个表格而结束,而这是非法的。因此,以上SQL语句会不正确地合并到SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDe.emp_seq=t.emp_seq(+)ANDa.proj_seq=t.proj_seq(+)ANDt.rpt_date(+)=‘20-FEB-94’ANDt.emp_seqISNULL并被不正确地移动到SELECT*FROMemployeese,assignmentsa,(SELECTt.emp_seq,t.proj_seq,1col1FROMtimesheetstWHEREt.rpt_date=‘20-FEB-94’)xWHEREe.emp_seq=a.emp_seqANDe.emp_seq=x.emp_seq(+)ANDa.proj_seq=x.proj_seq(+)ANDx.col1ISNULL这种两种情况都会造成非法的外部连接,所以应当避免。24.这个关联的实例类似于上面的实例23。通过传递性,对于以下的原始语句,系统能够变换原始语句,如实例23所示,然后执行变换,因此消除了实例23中遇到的限制。例如,实例23中的原始语句能够变换为SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqANDa.emp_seqNOTIN(SELECTt.emp_seqFROMtime_sheetstWHEREa.proj_seq=t.proj_seqANDt.rpt_date=‘20-FEB-94’)它又可变换为SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seq(+)ANDa.proj_seq=t.proj_seq(+)ANDt.rpt_date(+)=‘20-FEB-94’ANDt.emp_seqISNULL25.以下的关联实例涉及标量运算符“=”。这个实例显示了如何使用子查询中的传递性来确定子查询是标量的;如“e.emp_seq=t.emp_seq=a.emp_seq”。例如,SELECT*FROMtime_sheetst,projectspWHEREt.proj_seq=p.proj_seqANDt.rpt_date=(SELECThiredateFROMemployeese,assignmentsaWHEREa.proj_seq=p.proj_seqANDe.emp_seq=a.emp_seqANDe.emp_seq=t.emp_seq)能够变换为SELECT*FROMtime_sheetst,projectsp,employeese,assignmentsaWHEREt.proj_seq=p.proj_seqANDt.rpt_date=e.hiredateANDa.proj_seq=p.proj_seqANDe.emp_seq=t.emp_seqANDe.emp_seq=a.emp_seq26.以下的关联实例涉及集合运算符“IN”。例如,SELECT*FROMemployeeseWHEREemp_seqIN(SELECTa.emp_seqFROMassignmentsa,time_sheetstWHEREa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDa.proj_seq=1ANDt.rpt_date=‘20-FEB-94’)可以变换为SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDa.proj_seq=1ANDt.rpt_date=‘20-FEB-94’27.以下的关联实例显示了虽然关联准则涉及子查询中多于一个表格,还是可能合并的情况。在这种情况下,“t.”通过接口把“e.”连接到子查询;然后“t.”通过PROJ_SEQ列与“a.”关联。能够应用传递性,所以“t”既通过PROJ_SEQ又通过EMP_SEQ与“a”关联。此外,连接时对“e”和“a”保证了唯一性。例如,SELECT*FROMtime_sheetstWHEREt.rpt_dateNOTIN(SELECThiredateFROMemployeese,assignmentsaWHEREa.proj_seq=t.proj_seqANDe.emp_seq=a.emp_seqANDe.emp_seq=t.emp_seq)能够变换为SELECTt.*FROMtime_sheetst,employeese,assignmentsaWHEREt.rpt_date=e.hiredate(+)ANDt.emp_seq=e.emp_seq(+)ANDt.proj_seq=a.proj_seq(+)ANDt.emp_seq=a.emp_seq(+)AND(a.emp_seqISNULLORe.emp_seqISNULL)由于“a”和“e”是分别连接“t”的,系统需要对每个表格的不可空列增加ISNULL,并把它们用或连在一起。28.以下的关联实例说明了带有GROUPBY子句的子查询中SELECT列表上的聚集。例如,SELECT*FROMsal_historysWHEREeffective_dateIN(SELECTMAX(start_date)FROMassignmentsaWHEREs.emp_seq=a.emp_seqGROUPBYproj_seq)能够变换为SELECT*FROMsal_historys,(SELECTDISTINCT.emp_seq,MAX(start_date)col1FROMassignmentsaGROUPBYproj_seq,emp_seq)xWHEREs.emp_seq=x.emp_seqANDeffective_date=x.col129.以下的非关联实例说明了子查询移动时把聚集变换为聚集的聚集。例如,SELECT*FROMassignmentsaWHEREstart_date<ANY(SELECTMIN(rpt_date)FROMtime_sheetsGROUPBYproj_seq)能够变换为SELECT*FROMassignmentsa,(SELECTMAX(MIN(rpt_date))col1FROMtime_sheetsGROUPBYproj_seq)xWHEREa.start_date<x.col130.以下的关联实例说明了,通过像是“<ANY”的运算符连接的子查询不能变换。例如,SELECT*FROMassignmentsaWHEREstart_date<ANY(SELECTMIN(rpt_date)FROMtime_sheetstWHEREa.emp_seq=t.emp_seqGROUPBYproj_seq)会不正确地变换为SELECT*FROMassignmentsa,(SELECTemp_seq,MIN(rpt_date)col1FROMtime_sheetstGROUPBYproj_seq,emp_seq)xWHEREa.start_date<x.col1ANDa.emp_seq=x.emp_seq这种变换是不正确的,因为子查询仍然是一个集合,而不是所需要的标量,如每EMP_SEQ,由于PROJ_SEQ的成组作用,仍然可能有许多MIN(rpt_date)值。就算该子查询不具有GROUPBY子句,该变换也仍然不是毫无问题的,因为该变换会需要把关联列放到SELECT列表上并进行聚集以及聚集的聚集。虽然原始的GROUPBY列既不与HAVING子句关联,又不是它的一部分,系统也不能摆脱它。例如,该查询可能会不正确地变换为SELECTe.*FROMemployeese,(SELECTemp_seq,MAX(rpt_date)col1FROMtime_sheetstGROUPBYt.emp_seq)xWHEREe.emp_seq=x.emp_seqANDe.hiredate<x.col1以上变换是不正确的,因为对于同样的PROJ_SEQ,可能会由某个rpt_date小于e.hiredate,而另一个rpt_date大于e.hiredate。在原始的查询中,带有MIN(rpt_date)的子查询将返回行中的第一个数据。不过,在以上的查询中,该子查询将返回最大值。31.以下的关联实例是一个极好的实例,因为关联准则使用“<”运算符而不是“=”运算符,其中的子查询不能移动。例如,SELECT*FROMsal_historys1,employeeseWHEREe.emp_seq=s1.emp_seqANDs1.effective_date=(SELECTMIN(s2.effective_date)FROMsal_historys2WHEREe.emp_seq=s2.emp_seqANDe.hiredate<s2.effective_date)会不正确地变换为SELECT*FROMsal_historys1,employeese,(SELECTs2.emp_seq,s2.effective_date,MIN(s2.effective_date)col1FROMsal_historys2GROUPBYs2.emp_seq,s2.effective_date)xWHEREe.emp_seq=x.emp_seqANDs1.effective_date=x.col1ANDe.hiredate<x.effective_date进行正确变换的一种方法可能是,将不等关联中涉及的表格移动到子查询中,如下所示SELECT*FROMsal_historys1,(SELECTe.emp_seq,MIN(s2.effective_date)col1FROMsal_historys2,employeeseWHEREe.emp_seq=s2.emp_seqANDe.hiredate<s2.effective_dateGROUPBYe.emp_seq)xWHEREs1.emp_seq=x.emp_seqANDs1.effective_date=x.col1这种解决方案是将EMPLOYEES表格移动到子查询中,然后移动子查询。32.以下的关联实例是一个不带聚集的标量子查询。例如,SELECT*FROMsal_historysWHEREeffective_date=(SELECThiredateFROMemployeeseWHEREs.emp_seq=e.emp_seq)能够变换为SELECTs.*FROMsal_historys,employeeseWHEREs.effective_date=e.hiredateANDs.emp_seq=e.emp_seq33.以下的关联实例类似于前一个实例32。不过,在这个实例中在并非唯一关键字一部分的列上有另外的关联准则。处理关联的子查询时这可能是重要的;也就是对子查询的PK或UNQ关键字的关联准则应当使用“=”运算符,而另外的关联准则使用的运算符则无关紧要。在这种情况下,关联准则“t.rpt_date>=a.start_date”不是唯一关键字的一部分。例如,SELECT*FROMtime_sheetstWHERErpt_date<(SELECTstop_dateFROMassignmentsaWHEREa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.rpt_date>=a.start_date)能够变换为SELECTt.*FROMtime_sheetst,assignmentsaWHEREt.rpt_date<a.stop_dateANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.rpt_date>=a.start_date34.以下的关联实例使用了标量运算符来连接标量子查询,其中由于聚集和没有GROUPBY子句,子查询是标量的。例如,SELECT*FROMemployeeseWHEREhiredate>(SELECTMIN(effective_date)FROMsal_historysWHEREe.emp_seq=s.emp_seq)能够变换为其中SELECTe.*FROMemployeese,(SELECTemp_seq,MIN(effective_date)col1FROMsal_historysGROUPBYemp_seq)xWHEREe.emp_seq=x.emp_seqANDe.hiredate>x.col135.以下的关联实例说明了HAVING子句中的关联。解决方案是,HAVING子句变为外层查询中的准则,在关联准则中比较的局部项目应当出现在子查询的SELECT列表中。应当注意,另一个要校验的语法将是子查询没有GROUPBY子句,但确实具有HAVING子句。例如,SELECT*FROMemployeeseWHERENOTEXISTS(SELECTa.proj_seqFROMassignmentsa,time_sheetstWHEREa.proj_seq=t.proj_seqANDa.emp_seq=t.emp_seqANDe.emp_seq=a.emp_seqGROUPBYa.proj_seqHAVINGMIN(t.rpt_date)<e.hiredate)能够转换为SELECT*FROMemployeese,(SELECTa.proj_seq,a.emp_seq,MIN(t.rpt_date)col1,1col2FROMassignmentsa,time_sheetstWHEREa.proj_seq=t.proj_seqANDa.emp_seq=t.emp_seqGROUPBYa.proj_seq,a.emp_seq)xWHEREe.emp_seq=x.emp_seq(+)ANDx.col1<e.hiredate(+)ANDx.col2ISNULL36.以下是一个关联的实例,其中运算符是NOTIN运算符,并且有不等关联准则。系统应当注意,它不必把EMP_SEQ列的另一个拷贝移动到GROUPBY子句上,因为它已经在那里了。例如,SELECT*FROMsal_historys1WHEREs1.salNOTIN(SELECTMIN(sal)FROMsal_historys2WHEREs1.emp_seq!=s2.emp_seqANDs1.effective_date=s2.effective_dateGROUPBYemp_seq)能够变换为SELECTs1.*FROMsal_historys1,(SELECTemp_seq,effective_date,MIN(sal)col1,1col2FROMsal_historys2GROUPBYemp_seq,effective_date)xWHEREs1.sal=x.col1(+)ANDs1.emp_seq!=x.emp_seq(+)ANDs1.effective_date=x.effective_date(+)ANDx.col2ISNULLorderbys1.emp_seq,s1.effective_date37.以下的关联实例类似于上面的实例30,只不过在实例30中子查询不具有聚集。虽然这个查询可能不实际,这个实例用于说明,带有GROUPBY的子查询为什么不应当对于任何的ANY、SOME或ALL运算符移动。例如,SELECT*FROMassignmentsaWHEREstart_date<ALL(SELECTrpt_dateFROMtime_sheetstWHEREa.emp_seq=t.emp_seqGROUPBYrpt_dateHAVINGCOUNT(*)>1)会不正确地变换为SELECTa.*FROMassignmentsa,(SELECTMIN(rpt_date)col1,emp_seq,1col2FROMtime_sheetstWHEREa.emp_seq=t.emp_seqGROUPBYrpt_date,emp_seqHAVINGCOUNT(*)>1)xWHERE(start_date<x.col1ORx.col2ISNULL)ANDa.emp_seq=x.emp_seq(+)问题在于,对于外层查询,已经有了RPT_DATE的组合,而且没有连接准则。因此,我们需要标量时,对EMP_SEQ我们就以每个连接一个集合而结束。38.以下的关联实例说明了由于不等关联准则,ANY、SOME或ALL子查询不能移动的情况。要移动,应当确保最多一行与原始外层查询进行比较。不过,对于该变换中的不等连接准则,可以连接多行。例如,SELECT*FROMsal_historys1WHEREsal<ANY(SELECTsalFROMsal_historys2WHEREs1.emp_seq!=s2.emp_seqANDs1.effective_date=s2.effective_date)会不正确地变换为SELECTs1.*FROMsal_historys1,(SELECTemp_seq,effective_date,MAX(sal)col1FROMsal_historys2)xWHEREs1.sal<x.col1ANDs1.emp_seq!=x.emp_seqANDs1.effective_date=x.effective_date39.以下是一个带有“>ALL”运算符的关联实例。这个实例类似于上面的实例3,只不过这个实例使用MAX函数而不是MIN函数。例如,SELECTemp_seq,birthdateFROMemployeeseWHEREbirthdate>ALL(SELECTbirthdateFROMdependentsdWHEREe.emp_seq=d.emp_seq)能够变换为SELECTe.emp_seq,e.birthdateFROMemployeese,(SELECTd.emp_seq,MAX(birthdate)col1,1col2FROMdependentsdGROUPBYemp_seq)xWHEREe.emp_seq=x.emp_seq(+)AND(e.birthdate>x.col1ORx.col2ISNULL)40.以下的关联实例说明了“<ANY”运算符。<ANY和“>ANY”运算符之间的唯一差异是MAX变为MIN。例如,SELECT*FROMsal_historys1WHEREemp_seq=1001ANDsal<ANY(SELECTsalFROMsal_historys2WHEREs1.emp_seq=s2.emp_seqANDs1.effective_date=s2.effective_date)能够变换为SELECTs1.*FROMsal_historys1,(SELECTemp_seq,effective_date,MAX(sal)col1FROMsal_historys2GROUPBYemp_seq,effective_date)xWHEREemp_seq=1001ANDs1.sal<x.col1ANDs1.emp_seq=x.emp_seqANDs1.effective_date=x.effective_date41.以下的关联实例说明了标量运算符“=”。例如,SELECT*FROMemployeeseWHEREhiredate=(SELECTMIN(effective_date)FROMsal_historysWHEREe.emp_seq=s.emp_seq)能够变换为SELECTe.*FROMemployeese,(SELECTemp_seq,MIN(effective_date)col1FROMsal_historysGROUPBYemp_seq)xWHEREhiredate=x.col1ANDe.emp_seq=x.emp_seq42.以下的关联实例说明了EXISTS运算符,其中子查询包含HAVING子句但是没有GROUPBY和聚集。这里的要点在于在HAVING子句之前增加GROUPBY子句。例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTnullFROMsal_historysWHEREe.emp_seq=s.emp_seqHAVINGCOUNT(*)>1)能够变换为SELECTe.*FROMemployeese,(SELECTemp_seqFROMsal_historysGROUPBYemp_seqHAVINGCOUNT(*)>1)xWHEREe.emp_seq=x.emp_seq43.这个关联的实例类似于前面的实例42,其中有HAVING子句。不过,在这个实例中也存在GROUP。例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTnullFROMsal_historysWHEREe.emp_seq=s.emp_seqGROUPBYeffective_dateHAVINGCOUNT(*)>1)能够变换为SELECTe.*FROMemployeese,(SELECTDISTINCTemp_seqFROMsal_historysGROUPBYeffective_date,emp_seqHAVINGCOUNT(*)>1)xWHEREe.emp_seq=x.emp_seq44.以下的非关联实例说明了由于EXISTS运算符子查询不能移动的情况。其理由是这种解决方法向子查询增加“ROWNUM=1”准则。不过,HAVING子句能够使检索到的唯一行失去资格。在这个实例中,应当至少有两行合格,否则子查询返回空集。例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTnullFROMsal_historysHAVINGCOUNT(*)>1)会不正确地变换为SELECT*FROMemployeese,(SELECTnullFROMsal_historysWHEREROWNUM=1HAVINGCOUNT(*)>1)45.下一个关联的实例说明了与上面多个级别关联的子查询。包括这个实例是因为在上一节中,“限制”表明不应当试图转换这些类型的关联子查询。其理由是它们相对比较复杂,尽管还是可能的,如下面所示。例如,SELECT*FROMemployeeseWHEREEXISTS(SELECTnullFROMassignmentsaWHEREa.emp_seq=e.emp_seqANDEXISTS(SELECTnullFROMtime_sheetstWHEREtemp_seq=e.emp_seqANDt.proj_seq=a.proj_seq))能够首先变换为SELECTe.*FROMemployeeseWHEREEXISTS(SELECTnullFROMassignmentsa,(SELECTDISTINCTemp_seq,proj_seqFROMtime_sheetst)x1WHEREa.emp_seq=e.emp_seqANDx1.emp_seq=e.emp_seqANDx1.proj_seq=a.proj_seq)再变换为SELECTe.*FROMemployeese,(SELECTDISTINCTa.emp_seqcol1,x1.emp_seqcol2FROMassignmentsa,(SELECTDISTINCTemp_seq,proj_seqFROMtime_sheetst)x1WHEREx1.proj_seq=a.proj_seq)x2WHEREx2.col1=e.emp_seqANDx2.col2=e.emp_seq第一个变换相对比较简单。子查询也许不能合并,所以就移动它。这需要SELECT列表中的DISTINCT关键字,加上曾经是要移动到SELECT列表之关联准则一部分的局部列。此外,关联的准则会移动到外层查询。第二个变换稍微复杂一点,因为现在有来自子查询不同的局部表格的两列,它们曾经具有相同的名称,但是曾经与外层查询关联。这表明它们移动到SELECT列表时必须给这些列独特的别名。然后系统只需把关联的准则移动到外层查询。为了更好地优化这一点,系统在移动之前应当注意准则的传递性。例如,我们有“a.emp_seq=e.emp_seq”,也有“x1.emp_seq=e.emp_seq”。这等价于说“a.emp_seq=x1.emp_seq”。理解了这一点,就会使子查询中的连接效率更高,并会得出SELECTe.*FROMemployeese,(SELECTDISTINCTa.emp_seqcol1,x1.emp_seqcol2FROMassignmentsa,(SELECTDISTINCTemp_seq,proj_seqFROMtime_sheetst)x1WHEREx1.proj_seq=a.proj_seqANDa.emp_seq=x1.emp_seq)x2WHEREx2.col1=e.emp_seqANDx2.col2=e.emp_seq46.OR运算符被视为子查询的父级。以下的实例说明了转换为一个连接将产生错误的答案。例如,SELECTcount(*)FROMemployeesWHEREhiredate>‘01-jan-97’ORemp_seqIN(SELECTemp_seqFROMsal_historyWHEREsal>100)如果不考虑OR,转换将是SELECTcount(*)FROMemployeese,(SELECTDISTINCTemp_seqFROMsal_historyWHEREsal>100)xWHEREhiredate>‘01-jan-97’ORe.emp_seq=x.emp_seq看到这个问题的一种方法是现在把OR转换为一个UNIONALL,如下所示SELECTcount(*)FROMemployeese,(SELECTDISTINCTemp_seqFROMsal_historyWHEREsal>100)xWHEREhiredate>‘01-jan-97’UNIONALLSELECTcount(*)FROMemployeese,(SELECTDISTINCTemp_seqFROMsal_historyWHEREsal>100)xWHEREhiredate>‘01-jan-97’47.以下的实例涉及OR运算中的关联准则。如果关联准则在OR运算中,就不可能转换为外部连接,并且转换为NOTIN也很复杂。例如,SELECT*FROMemployeeseWHERENOTEXISTS(SELECT*FROMsal_historysWHEREs.effective_date=e.birthdateORs.effective_date=e.hiredate)如果转换为NOT,系统应当做以下工作SELECT*FROMemployeeseWHEREbirthdateNOTIN(SELECTeffective_dateFROMsal_historys)ORhiredateNOTIN(SELECTeffective_dateFROMsal_historys)如果因为会产生不可分析的语法而不可能转换为外部连接SELECTe.*FROMemployeese,(SELECTDISTINCTeffective_dateFROMsal_history)xWHEREe.birthdate=x.effective_date(+)ORe.hiredate=x.effective_date(+)48.以下的实例说明了原始子查询可能产生空集时NOTIN到NOTEXISTS的变换。为了展示的目的,人为地强制子查询产生一个空集。例如,SELECT*FROMemployeesWHEREhiredateNOTIN(SELECTeffective_dateFROMsal_historyWHERE1=2)应当变换如下以确保产生同样的结果集SELECT*FROMemployeeseWHERENOTEXISTS(SELECT*FROMsal_historysWHERE1=2ANDe.hiredate=s.effective_date)AND(e.hiredateISNOTNULLORNOTEXISTS(SELECT*FROMsal_historyWHERE1=2)不过,这种解决方法的一个问题是我们又回到了原始状态。49.以下的关联实例说明了带有与外层查询关联的聚集的HAVING子句。例如,SELECT*FROMprojectspWHEREEXISTS(SELECTnullFROMtime_sheetstWHEREt.proj_seq=p.proj_seqGROUPBYemp_seqHAVINGMIN(t.rpt_date)=p.start_date)这种变换可能需要移动聚集和关联列到SELECT列表,如下所示SELECT*FROMprojectsp,(SELECTproj_seq,MIN(rpt_date)col1FROMtime_sheetsGROUPBYemp_seq,proj_seq)xWHEREp.proj_seq=x.proj_seqANDp.start_date=x.col150.以下的关联实例展示了与NOTIN的合并。注意,变换中增加的准则包含了连接到外层查询的子查询表格的ROWID。使用别名“t”而不是“a”的ROWID这一点很重要,因为“t”是连接次序中最低级别的子级,“e”外部连接到“a”,然后“a”外部连接到“t”。例如,SELECTemp_seqFROMemployeeseWHEREe.hiredateNOTIN(SELECTstart_dateFROMassignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.rpt_date=‘01-jan-87’ANDa.proj_seq=1)能够变换为SELECTe.emp_seqFROMemployeese,assignmentsa,time_sheetstWHEREe.hiredate=a.start_date(+)ANDe.emp_seq=a.emp_seq(+)ANDa.emp_seq=t.emp_seq(+)ANDa.proj_seq=t.proj_seq(+)ANDt.rpt_date(+)=‘01-jan-87’ANDa.proj_seq(+)=1ANDt.ROWIDisnull51.以下的关联实例类似于前一个实例50,只不过子查询的接口列与子查询中关联表格不相同。例如START_DATE属于ASSIGNMENTS,但是T.EMP_SEQ关联却属于一个不同的表格。如果使用传递性,因为T和A之间的连接准则,我们可以把T.EMP_SEQ转换到A.EMP_SEQ。例如,SELECTemp_seqFROMemployeeseWHEREe.hiredateNOTIN(SELECTstart_dateFROMassignmentsa,time_sheetstWHEREe.emp_seq=t.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.rpt_date=‘01-jan-87’ANDa.proj_seq=1)首先增加传递性以产生SELECTemp_seqFROMemployeeseWHEREe.hiredateNOTIN(SELECTstart_dateFROMassignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.rpt_date=‘01-jan-87’ANDa.proj_seq=1)然后,我们就有了与前一个实例50相同的查询,这说明发生了同样的变换。52.这个关联的实例类似于实例50,只不过现在子查询中对应于接口列的表格是TIME_SHEETS。唯一的差异在于,EMPLOYEES外部连接到TIME_SHEETS,然后TIME_SHEETS外部连接到ASSIGNMENTS。所以,增加的准则使用ASSIGNMENTS表格的ROWID,而不是像在实例50中的TIME_SHEETS表格。例如,SELECTemp_seqFROMemployeeseWHEREe.hiredateNOTIN(SELECTrpt_dateFROMassignmentsa,time_sheetstWHEREe.emp_seq=t.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDt.proj_seq=1)能够变换为SELECTe.emp_seqFROMemployeese,assignmentsa,time_sheetstWHEREe.hiredate=t.rpt_date(+)ANDe.emp_seq=t.emp_seq(+)ANDt.emp_seq=a.emp_seq(+)ANDt.proj_seq=a.proj_seq(+)ANDt.proj_seq(+)=1ANDa.ROWIDisnull53.以下的关联实例说明了一个聚集的聚集不是一种可能的转换。例如,SELECT*FROMprojectspWHEREstop_dateIN(SELECTMAX(MIN(rpt_date))FROMtime_sheetstWHEREt.proj_seq=p.proj_seqGROUPBYemp_seqHAVINGMIN(t.rpt_date)=p.start_date)会不得不错误地转换为SELECT*FROMprojectsp,(SELECTproj_seq,MIN(rpt_date)col1,MAX(MIN(rpt_date))col2FROMtime_sheetstGROUPBYemp_seq,proj_seq)xWHEREp.proj_seq=x.proj_seqANDp.start_date=x.col1ANDp.stop_date=x.col2不过,这样的SQL将不会编译,所以应当避免。54.以下的关联实例说明了一个不等关联准则,以及聚集不是一种可能的转换。例如,SELECT*FROMassignmentsaWHEREa.emp_seqIN(SELECTemp_seqFROMtime_sheetstWHEREt.proj_seq=a.proj_seqANDa.start_date>t.rpt_dateGROUPBYemp_seqHAVINGMAX(rpt_date)<a.stop_date)会错误地变换为SELECT*FROMassignmentsa,(SELECTemp_seq,proj_seq,rpt_date,MAX(rpt_date)col1FROMtime_sheetstWHEREt.proj_seq=a.proj_seqANDa.start_date>t.rpt_dateGROUPBYemp_seq,proj_seq,rpt_date,)xWHEREa.emp_seq=x.emp_seqANDa.proj_seq=x.proj_seqANDa.start_date>x.rpt_dateANDa.stop_date>x.col1这里的问题在于,由于不等准则“a.start_date>t.rpt_date”,T.RPT_DATE将不得不移动到SELECT列表以及GROUPBY。不过,聚集也不能移动到SELECT列表,因为它将包含错误的组。事实上,现在MAX将等于RPT_DATE。55.以下的关联实例也说明了一个聚集的聚集不是一种可能的转换。例如,SELECT*FROMassignmentsaWHEREa.stop_dateIN(SELECTMAX(MIN(rpt_date))FROMtime_sheetstWHEREt.proj_seq=a.proj_seqGROUPBYemp_seq)会错误地变换为SELECT*FROMassignmentsa,(SELECTproj_seq,MAX(MIN(rpt_date))col1FROMtime_sheetstGROUPBYemp_seq,proj_seq)xWHEREa.stop_date=x.col1ANDa.proj_seq=x.proj_seq问题在于,你不能在SELECT列表中具有标量和聚集。最优化以下进一步列出系统或用户选定了最优化模式之后,本系统通过产生另外的SQL语句,用来优化SQL语句的方法。最优化目标的线索应当包括在每个SQL语句变换中。系统将确定非连接准则中的所有传递性,确定所有连接次序以及确定所有的不同子查询变换。非连接传递性简要地看一看以下的查询,SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqANDe.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=′SPOUSE′)在这个实例中,DEPENDENTS子查询的准则可以连接到“a.emp”或“t.emp_seq”,正如同原始的e.emp_seq”一样。其它系统不会理解这一点。通过转换到“a.empseq”,本系统允许数据库使用ASSIGNMENTS上的连接键索引。另一种变换会是把“e.emp_seq”改变为“a.emp_seq”(第5行),如下所示1SELECT*FROMemployeese,assignmentsa,time_sheetst2WHEREe.emp_seq=a.emp_seq3ANDa.emp_seq=t.emp_seq4ANDa.proj_seq=t.proj_seq5ANDa.emp_seqIN(SELECTemp_seqFROMdependents6WHERErelation=′SPOUSE′)由于运算对象“e.emp_seq”能够被两个其它值取代,该SQL能够以三种方式写出(一种是原始方式,另外两种是能够取代“e.emp_seq”的不同值的数)。多种非连接传递性下面介绍系统如何处理多种非连接传递性。例如,给定SQL语句,1SELECT*FROMemployeese,assignmentsa,time_sheetst2WHEREe.emp_seq=a.emp_seq3ANDa.emp_seq=t.emp_seq4ANDa.proj_seq=t.proj_seq5ANDa.proj_seq=(SELECTproj_seqFROMprojects6WHEREname=′EXPLAINSQLDEVELOPMENT′)7ANDe.emp_seqIN(SELECTemp_seqFROMdependents8WHERErelation=′SPOUSE′)在两个不同的非连接准则中,“a.proj_seq”(第5行)和“e.emp_seq”(第7行)两个运算对象能够被等价的列取代。其它系统不会理解这一点。不过,本系统通过把“e.emp_seq”(第7行)转换到“a.emp_seq”,将允许数据库使用ASSIGNMENTS上的连接键索引。对于“e.emp_seq”的准则与上一个实例相同。本系统也能够以“t.proj_seq”取代“a.proj_seq”(第5行)。这表明可能有6(2×3)种不同的SQL语句能够使用(注意这包括原始值)。多列运算对象如何处理包括多列的运算对象?例如1SELECT*FROMemployeese,sal_historys2WHEREe.emp_seq=s.emp_seq3AND(s.emp_seq,s.effective_date)IN4(SELECTemp_seq,max(effective_date)5FROMsal_historys16WHEREeffective_date<=SYSDATE7GROUPBYemp_seq)准则“s.emp_seq,seffective_date”(第3行)表示能够应用传递性的非连接准则。由于连接子句说明“e.emp_seq”和“s.emp_seq”是等价的,对于非连接准则运算对象就有两种选择。第一种是原始的,第二种是由“e.emp_seq”取代“s.emp_seq”。问题在于这到底是否应当做。这个问题的理由在于运算对象中的两列都引用同一个表格,并且在两列中都有连接索引。即使在两列中只有一个单列索引。答案是不要担心,只要把其它列加到运算对象上,并把对应列加到SELECT列表中,如下所示SELECT*FROMemployeese,sal_historysWHEREe.emp_seq=s.emp_seqAND“(e.emp_seq,s.emp_seq,s.effective_date)IN”(SELECT“emp_seq”,emp_seq,max(effective_date)FROMsal_historys1WHEREeffective_date<=SYSDATEGROUPBYemp_seq)这就简化了判断,并让数据库决定使用哪一个。非连接准则进行OR运算如果查询中存在进行OR运算的准则怎么办?如果非连接准则与任何连接准则进行OR运算,由于与连接准则的OR运算,不能使用传递性了。能够使用的仅有的传递性是进行AND运算者。以下SQL说明了带有OR的查询,但是注意所有的连接准则是与非连接准则进行AND运算。SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=t.proj_seqAND(e.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=′SPOUSE′)ORt.rpt_date=SYSDATE-7)以下的SQL不使用括号来指定准则的优先次序。使用默认的优先次序——AND在OR之前执行。1SELECT*FROMemploveese,assignmentsa,time_sheetst2WHEREe.emp_seq=a.emp_seq3ORe.emp_seqIN(SELECTemp_seqFROMdependents4WHERErelation=′SPOUSE′)5ANDa.emp_seq=t.emp_seq6ANDa.proj_seq=t.proj_seq在这种情况下,在第5行和第6行中只有与非连接准则进行AND运算的连接准则。“e.emp_seq=a.emp_seq”的传递性不能使用。并且由于没有与非连接准则进行AND运算的连接准则引用“e.emp_seq”,就没有传递值。外部连接如果查询中存在非连接准则但是有外部连接怎么办?下一个实例给出了说明了SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seq(+)ANDa.emp_seq=t.emp_seq(+)ANDa.proj_seq=t.proj_seq(+)ANDe.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=′SPOUSE′)由于这里的非连接准则在“e.emp_seq”上有一个运算对象,并且在来自非外部连接表格的等价列处没有连接准则,就没有传递值。以下的实例与前一个略有不同。SELECT*FROMemployeese,assignmentsa,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seq(+)ANDa.proj_seq=t.proj_seq(+)ANDe.emp_seqIN(SELECTemp_seqFROMdependentsWHERErelation=′SPOUSE′)在这个实例中,TIME_SHEETS是仅有的外部连接表格。所以在这个实例中,传递性“e.emp_seq=a.emp_seq”能够使用,但是不能使“e.emp_seq”等于“t.emp_seq”。简单非连接准则的传递性以下的查询将被用于解释这个概念。1SELECT*FROMemployeese,dependentsd2WHEREe.emp_seq=d.emp_seq3AND“d.emp_seq=1001”非连接准则为“d.emp_seq=1001”(第3行)。由于基于连接准则的传递性,该查询可以重写为SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqAND“e.emp_seq=1001”现在这个非连接准则在EMPLOYEES表格上了。这应当视为单列运算对象带有两个可能值的传递性。非连接准则和嵌套选择的传递性这是一个单列传递性的区域,它可能是极为有益的。如果嵌套选择在选择列表中不具有GROUPBY语法和聚集,并且在引用连接列之一的主查询中有非连接准则,这种非连接准则就可以移动到嵌套选择中。例如,非连接准则是“e.emp_seqBETWEEN1001and1010”(第4行)。1SELECT*FROMemployeese,2(SELECTDISTINCTemp_seqFROMdependents)x3WHEREe.emp_seq=x.emp_seq4AND“e.emp_seqBETWEEN1001and1010”因为连接准则和传递性,该查询可以重写如下SELECT*FROMemployeese,(SELECTDISTINCTemp_seqFROMdependentsWHERE“emp_seqBETWEEN1001and1010”)xWHEREe.emp_seq=x.emp_seqANDe.emp_seqBETWEEN1001and1010其要点在于不把它视为能够接受多个值的单列运算对象,而是简单地在嵌套的选择中复制准则。驱动表格可能有带有没有索引之非连接准则的查询。例如,一个索引搜索没有办法开始查询。可能有确实具有有索引之非连接准则的查询。以及最后可能有不带有非连接准则的查询。另一个重要的考虑为连接准则是不是在两面都有索引。所有这些都必须考虑到最优化模式是什么。注意,下面引用的发连接准则包括了一个运算对象是一个子查询的情况。也要注意,对于带有多于一个表格的所有子查询以及主查询,都应当确定潜在的驱动表格。如果非连接准则存在确定驱动表格是根据使用的最优化模式。FIRSTROWS如果最优化模式设置为FIRSTROWS,那么对于表格/视图或者From子句中的嵌套选择(它不是外部连接的表格),对有索引的非连接准则和没有索引的非连接准则的数目进行计数。如果表格具有任何有索引的非连接准则,那么表格能够用于驱动查询。如果所有表格都没有有索引的非连接准则,那么潜在的驱动表格就是带有任何没有索引的非连接准则者。ALLROWS如果最优化模式设置为ALLROWS,那么对于表格/视图或者From子句中的嵌套选择(它不是外部连接的表格),只须非连接准则的数目进行计数,无论是有索引的还是没有索引的。潜在的驱动表格就是带有任何非连接准则者。(该计数后来将用于确定连接次序。)以下SQL语句将用于说明最优化模式和变换两个方面。SELECT*FROMemployeese,dependentsd,assignmentsaWHEREe.emp_seq=d.emp_seqANDe.emp_seq=a.emp_seqANDlnamelike‘SM%’ANDfname=‘ED’ANDrelation=‘SPOUSE’对于上面的SQL,如果最优化模式为FIRSTROWS,因为LNAME(我们可以忘记FNAME是连接索引的一部分这一事实)上的有索引的非连接准则,EMPLOYEES可以是驱动表格。RELATION上的非连接准则是没有索引的,所以IEPENDENTS不能是驱动表格。此外,ASSIGNMENTS上没有非连接准则。所以,仅有的潜在驱动表格是EMPLOYEES。对于上面的SQL,如果最优化模式为ALLROWS,因为EMPLOYEES和DEPENDENTS都有非连接准则,它们都可以是驱动表。而且,ASSIGNMENTS没有非连接准则,因而不能是驱动表。如果没有非连接准则如果绝对没有非连接准则,而且我们或者通过引用的完整性,或者通过用户连接,知道了层次(其中使用唯一索引一方是父级一方,而使用非唯一索引一方是子级一方),那么驱动表格应当是最顶端的父级。对于FIRSTROWS和ALLROWS两种最优化模式,都应当如此。例如,假设在以下的实例中没有引用的完整性SELECT*FROMemployeese,dependentsd,assignmentsaWHEREe.emp_seq=d.emp_seqANDe.emp_seq=a.emp_seqEMPLOYEES和DEPENDENTS之间的连接,使用了EMPLOYEES一方的唯一索引和DEPENDENTS一方的非唯一索引。所以,EMPLOYEES视为父级,而DEPENDENTS视为一个子级。EMPLOYEES和ASSIGNMENTS之间的连接,使用了EMPLOYEES一方的唯一索引。不过,在ASSIGNMENTS一方没有索引(有一个索引包括EMP_SEQ但是不作为导引列)。下一节“如果没有索引的连接准则”将解释这一点的重要性。所以对于上面的实例,EMPLOYEES会是驱动查询的唯一候选者(尽管在下一节中你将看到,由于缺乏连接索引,ASSIGNMENTS也可以被视为潜在的驱动者)。一个查询中有不止一个表格能够被视为最顶端的表格时怎么办?以下的SQL可说明。SELECT*FROMemployeese,dept_historydh,departmentsdWHEREe.emp_seq=dh.emp_seqANDdh.dept_seq=d.dept_seq在这种情况下,EMPLOYEES和DEPENDENTS都是DEPT_HISTORY的父级。所以,两者都能够被试用为驱动表格。没有索引的连接准则如果某个表格的连接没有索引,那么该表格是驱动查询的一个候选者,如果最优化模式为ALLROWS的话。注意,即使该表格不具有任何非连接准则而其它表格却有,该表格也能够驱动一个ALLROWS查询。如果最优化模式为FIRSTROWS,这样一个表格就不会如此对待,除非表格上有非连接准则。例如,使用前面的规则,给予带有有索引之准则的表格优先设置。以下的实例说明了SELECT*FROMemployeese,assignmentsa,projectspWHEREa.proj_seq=p.proj_seqANDe.emp_seq=a.emp_seq在上面的查询中,ASSIGNMENTS在“a.proj_seq”上不具有所引。所以ASSIGNMENTS也是ALLROWS最优化模式下的驱动查询的一个候选者。外部连接如果某个表格是外部连接的,它就不应当用作驱动表格。所以,如以下实例所示,即使在DEPENDENTS上有某个非连接准则,DEPENDENTS仍然不能用于驱动查询。SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seq(+)ANDd.relation(+)=‘SPOUSE’FROM子句嵌套选择以下的查询将说明外部连接带来的问题。由于嵌套选择作为非连接准则中的一个子查询,该子查询应当永远是一个驱动表格。事实上,所有不是外部连接的嵌套SELECTS都首先连接,无论它们是否具有非连接准则。在以下的查询中,子查询首先连接时出现了最佳方案,因而效率更高地驱动了历史记录的选择。在下面的ORDERED线索一节还要讨论这一点。SELECTe.emp_seq,e.lname,e.fname,s.sal,d.dept_seq,j.job_seqFROMemployeese,sal_historys,job_historyj,dept_historyd,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historyGROUPBYemp_seq)x1,(SELECTemp_seq,MAX(effective_date)col1FROMjob_historyGROUPBYemp_seq)x2,(SELECTemp_seq,MAX(effective_date)col1FROMdept_historyGROUPBYemp_seq)x3WHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.emp_seq=x1.emp_seqANDs.effective_date=x1.col1ANDj.emp_seq=x2.emp_seqANDj.effective_date=x2.col1ANDd.emp_seq=x3.emp_seqANDd.effective_date=x3.col1视图理想情况下,系统应当为非连接准则检验视图定义本身,同时检验查询包含该视图。如果视图定义包含一个视图,你就不会递归地检验该视图了。一个级别就足够了,往往这也是全部需要。也要记住视图可能是多个表格的一个连接。为了简化处理,把视图看作一个潜在的驱动表格。连接次序在决定连接次序之前,系统需要知道从何处开始。例如,什么是潜在的驱动表格/视图或者From子句中的嵌套选择。(参见上面的驱动表格。)ORDERED线索将用于指定连接次序。首先,对于我们将要指定连接次序的SQL语句,不用ORDERED线索对它进行测试。例如,这将是(不用ORDERED线索)测试的第一种置换。其它置换将根据决定的不同驱动表格。如果某个SQL语句具有能够驱动查询的两个表格,那么至少将有三种置换原始的和两种使用ORDERED线索和不同驱动表格的。采取驱动表格之一。看一下FROM子句中对象之间的层次,检验哪个连接例程将遇到“最多的”非连接准则(其中FIRSTROWS寻找有索引的非连接准则——如果它们存在,否则寻找没有索引的非连接准则;ALLROWS只是寻找最多的非连接准则,无论是否有索引)。所有对象以非连接准则连接之后,其它的连接就无关紧要了,所以不产生那些表格的变化。以下的实例将予以说明。实例1下面的实例是用于ALLROWS最优化模式。SELECTe.*FROMemployeese,time_sheetst,projectsp,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historysWHEREsal>100GROUPBYemp_seq)xWHEREe.emp_seq=t.emp_seqANDt.proj_seq=p.proj_seqANDp.name=‘PAFO’ANDt.emp_seq=x.emp_seqANDt.rpt_date=x.col1在PROJECTS上有一个非连接准则,对于嵌套选择也有一个。这个实例的层次显示在图10中。如图10所示,该层次把嵌套的选择描述为EMPLOYEES的一个子级。在这种情况下可以这样做,因为我们知道SAL_HISTORY是EMPLOYEES的一个子级。虚线显示了这种关系是通过传递性的,正如下面还要进一步介绍的。因为传递性,如果PROJECTS用作驱动表格,我们会希望在最多非连接准则的方向上连接。这表明我们希望尽快获得嵌套的选择。所以该连接应当是PROJECTS->TIME_SHEETS->nestedselect->EMPLOYEES.如果嵌套的选择用作驱动者,该连接应当是nestedselect->TIME_SHEETS->PROJECTS->EMPLOYEES实际情况是,在前面的SQL中所有非连接准则也都有索引,所以会使用相同的连接次序。实例2SELECT*FROMemployeese,(SELECTemp_SeqFROMdependents)xWHEREe.emp_seq=x.emp_seqANDe.emp_seqBETWEEN1001and1010下面的实例是用于FIRSTROWS或者ALLROWS最优化模式。EMPLOYEES和嵌套的选择之间的连接准则在双方都是有索引的。不过,由于非连接准则应当在嵌套的选择中复制(回顾上面关于传递性一节),那么两个对象都能够驱动查询。实例3这个查询意在用于FIRSTROWS最优化模式。这是使用的标准历史数据查询。这里的要点在于,每个准则中有运算对象之一的非连接准则是一个子查询。由于在每种情况下其它运算对象都是有索引的,它们被视为有索引的非连接准则。SELECTe.emp_seq,e.lname,e.fname,s.sal,d.dept_seq,j.job_seqFROMemployeese,sal_historys,job_historyj,dept_historydWHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.effective_date=(SELECTMAX(effective_date)FROMsal_historys1WHEREs.emp_seq=s1.emp_seqANDeffective_date<=SYSDATE)ANDj.effective_date=(SELECTMAX(effectiv_ate)FROMjob_historyj1WHEREj.emp_seq=j1.emp_seqANDeffective_date<=SYSDATE)ANDd.effective_date=(SELECTMAX(effective_date)FROMdept_historyd1WHEREd.emp_seq=d1.emp_seqANDeffective_date<=SYSDATE)这个实例的层次显示在图11中。虚线表示了传递的连接。由于每一个历史表格都有有索引的非连接准则,因而它们能够直接连接。所以,任何历史表格都能成为驱动表格。除了默认的以外,不同的连接次序如下SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEES或者SAL_HISTORY->JOB_HISTORY->DEPT_HISTORY->EMPLOYEES或者DEPT_HISTORY->SAL_HISTORY->JOB_HISTORY->EMPLOYEES或者DEPT_HISTORY->JOB_HISTORY->DEPT_HISTORY->EMPLOYEES或者JOB_HISTORY->DEPT_HISTORY->SAL_HISTORY->EMPLOYEES或者JOB_HISTORY->SAL_HISTORY->DEPT_HISTORY->EMPLOYEES实例4那么,如果实例3中的子查询移动到FROM子句中怎么办?现在嵌套的选择就有可能驱动查询。假设这是在ALLROWS最优化模式下。SELECTe.emp_seq,e.lname,e.fname,s.sal,d.dept_seq,j.job_seqFROMemployeese,sal_historys,job_historyj,dept_historyd,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historyWHEREeffective_date<=SYSDATEGROUPBYemp_seq)x1,(SELECTemp_seq,MAX(effective_date)col1FROMjob_historyWHEREeffective_date<=SYSDATEGROUPBYemp_seq)x2,(SELECTemp_seq,MAX(effective_date)col1FROMdept_historyWHEREeffective_date<=SYSDATEGROUPBYemp_seq)x3WHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.emp_seq=x1.emp_seqANDs.effectivedate=x1.col1ANDj.emp_seq=x2.emp_seqANDj.effective_date=x2.col1ANDd.emp_seq=x3.emp_seqANDd.effective_date=x3.col1这个实例的层次显示在图12中。注意,由于EMP_SEQ的传递性,在历史表格之间有传递的连接准则。例如,由于“s.emp_seq=x1.emp_seq”,以及“s.emp_seq=e.emp_seq”,以及“e.emp_seq=j.emp_seq”,以及“j.emp_seq=x2.emp_seq”,所以“x1.emp_seq=x2.emp_seq”。此外,注意对于历史表格,没有非连接准则,只有嵌套的选择。所以历史表格都不能驱动查询。在上面论述的任何情况下,对于驱动表格,FROM子句中的嵌套子查询永远都能是驱动者。现在我们具有更多的可能连接次序。为了使它更便于观察,给嵌套的选择以别名;SAL_HISTORY上的嵌套选择是S.N.;DEPT_HISTORY上的嵌套选择是DN;等等。此外,正如前面所述,系统不管没有非连接准则之对象的重新排列。S.N.->DN->JN->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEESS.N.->JN->DN->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEESDN->S.N.->JN->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEESDN->JN->S.N.->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEESJN->DN->S.N.->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEESJN->S.N.->DN->SAL_HISTORY->DEPT_HISTORY->JOB_HISTORY->EMPLOYEES再次注意,后面的表格在其次序中保持不变。初始次序可能变化,但是对于不同的连接次序它总是保持不变。集群表格如果某个表格组合在查询中的一个其它表格之内,那些表格就应当一起连接,而不要其它表格的中间连接。例如,ASSIGNMENTS、PROJECTS和TIME_SHEETS在相同的散列集群中。以下的查询说明了SELECT*FROMemployeese,assignmentsa,projectsp,time_sheetstWHEREe.emp_seq=a.emp_seqANDa.emp_seq=t.emp_seqANDa.proj_seq=p.proj_seqANDa.proj_seq=t.proj_seqANDt.rpt_dateBETWEEN′01-JAN-97′AND′30-JAN-97′这个实例的层次显示在图13中。传递的连接关系由图13中的虚线显示。仅有TIME_SHEETS具有非连接准则。所以TIME_SHEETS将是唯一的驱动者。由于集群和别处缺乏非连接准则,所有的集群表格都应当依次连接如下TIME_SHEETS->ASSIGNMENTS->PROJECTS->EMPLOYEESORDERED线索ORDERED线索永远可用。不过,如果连接准则允许连接的次序,系统的效率将更高。这往往表明增加与当前连接准则中传递性对应的连接准则。挑选连接次序时,系统必须检验是否存在显式的连接准则。如果没有,那么系统应当通过传递性检验连接准则的隐含之处。如果存在隐含的连接准则,该准则应当在测试之前加到查询中。由于参数已经嵌入在ORDERED线索之内,隐含的连接准则应当继续留在ORDERED线索中。允许用户手工使用ORDERED线索时,系统以这种方式处理连接次序,因为不需要为了其它最优化模式而修改SQL文本。对于下面的实例,虽然表格X1和X2之间没有显式的连接准则,通过传递性确实存在一个连接。检验的方法是沿着从X1到X2的连接路径。例如,从X1到S,连接为“x1.emp_seq=s.emp_seqANDx1.col1=s.effective_ate”。从S到E连接也通过EMP_SEQ。这就隐含了“x1.emp_seq=e.emp_seq”。继续沿着该连接路径到X2,从E到D也通过EMP_SEQ。因此可以说“x1.emp_seq=d.emp_seq”。最后该路径通过“d.emp_seq=x2.emp_seqANDd.effective_date=x2.col1”从D到X2。所以,该路径最后为“x1.emp_seq=x2.emp_seq”。由于ORDERED线索需要X1连接X2,并且隐含连接准则“x1.emp_seq=x2.emp_seq”存在,应当增加该准则作为ORDERED的一个参数。为了获得X2和X3之间的传递连接准则,可以执行同样的过程。例如,“x2.emp_seq=x3.emp_seq”。由于X3并非显式地连接到E,系统应当包括连接准则“x3.emp_seq=e.emp_seq”。对于隐含的连接准则,建议的ORDERED线索格式如下SELECT/*+ORDERED(x1,x2,x3,e,s,j,d,“x1.emp_seq=x2.emp_seq”,“x2.emp_seq=x3.emp_seq”,“x3.emp_seq=e.emp_seq”)*/e.emp_seq,e.lname,e.fname,s.sal,d.dept_seq,j.job_seqFROMemployeese,sal_historys,job_historyj,dept_historyd,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historyGROUPBYemp_seq)x1,(SELECTemp_seq,MAX(effective_date)col1FROMjob_historyGROUPBYemp_seq)x2,(SELECTemp_seq,MAX(effective_date)col1FROMdept_historyGROUPBYemp_seq)x3WHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.emp_seq=x1.emp_seqANDs.effectivedate=x1.col1ANDj.emp_seq=x2.emp_seqANDj.effective_date=x2.col1ANDd.emp_seq=x3.emp_seqANDd.effective_date=x3.col1FROM子句中的嵌套选择如果嵌套的选择在FROM子句中,并且非连接准则在主SQL模块中引用它,系统将核实把该准则移动到嵌套SELECT的WHERE子句中。例如SQL语句,SELECT*FROMemployeese,(SELECTDISTINCTemp_seq,relationFROMdependentsdWHEREbirthdate>‘01-jan-80’)xWHEREe.emp_seq=x.emp_seqANDx.relation=‘SPOUSE’能够变换为SELECT*FROMemployeese,(SELECTDISTNCTemp_seq,relationFROMdependentsdWHEREbirthdate>‘01-jan-80’ANDrelation=‘SPOUSE’)xWHEREe.emp_seq=x.emp_seq在这种情况下,非连接准则移动到子查询,以便进一步减少先前合格的、连接后要过滤的行数。应当注意,虽然某些数据库可能已经考虑到这一点,把该准则移动到WHERE子句还是有益的,加之更便于指定线索。进行NOT运算的逻辑一般说来,以优化而论,本文不考虑进行NOT运算的准则,因为它无关紧要。换句话说,典型情况下数据库自动处理进行NOT运算之准则的调换。NVL函数如果该函数的默认参数不等于运算对象常数,那么返回NULL时WHERENVL(cost,0)=10将取值为WHERE0=10。所以,由于默认值(0)不等于10,NVL函数能够去掉,因此允许准则使用索引。如果准则不能使用索引,就什么都不做。如果该列不是可空的,那么也什么都不做,因为完全没有要NVL函数的理由。WHERENVL(cost,0)=0不应当调换,除非存在着其它有索引的准则。例如,要是这种情况确实发生了,系统会不得不调换为WHEREcost=0ORcostISNULL。那仍然会需要不用其它有索引之准则的全表格扫描。例如,看一看下面的实例SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqANDe.hiredate=SYSDATE-7ANDNVL(d.birthdate,’01-JAN-80’)=’01-JAN-80’能够变换为现在,这能够进一步变换为SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqANDe.hiredate=SYSDATE-7AND(d.birthdate=’01-JAN-80’ORd.birthdateISNULL)SELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqANDe.hiredate=SYSDATE-7ANDd.birthdate=’01-JAN-80’UNIONALLSELECT*FROMemployeese,dependentsdWHEREe.emp_seq=d.emp_seqANDe.hiredate=SYSDATE-7ANDd.birthdateISNULL分别优化每个SQL模块(如每个嵌套的查询)变得更加可能了。此外,注意该变换不包括底层模块中标准的不等式。它不必如此,因为OR在同一列。下面将更详细地讨论进行OR运算的准则。进行OR运算的准则以下实例介绍了系统如何处理进行OR运算的准则。例如,SQL语句,select*fromtabwhereA=10ORB=20能够重写为select*fromtabwhere(A=10)UNIONALLSelect*fromtabwhere(A!=10)AND(B=20)如果列A和B可空,那么该方法会是某个WHERE子句写为,select*fromtabwhere(A=10)UNIONALLselect*fromtabwhere(A!=10ORAISNULL)AND(B=20)写成如下形式的WHERE子句whereA=10or(B=20ANDC=1)能够重写为WHEREA=10UNIONALLWHEREA!=10AND(B=20ANDC=1)如果列是可空的,应当增加‘ISNOTNULL’。新方案运算位图键迭代某个子查询输出多个值,然后去往位图索引时,就会发生这种情况。遇到IN等运算符时,也要检验是否发生这种情况。过去对于位图索引中的IN运算符,是BITMAPOR处理多值情况。其它线索在数据仓库中,可以使用一种星形模式来描述一个或多个非常大的事实表格,表格中包含着数据仓库中的主要信息和若干个维数小得多的表格(如查找表格),每个小表格包含着事实表格中某个具体属性条目有关的信息。星形查询是事实表格和若干查找表格之间的一种连接。每个查找表格连接事实表格,是使用一种主键到外来键的连接。不过,查找表格并不相互连接。星形变换是一种基于成本的查询变换,致力于高效地执行星形查询。虽然星形最优化可能对于小维数和致密的事实表格很适用,但是如果以下的任何一条成立的话,星形变换也可以被视为一种替代方案。例如,假若维数大或者假若事实表格稀疏,星形变换可能有用。此外,对于并非所有维的表格都具有限定性谓语的查询,星形变换可能有用。星形变换不依赖于计算维表格的笛卡尔乘积,这使它更适于事实表格稀疏和维数大的情况,这种情况会导致大的笛卡尔乘积,而与事实表格中实际匹配的行却不多。此外,星形变换不是依赖于连接索引,而是基于位图索引与事实表格中各列的结合。因此该变换能够结合与限定维严格对应的索引。没有必要产生许多连接索引,其中不同的列次序匹配不同查询中限定维的不同模式。“STAR_TRANSFORMATION”使本系统使用最好的方案,其中使用了变换。在事实表格中有位图索引和维表格中有足够的准则时,就会发生这种情况。带有特定特征的表格不支持星形变换。例如,带有以下特征的表格不支持。带有与位图存取路径不兼容之表格线索的表格,带有太少的位图索引的表格(为了优化器考虑产生一个子查询,事实表格一列上应当有一个位图索引),远程表格(不过,在产生的子查询中允许远程维表格),反连接表格,在子查询中已经用作维表格的表格,视图真正分离而不是视图部分的表格,具有良好的单表格存取路径的表格以及对于变换来说太小而不值得做的表格。以下实例说明了在查询中早期减少嵌套选择的结果。例如,如果用户输入下面带有FROM子句中的嵌套选择的查询。SELECTe.*FROMemployeese,time_sheetst,projectsp,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historysGROUPBYemp_seq)xWHEREe.emp_seq=t.emp_seqANDt.proj_seq=p.proj_seqANDp.name=‘PAFO’ANDt.emp_seq=x.emp_seqANDt.rpt_date=x.col1“ANDx.col1<SYSDATE”用户应当放置了带有嵌套选择的准则,如下所示。那样的话,就能够早期减少嵌套选择的结果。SELECTe.*FROMemployeese,time_sheetst,projectsp,(SELECTemp_seq,MAX(effective_date)col1FROMsal_historysGROUPBYemp_seq“HAVINGMAX(effective_date)<SYSDATE)”xWHEREe.emp_seq=t.emp_seqANDt.proj_seq=p.proj_seqANDp.name=‘PAFO’ANDt.emp_seq=x.emp_seqANDt.rpt_date=x.col1OBJECTINSTANCE如果某个表格不在最优化期间存取,系统将不能在SQL语句中鉴别是哪个表格。例如,在以下的SQL语句中,ASSIGNMENTS和PROJECTS都是仅仅由它们的索引存取在ALLROWS模式下表示的。换句话说,该表格不必存取。所以,如果用户对ASSIGNMENTS单击索引操作,在该SQL中将不会选中任何内容来确认该方案和该SQL之间的关系。SELECT*FROMemployeese,(SELECTDISTINCTemp_seqFROMassignmentsa,projectspWHEREa.proj_seq=p.proj_seq)xWHEREe.emp_seq=x.emp_seq系统至少应当检验该表格在整个SQL语句中是不是唯一的。如果是,那么用户单击索引操作时,系统应当选中该表格。OBJECTINSTANCE在FROM子句中有嵌套选择时,本系统不考虑给予嵌套选择的OBJECT_INSTANCE值,以及给予嵌套选择内部对象的OBJECT_INSTANCE值。改变SQL如果用户希望FIRSTROWS模式,一般说来在把子查询移动到FROM子句中它不会有意义。合并总是好的,但是不必移动。例如检验历史数据查询的实例。事实上,离开关联的子查询,或者甚至把不关联的转换为关联的可能有意义。增加ORDERED线索时,它申请一个不同于指定的原始SQL的连接次序,系统重新排列FROM子句并传递到数据库。不过,系统重新排列时,它仍然显示带有参数的原始ORDERED线索。由于参数不是实际线索语法的一部分,显示SQL的HINTS模式时,系统丢弃了ORDERED线索的参数。例如,如果EDIT窗口中的原始SQL为SELECT/*+HINT1ORDERED(s,d,e,j)*/*FROMemployeese,sal_historys,job_historyj,dept_historydWHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.sal>500ANDj.job_seq=10ANDd.effective_date=j.effective_dateANDs.effective_date=j.effective_datetheHINTS1windowshoulddisplaySELECT/*+ORDERED*/*FROMsal_historys,dept_historyd,employeese,job_historyjWHEREe.emp_seq=s.emp_seqANDe.emp_seq=j.emp_seqANDe.emp_seq=d.emp_seqANDs.sal>500ANDj.job_seq=10ANDd.effective_date=j.effective-dateANDs.effective_date=j.effective_date这与关键字HINTS1的消失一致,再加上FROM子句的变化。换句话说,对于特定的模式,SQL窗口应当原原本本地显示传递到数据库的内容。当连接准则增加到ORIERED线索时,应当这样做。报告从产生一种直方图来说,推荐哪些“有索引的列(单列索引)”也许有益,会是一种有用的报告。由于仅仅对于不具有正态分布的列,才应当构造直方图,我们需要找到不具有正态分布的列。“线索”对于ALLROWS,“连接到表格”索引大于表格时,表示FULL。SQL检验如果子查询以关系运算符NOTIN或任何形式的ALL连接,系统就能够显示以下消息,并选中该子查询——如果可能的话“如果子查询不返回行(空集),包括该子查询的准则将取值为TRUE,换句话说,子查询不返回行时,它的作用与NOTEXISTS完全相同。”改变SQL子查询返回了将馈入引用同一表格之准则的列时,应当改变该子查询的选择列表,使它包含ROWID。例如,看一看下面的历史查询,SELECT*FROMemployeese,sal_historys1WHEREe.emp_seq=s1.emp_seqANDs1.effective_date=(SELECTmax(effective_date)FROMsal_historys2WHEREe.emp_seq=s2.emp_seqANDs2.effective_date<=SYSDATE)可以变换为SELECT*FROMemployeese,sal_historys1WHEREe.emp_seq=s1.emp_seqANDs1.“ROWID”=(SELECT“ROWID”FROMsal_historys2WHEREe.emp_seq=s.emp_seqANDs2.effective_date<=SYSDATE)线索应用线索时,用户必须负责查看线索是否见效。本系统能够提供反馈,通知用户线索确实具有所需的效果。例如,系统可能通知用户ORDERED线索是否以指定的次序连接。对于包括NOTIN或NOTEXISTS子查询的查询,可以应用传递性,所以外层查询中的连接列属于同一表格。这就合并了NOTIN和NOTEXISTS子查询,因为一个表格不能外部连接到多于一个表格。例如以下的SQL语句,SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqAND(e.emp_seq,a.start_date)NOTIN(SELECTemp_seq,rpt_dateFROMtime_sheets)能够转换为SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqAND(a.emp_seq,a.start_date)NOTIN(SELECTemp_seq,rpt_dateFROMtime_sheets)然后它能够转换为SELECTe.*,a.*FROMemployeese,assignmentsa,time_sheetst1WHEREe.emp_seq=a.emp_seqANDa.emp_seq=t1.emp_seq(+)ANDa.start_date=t1.rpt_date(+)ANDt1.ROWIDISNULL以下的实例说明了合并要连接的NOTIN和NOTEXISTS子查询。例如,SQL语句,SELECT*FROMemployeeseWHERENOTEXISTS(SELECT*FROMassignmentsaWHEREe.emp_seq=a.emp_seq)将转换为SELECT/*+ALL_ROWS*/e.*FROMemployeese,(SELECTDISTINCT1col2,a.emp_seqcol1FROMassignmentsa)t1WHEREe.emp_seq=t1.col1(+)ANDt1.col2ISNULL不过,该子查询应当合并如下SELECTe.*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seq(+)ANDa.emp_seqISNULL系统通过把外层的连接列中的常数移动到子查询中,能够把SQL4转换到SQL5再转换到SQL6,如下所示。例如,原始SQL语句,SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqAND(e.emp_seq,′22-FEB-94′)NOTIN(SELECTemp_seq,rpt_dateFROMtime_sheets)SQL4能够变换为SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqANDe.emp_seqNOTIN(SELECTemp_seqFROMtime_sheetsWHERErpt_date=′22-FEB-94′)SQL5它能够变换为SELECTe.*,a.*FROMemployeese,assignmentsa,time_sheetst1WHEREe.emp_seq=a.emp_seqANDa.emp_seq=t1.emp_seq(+)ANDt1.rpt_date(+)=′22-FEB-94′ANDt1.ROWIDISNULLSQL6以下的查询不能转换,因为如果合并,该子查询表格必须外部连接到一个表格。该子查询不能外部连接到一个单独的常数。SELECT*FROMemployeese,assignmentsaWHEREe.emp_seq=a.emp_seqAND′22-FEB-94′NOTIN(SELECTrpt_dateFROMtime._sheets)SQL7如果一个子查询的连接列是一个常数,就能够执行合并,如果连接时所有表格都唯一的话。合并该子查询时,常数Θ设置为NOTEQUALS对应的外层连接列,并且与‘<child>.ROWIDISNULL’进行OR运算。例如,SQL语句,SELECT*FROMemployeeseWHERE(e.emp_seq,lname)NOTIN(SELECTemp_seq,′SMITH′FROMtime_sheetsWHEREproj_seq=1ANDrpt_date=′22-feb-94′)SQL8能够变换为SELECTe.*FROMemployeese,time_sheetst1WHEREe.emp_seq=t1.emp_seq(+)ANDproj_seq(+)=1ANDrpt_date(+)=′22-feb-94′AND(t1.ROWIDISNULLORlname<>′SMITH′)SQL9试图变换SQL语句时必须谨慎。例如,可能试图把以下的SQL语句SELECT*FROMemployeeseWHEREemp_seqNOTIN(SELECTa.emp_seqFROMprojectsp,assignmentsaWHEREa.proj_seq=p.proj_seqANDp.name=′EXPLAINSQLDEVELOPMENT′)ORDERBY1变换为以下的SELECT/*+ALL_ROWS*/e.*FROMemployeese,projectsp,assignmentsaWHEREe.emp_seq=a.emp_seq(+)ANDa.proj_seq=p.proj_seq(+)ANDp.name(+)=′EXPLAINSQLDEVELOPMENT′ANDp.ROWIDISNULLORDERBY1不过,这会是不正确的,由于不可能把这里的原始SQL合并到外部查询。这会导致一种不正确的变换。如果系统试图增加带有任意表格的‘ROWIDISNULL’子句,可能发生另一个问题。例如,有时系统也许挑选项目表格,有时它也许挑选分配表格,取决于FROM子句中的次序。这会是不正确的。应当谨慎地确保最低级别的子级表格为空(如项目表格)。否则,向子表格(在这种情况下是项目)连接准则将是不适当的。在变换期间,应当遵循以下的规则和方针。A)只有满足以下条件,本系统才应当进行变换i)在连接次序中的所有中间表格都应当连接以保证唯一性。ii)可以有多于一个分支。不过,最多只能有一个最低级别的子表格可为非唯一。按次序加如时,所有其它的表格都应当是唯一的。换句话说,检验以上的SQL时,分配表格就是带有连接子查询列的表格。由于它不是最低级别的子级(项目表格连接它),它在选择列表中或者在WHERE子句中的列连接带有=运算符的常数,必然形成一个独特的键。不过,emp_seq不是唯一的列。所以这个子查询不能合并。不过,项目名称是唯一的列时,下面的实例SQL10能够转换。iii)如果子查询连接列之一是一个常数,所有表格都应当连接以保证唯一性。iv)没有聚集函数(只要没有聚集,GROUPBY、DISTINCT和HAVING都可以。合并时,忽略GROUPBY和DISTINCT并把HAVING准则移动到WHERE子句)v)没有CONNECTBY语法vi)没有集合运算(如UNION、MINUS)vii)子查询不包含外部连接。B)对于每个更低级别的子级,增加‘<child>.ROWIDISNULL’并用OR连在一起。C)如果在外层的连接列中有一个常数并且有其它的连接列,改变它为与子查询WHERE子句的相等准则。D)如果在子查询的连接列中的一个常数中有一个常数,设置该常数为NOTEQUALS对应的外层连接列并把它与‘<child>.ROWIDISNULL’进行OR运算。SELECT*FROMstatus_listWHEREstatusNOTIN(SELECTp.statusFROMprojectsp,assignmentsaWHEREa.proj_seq=p.proj_seqANDp.name=′EXPLAINSQLDEVELOPMENT′)SQL10确定一个外部连接是否能够发生时,关联的子查询可能会产生一个问题。确切地说,当NOTIN运算符连接到关联的子查询时,外层查询中的连接列必须属于与子查询关联的表格相同的表格。例如,参见下面的SQL语句。子查询中的连接列应当属于与子查询关联的表格相同的表格。在下面的实例中,HIREDATE列属于“e.”以及关联准则之一,而其它关联准则引用“a.”所以合并是不可能的。SELECT*FROMtime_sheetstWHEREt.rpt_dateNOTIN(SELECThiredateFROMemployeese,assignmentsaWHEREa.proj_seq=t.proj_seqANDe.emp_seq=a.emp_seqANDe.emp_seq=t.emp_seq)SQL11“[NOTIN]子查询中的连接列必须属于与子查询关联的表格相同的表格”不是必需的,只要上面的规则/方针A)i)和A)ii)成立。不过,因为外部连接,外层查询中对应的连接列应当属于同一表格。如果进行转换,分配表格既需要外部连接到TIME_SHEETS,又需要外部连接到EMPLOYEES。这是不合法的。不过,可以应用传递性来把上面显示的SQL11语句转换为下面显示的SQL12。然后SQL12可以合并到下面显示的SQL13。这种合并是可能的,因为EMPLOYEES和ASSIGNMENTS在连接列上都有唯一的索引。SELECT*FROMtime_sheetstWHEREt.rpt_dateNOTIN(SELECThiredateFROMemployeese,assignmentsaWHEREa.proj_seq=t.proj_seqANDt.emp_seq=a.emp_seqANDe.emp_seq=t.emp_seq)SQL12SELECT*FROMtime_sheetst,employeese,assignmentsaWHEREe.hiredate(+)=t.rpt_dateANDa.proj_seq(+)=t.proj_seqANDa.emp_seq(+)=t.emp_seqANDe.emp_seq(+)=t.emp_seqAND(a.rowidISNULLore.rowidISNULL)SQL13也应当注意,你不能合并在子查询SELECT子句中带有non-column(常数、函数等等)的NOTEXISTS或NOTIN子查询。例如,可以把以下的SQL14试图变换为SQL15。不过,因为t.rpt_date=’22-FEB-94’,SQL15产生的结果集与SQL14不同。该准则可以改变为t.rpt_date<>’22-FEB-94’。不过,我们将不得不保证原始子查询返回至少一行。这就破环了变换的目的,因为我们不得不保持该子查询。SELECT*FROMtime_sheetstWHERE(t.emp_seq,t.rpt_date)NOTIN(SELECTemp_seq,′22-FEB-94′FROMemployees)SQL14SELECT/*+ALL_ROWS*/t.*FROMtime_sheetst,employeest1WHEREt.emp_seq=t1.emp_seq(+)ANDt.rpt_date=′22-FEB-94′ANDt1.ROWIDISNULLSQL15设置NOTIN、NOTEXISTS子查询可以为查询如果运算符是NOTIN或者NOTEXISTS,可以调用以下的例程,试图确定它是否应当试图把子查询转换为一个连接。<<codebegin>>IFsubquerycontainsaggregateinSelectlistORsubquerycontainsaggregateinHAVINGclauseORsubquerycontainsConnectBysyntaxORsubquerycontainssetoperationTHENRETURNFALSELOOPovereachtableXintheFROMlist,ifthereismorethan1joincriterion,applytransitivitysothetablejointothesametable,ifpossible.InitializeUnique_listtoemptyNon_Unqiue_table=nullLOOPovereachTABLEintheFROMlistcontaininginterfacecolumnsIFCheckTableUniqueIndexColsreturnTRUE(CheckTableUniqueIndexColsroutinechecksforagiventable,whetherallthecolumnsofitsanyoneuniqueindexesarepresentinthewhereclauseandhavea′=′operationandtheotheroperandisaconstantoracorrelated.)THENaddTABLEtotheUnique_listELSEIFNon_Unqiue_tableisnullTHENNon_Unqiue_table=TABLEELSERETURNFALSEENDLOOPovereachTABLEintheFROMlistcontaininginterfacecolumnsLOOPovereachTABLEintheFROMlistcontaininginterfacecolumnsIFCheck_Unique(TABLE,interfacetableofthesurroundingquery,Unique_list,Non_Unique_table)returnsFALSERETURNFALSEENDLOOPovereachtableintheFROMlistcontaininginterfacecolumns(Makesurealltableisjoined)LOOPovereachTABLEIFTABLEnotinunique_listandTABLE<>Non_unique_tableTHENRETURNFALSEENDLOOPovereachTABLERETURNTRUE<<codeend>>检验唯一性输入TABLEPARENT_TABLENONUNIQUE_TABLE<<codebegin>>LOOPovereachtableYjoinedtoTABLEIFY=PARENT_TABLEcontinueloopIFYisinUnique_listorYisNon_unique_table(ifYisalreadyjoined,cannotjointoanothertable)THENRETURNFALSELOOPovereachindexofYIFallindexedcolumnsarejoinedtoXwith=operatorORindexcolumns=constantTHENaddYtoUnique_listIFCheck_Unique(Y,X,Unique_list,Non_unique_table)returnsFALSETHENRETURNFALSEBREAK(uniqueindexfound,noneedtosearchanymore)ENDLOOPovereachindexofYIFYnotinUnique_listIFNon_unique_tableisnullNon_unique_table=Y(SinceYisnotunique,makesurenoothertableisjoinedtoY)LOOPovereachtableZjoinedtoYIFZnotinUnique_listandZ<>Non_unique_tableTHENRETURNFALSEENDLOOPovereachtableZjoinedtoYELSERETURNFALSEENDLOOPovereachtableYjoinedtoTABLERETURNTRUE<<codeend>>如果某个WHERE子句准则仅仅引用FROM子句中嵌套选择内的表格,就把它移动到该嵌套选择。例如,以下的SQLSELECT*FROMemployeese,(SELECTDISTINCTemp_seq,relationFROMdependentsdWHEREbirthdate>′01-jan-80′)xWHEREe.emp_seq=x.emp_seqANDx.reLation=′SPOUSE′能够转换为SELECT*FROMemployeese,(SELECTDISTINCTemp_seq,relationFROMdependentsdWHEREbirthdate>′01-jan-80′ANDx.relation=′SPOUSE′)xWHEREe.emp_seq=x.emp_seq如果某个HAVING子句准则不涉及组函数,它就能够移动到WHERE子句。例如,以下的SQL语句SELECTLNAME,count(*)FROMemployeesGROUPBYLNAMEHAVINGLNAME<′D′能够变换为SELECTLNAME,count(*)FROMemployeesWHERELNAME<′D′GROUPBYLNAME使用按照本说明书的指导编程的一台或多台常规的通用数字计算机和/或服务器,可以方便地实现本公开文件。根据本公开文件的指导,可以容易地准备适当的软件代码。此外,虽然以上的介绍引用了特定的数据库系统(如Oracle),但是应当理解,本公开文件不限于任何具体的数据库或数据库系统的类型。考虑到以上的指导,本公开文件许多另外的修改和变化都是可能的。所以应当理解,在附带的权利要求书的范畴之内,本公开文件的实现方式可以不同于本文中介绍的特定方式。权利要求1.一种调节数据库查询的方法,包括选择数据库查询;分析选定的数据库查询以确定选定的数据库查询的若干部分之间的关系;从多个可用的最优化模式中选择最优化模式;根据确定的关系和选定的最优化模式,通过修改选定的数据库查询的至少一个部分,调节选定的数据库查询;以及显示修改后的数据库查询。2.根据权利要求1的方法,其特征在于,该分析确定数据库查询之内的标记,标记是由分隔符分开的单词。3.根据权利要求1的方法,其特征在于,多个可用的最优化模式包括基于成本的和基于规则的模式。4.根据权利要求3的方法,其特征在于,基于成本的模式包括First_Rows模式和All_Rows模式。5.根据权利要求1的方法,进一步包括确定与使用调节后的数据库查询相关联的成本。6.根据权利要求5的方法,进一步包括比较两种成本,一种是与使用选定的数据库查询相关联的成本,另一种是与使用调节后的数据库查询相关联的成本。7.根据权利要求1的方法,进一步包括分析选定的数据库查询,以确定该数据库查询是否包括由NOTEXISTS、NOTIN和ALL子句中至少一个连接的至少一个子查询。8.根据权利要求7的方法,进一步包括提示用户根据该数据库查询是否包括NOTEXISTS、NOTIN和ALL子句中的至少一个,选择调节期间使用的优先设置。9.根据权利要求8的方法,其特征在于,该优先设置包括重写优先设置,使得用户能够在从NOTEXISTS运算符到NOTIN运算符的转换和从选定的数据库查询到外部连接的转换中,选择至少一个。10.根据权利要求8的方法,其特征在于,该优先设置包括重写优先设置,使得用户能够选择把ALL运算符连接的子查询转换为一个连接或外部连接。11.根据权利要求8的方法,其特征在于,该优先设置包括重写优先设置,使得用户能够选择是否使用一个NOTEXISTS运算符和一个外部连接中的至少一个,对NOTIN运算符连接的子查询进行转换。12.一种包括用于调节数据库查询的计算机可执行代码的计算机存储介质,包括使用户选择数据库查询的计算机可执行代码;用于分析选定的数据库查询以确定选定的数据库查询的若干部分之间关系的计算机可执行代码;使用户从多个可用的最优化模式中选择最优化模式的计算机可执行代码;用于根据确定的关系和选定的最优化模式,通过修改选定的数据库查询的至少一个部分,调节选定的数据库查询的计算机可执行代码;以及用于显示修改后的数据库查询的计算机可执行代码。13.根据权利要求12的计算机存储介质,其特征在于,该分析确定数据库查询之内的标记,标记是由分隔符分开的单词。14.根据权利要求12的计算机存储介质,其特征在于,多个可用的最优化模式包括基于成本的和基于规则的模式。15.根据权利要求14的计算机存储介质,其特征在于,基于成本的模式包括First_Rows模式和All_Rows模式。16.根据权利要求12的计算机存储介质,进一步包括用于确定与使用调节后的数据库查询相关联的成本的代码。17.根据权利要求16的计算机存储介质,进一步包括用于比较两种成本的代码,一种是与使用选定的数据库查询相关联的成本,另一种是与使用调节后的数据库查询相关联的成本。18.根据权利要求12的计算机存储介质,进一步包括用于分析选定的数据库查询的代码,以确定该数据库查询是否包括由NOTEXISTS、NOTIN和ALL子句中至少一个连接的至少一个子查询。19.根据权利要求18的计算机存储介质,进一步包括用于提示用户根据该数据库查询是否包括NOTEXISTS、NOTIN和ALL子句中的至少一个,选择调节期间使用的优先设置的代码。20.根据权利要求19的计算机存储介质,其特征在于,该优先设置包括重写优先设置,使得用户能够在从NOTEXISTS运算符到NOTIN运算符的转换和从选定的数据库查询到外部连接的转换中,选择至少一个。21.根据权利要求19的计算机存储介质,其特征在于,该优先设置包括重写优先设置,使得用户能够选择把ALL运算符连接的子查询转换为一个连接或外部连接。22.根据权利要求19的计算机存储介质,其特征在于,该优先设置包括重写优先设置,使得用户能够选择是否使用一个NOTEXISTS运算符和一个外部连接中的至少一个,对NOTIN运算符连接的子查询进行转换。23.一种用于调节数据库查询的、编程的计算机系统,包括一种显示器,用于向用户显示至少一个数据库查询;一种用户输入,使用户从显示的数据库查询中选择一个数据库查询,从多个可用的最优化模式中选择一个最优化模式;以及一种处理器,用于分析选定的数据库查询以确定选定的数据库查询的若干部分之间关系,并用于根据确定的关系和选定的最优化模式,通过修改选定的数据库查询的至少一个部分,调节选定的数据库查询,修改后的数据库查询通过显示器向用户显示。24.根据权利要求23的系统,其特征在于,该分析确定数据库查询之内的标记,标记是由分隔符分开的单词。25.根据权利要求23的系统,其特征在于,多个可用的最优化模式包括基于成本的和基于规则的模式。26.根据权利要求25的系统,其特征在于,基于成本的模式包括First_Rows模式和All_Rows模式。27.根据权利要求23的系统,其特征在于,该处理器确定与使用调节后的数据库查询相关联的成本。28.根据权利要求27的系统,其特征在于,该处理器比较两种成本的代码,一种是与使用选定的数据库查询相关联的成本,另一种是与使用调节后的数据库查询相关联的成本。29.根据权利要求23的系统,其特征在于,该处理器分析选定的数据库查询的代码,以确定该数据库查询是否包括由NOTEXISTS、NOTIN和ALL子句中至少一个连接的至少一个子查询。30.根据权利要求29的系统,其特征在于,该处理器提示用户根据该数据库查询是否包括NOTEXISTS、NOTIN和ALL子句中的至少一个,选择调节期间使用的优先设置。31.根据权利要求30的系统,其特征在于,该优先设置包括重写优先设置,使得用户能够在从NOTEXISTS运算符到NOTIN运算符的转换和从选定的数据库查询到外部连接的转换中,选择至少一个。32.根据权利要求30的系统,其特征在于,该优先设置包括重写优先设置,使得用户能够选择把ALL运算符连接的子查询转换为一个连接或外部连接。33.根据权利要求30的系统,其特征在于,该优先设置包括重写优先设置,使得用户能够选择是否使用一个NOTEXISTS运算符和一个外部连接中的至少一个,对NOTIN运算符连接的子查询进行转换。全文摘要一种调节数据库查询的方法包括选择数据库查询;分析选定的数据库查询以确定选定的数据库查询的若干部分之间的关系;从多个可用的最优化模式中选择最优化模式;根据确定的关系和选定的最优化模式,通过修改选定的数据库查询的至少一个部分,调节选定的数据库查询以及显示修改后的数据库查询。文档编号G06F17/30GK1592905SQ01811820公开日2005年3月9日申请日期2001年5月25日优先权日2000年5月26日发明者爱德华·凯茨尤兹库,蒙诺·萨拉库玛,翰-瓦农·沃,约翰·文森特,托马斯·沃林,乔伊斯·劳申请人:计算机联合思想公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1