风舞残阳 2008-4-2 17:55
MySQL数据库技术(16)
3.6检索记录
除非最终检索它们并利用它们来做点事情,否则将记录放入[wiki]数据库[/wiki]没什么好处。这就是SELECT语句的用途,即帮助取出数据。SELECT大概是[wiki]SQL[/wiki]语言中最常用的语句,而且怎样使用它也最为讲究;用它来选择记录可能相当复杂,可能会涉及许多表中列之间的比较。SELECT语句的语法如下:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202945791.JPG[/img]
除了词“SELECT”和说明希望检索什么的column_list部分外,语法中的每样东西都是可选的。有的数据库还需要FROM子句。MySQL有所不同,它允许对表达式求值而不引用任何表:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202945715.JPG[/img]
在第1章中,我们对SELECT语句下了很大的功夫,主要集中介绍了列选择的列表和WHERE、GROUPBY、ORDERBY、HAVING以及LIMIT子句。本章中,我们将主要精力放在SELECT语句中最可能令人搞不清的方面,即连接(join)上。我们将介绍MySQL支持的连接[wiki]类[/wiki]型、它们的含义、怎样指定它们等。这样做将有助于更有效地使用MySQL,因为在许多情况下,解决怎样编写查询的关键是确定怎样将表恰当地连接在一起。还应该参阅一下本章后面3.8节“解决方案随笔”。在那一节中将会找到解决几个SQL问题的方案,它们多数
都涉及SELECT语句这样或那样的功能。
使用SELECT的一个问题是,在第一次遇到一种新的问题时,并不总是能够知道怎样编写SELECT查询来解决它。但在解决以后,再遇到类似的问题时,可利用其中的经验。SELECT大概是过去的经验在能够有效地使用中起很大作用的语句,这是因为使用它的方法太多的原故。
在有了一定的经验后,可将这些经验用于新问题,您会发现自己思考问题类似于,“噢,是的,它就是一个LEFTJOIN问题。”或者,“啊哈,这就是一个受各对索引列制约的三路线连接。”(指出这一点,实际上我也感到有点不愿意。听到经验有帮助,您可能受到一定的鼓舞。另外,考虑到您最终能那样思考问题也会令自己有点惊讶。)下几节中介绍怎样利用MySQL支持的连接操作的格式,多数例子使用了下面的两个表。它们很小,很简单,足以很清楚地看出每种连接的效果。
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202945769.JPG[/img]
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202945262.JPG[/img]
[color=#0000ff]3.6.1平凡连接
[/color]
最简单的连接是平凡连接(trivialjoin),这种连接中只指定一个表。在此情况下,行从指定的表中选择。如:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202945678.JPG[/img]
有的作者根本就不考虑这种SELECT连接的形式,仅对从两个或多个表中检索记录的SELECT语句使用“连接”这个术语。本人认为那只是看法不同而已。
[color=#0000ff]3.6.2全连接
[/color]
如果指定多个表,将各个表名用逗号分隔,就指定了全连接。例如,如果连接两个表,来自第一个表中的每行与第二个表中每行进行组合:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202946102.JPG[/img]
全连接也称为叉连接,因为每个表的每行都与其他表中的每行交叉以产生所有可能的组合。这也就是所谓的笛卡儿积。这样连接表潜在地产生数量非常大的行,因为可能得到的行数为每个表中行数之积。三个分别含有100、200、300行的表的全连接将产生100×200×300=6百万行。即使各表很小,所得到的行数也会很大。在这样的情形下,通常要使用WHERE
子句来将结果集减少为易于[wiki]管理[/wiki]的大小。
如果在WHERE子句中增加一个条件使各表在某些列上进行匹配,此连接就是所谓的等同连接(equi-join),因为只选择那些在指定列中具有相等的值的行。如:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202946339.JPG[/img]
JOIN、CROSSJOIN和INNERJOIN连接类型都与“,”连接操作符意义相同。STRAIGHT_JOIN与全连接类似,但各表按FROM子句中指定的次序进行连接。一般情况下,在全连接中MySQL优化[wiki]程序[/wiki]自身完全不考虑安排各表的顺序,以便使记录的检索更快。在有的场合,优化程序将作出非优化的选择,这样将忽略STRAIGHT_JOIN关键字。在SELECT语句中,可在两个位置给出STRAIGHT_JOIN。一个位置是在SELECT关键字与选择列表之间,将其放在这里对语句中所有全连接具有整体作用。另一个在FROM子句中。下面两条语句是等价的:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202946286.JPG[/img]
限定列引用
SELECT语句中列的引用必须对FROM子句中指定的每个表是无歧义的。如果FROM子句中仅指定了一个表,则无歧义存在,因为所有列必须是该表的列。如果指定了多个表,只出现在一个表中的列名也是无歧义的。但是,如果某个列名出现在多个表中,该列的引用必须用表名来限定,用tbl_name.col_name语法来表明所指的是哪个表。如果表my_tbl1含有列a和b,表my_tbl2含有列b和c,则列a和c的引用是无歧义的,但b的引用必须限定为my_tbl1.b或my_tbl2.b,如:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202946944.JPG[/img]
有时,表名限定符还不能解决列的引用问题。例如,如果在一个查询中多次使用一个表,用表名限定列名没有什么用处。在此情况下,为表达您的想法可使用别名。给表指派一个别名,利用这个别名来引用列,其语法为:alias_name.col_name。下面的查询将表与
自身进行连接,给表指派了一个别名,以便应付引用列时有歧义的情况:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202946285.JPG[/img]
[color=#0000ff]3.6.3左连接
[/color]
等价连接只给出两个表匹配的行。左连接也给出匹配行,但它还显示左边表中有的但在右边表中无匹配的行。对于这样的行,从右边表中选择的列都显示为NULL。这样,每一行都从左边表中选出。如果右边表中有一个匹配行,则该行被选中。如果不匹配,行仍然被选中,但它是一个“假”行,其中所有列被设置为NULL。换句话说,LEFTJOIN强制结果集包含对应左边表中每一行的行,而不管左边表中的行在右边表中是否有匹配的行。匹配是根据ON或USING()子句中给出的列进行的。不管所连接的列是否具有相同的名称,都可使用ON。如:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202947291.JPG[/img]
USING()子句类似于ON,但连接列的名称必须在每个表中是相同的。下面的查询将my_tbl1.b连接到my_tbl2.b:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202947904.JPG[/img]
在希望只查找出现在左边表而不出现在右边表中的行时,LEFTJOIN极为有用。可通过增加一条查询右边表中具有NULL值的列的WHERE子句来完成这项工作。
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202947247.JPG[/img]
一般不用担心选择为NULL的列,因为没有什么意思。真正要关心的是左边表中不匹配的列,如:
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202947973.JPG[/img]
利用LEFTJOIN时有一件事情需要提防,如果所连接的列未定义为NOTNULL,将会在结果中得出一些无关的行。
LEFTJOIN有几个同义词和变种。LEFTOUTERJOIN为LEFTJOIN的一个同义词。
LEFTJOIN还有一个为MySQL所接受的ODBC表示如下(“oj”意为“outerjoin”):
[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202947703.JPG[/img]
NATURALLEFTJOIN类似于LEFTJOIN;它执行一个LEFTJOIN,匹配左边表和右边表中具有相同名称的所有列。
有的数据库还有,RIGHTJOIN,但MySQL迄今还没有。