我从入行以来主要做客户端,使用的数据库是SQLite,表结构比较简单。我给老师的SQL水平一半以上我就能搞定。现在偶尔需要去后台SQLServer查一些数据问题,显得有点捉襟见肘,尤其是各种JOIN,有时候分不清,就索性摸索一下,做个记录。前言在各种问答社区谈到SQL中各种JOIN的区别时,被引用最多的文章是C.L.的VisualRepresentationofSQLJoins。CodeProject上的莫法特。他确实简单明了,用Wen的图来帮助理解,效果很明显。这篇文章就按照他的讲解方式,稍微推一下,算是文章的粗略中文翻译。约定下面以两个数据库表Table_A和Table_B为例进行说明,它们的结构和数据如下:mysql>SELECT*FROMTable_AORDERBYPKASC;+----+---------+|PK|Value|+----+--------+|1|bothab||2|onlya|+----+--------+2rowsinset(0.00sec)mysql>SELECT*fromTable_BORDERBYPKASC;+----+--------+|PK|值|+----+--------+|1|bothab||3|onlyb|+----+--------+2rowsinset(0.00sec)其中Table_A和Table_B中PK都是1,2是Table_A独有的,3是Table_B独有的。常用的JOIN1、INNERJOININNERJOIN一般翻译为内连接。内连接查询可以将左表(A表)和右表(B表)中的关联数据连接起来并返回。维恩图:INNERJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AAINNERJOINTable_BBONA.PK=B.PK;查询结果:+------+------+-------+--------+|A_PK|B_PK|A_Value|B_Value|+------+------+----------+--------+|1|1|bothab|bothab|+------+------+--------+---------+1rowinset(0.00sec)注:A为Table_A的别名,B为Table_B的别名,下同.2.LEFTJOINLEFTJOIN一般翻译为左连接,也写作LEFTOUTERJOIN。左连接查询返回左表(A表)中的所有记录,而不管右表(B表)中是否有关联数据。在右表中找到的关联数据列也一起返回。维恩图:LEFTJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AALEFTJOINTable_BBONA.PK=B.PK;查询结果:+------+------+-------+--------+|A_PK|B_PK|A_Value|B_Value|+------+------+----------+--------+|1|1|bothab|bothba||2|NULL|onlya|NULL|+------+------+---------+--------+2rowsinset(0.00sec)3.RIGHTJOINRIGHTJOIN一般翻译为右连接,也写作RIGHTOUTERJOIN。右连接查询返回右表(B表)中的所有记录,而不管左表(A表)中是否有关联数据。在左表中找到的关联数据列也一起返回。维恩图:RIGHTJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AARIGHTJOINTable_BBONA.PK=B.PK;查询结果:+------+------+-------+--------+|A_PK|B_PK|A_Value|B_Value|+------+------+----------+--------+|1|1|bothab|bothba||NULL|3|NULL|onlyb|+------+------+---------+--------+2rowsinset(0.00sec)4.FULLOUTERJOINFULLOUTERJOIN一般是翻译为outerjoin,fulljoin,在实际查询语句中可以写成FULLOUTERJOIN或FULLJOIN。外连接查询可以返回左右表的所有记录,连接后返回左右表可以关联的记录。文氏图:FULLOUTERJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AAFULLOUTERJOINTable_BBONA.PK=B.PK;查询结果:ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear'FULLOUTERJOINPable.PK'atline4注意:我当前的示例使用不支持FULLOUTERJOIN的MySQL。应该返回的结果(使用UNION模拟):mysql>SELECT*->FROMTable_A->LEFTJOINTable_B->ONTable_A.PK=Table_B.PK->UNIONALL->SELECT*->FROMTable_A->RIGHTJOINTable_B->ONTable_A.PK=表_B。PK->WHERETable_A.PKISNULL;+-----+--------+-----+--------+|PK|Value|PK|Value|+-----+---------+-----+---------+|1|bothab|1|bothba||2|onlya|NULL|NULL||NULL|NULL|3|onlyb|+-----+--------+-----+--------+3rowsinset(0.00sec)以上四种是SQL中JOIN常见的类型和概念。看看他们的合影:是不是觉得总结起来少了点什么?学习数学集合不只有这些情况吗?确实,请继续阅读。扩展用法1.LEFTJOINEXCLUDINGINNERJOIN返回一个有左表但右表没有关联数据的记录集。维恩图:LEFTJOINEXCLUDINGINNERJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AALEFTJOINTable_BBONA.PK=B.PKWHEREB.PKISNULL;查询结果:+------+------+--------+--------+|A_PK|B_PK|A_Value|B_Value|+------+------+--------+--------+|2|NULL|onlya|NULL|+------+------+-------+----------+1rowinset(0.01sec)2.RIGHTJOINEXCLUDINGINNERJOIN返回包含右表但左表没有关联数据的记录集桌子。维恩图:RIGHTJOINEXCLUDINGINNERJOIN示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AARIGHTJOINTable_BBONA.PK=B.PKWHEREA.PKISNULL;查询结果:+------+------+--------+--------+|A_PK|B_PK|A_Value|B_Value|+------+------+--------+--------+|NULL|3|NULL|onlyb|+------+------+---------+----------+1rowinset(0.00sec)3.FULLOUTERJOINEXCLUDINGINNERJOIN返回左边不相关的记录集和正确的表。维恩图:完全外部连接不包括内部连接示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AAFULLOUTERJOINTable_BBONA.PK=B.PKWHEREA.PKISNULLORB.PKISNULL;因为OUT在MySQL的执行中使用了JOINTABLE,OUT查询的时候又报错了。ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear'FULLOUTERJOINTable_BBONA.PK=B.PKWHEREA.PKISNULLORB.PKISNULL'atline4应当返回的结果(用UNION模拟):mysql>SELECT*->FROMTable_A->LEFTJOINTable_B->ONTable_A.PK=Table_B.PK->WHERETable_B.PKISNULL->UNIONALL->SELECT*->FROMTable_A->RIGHTJOINTable_B->ONTable_A.PK=Table_B.PK->WHERETable_A.PKISNULL;+------+---------+------+--------+|PK|值|PK|值|+------+--------+------+------+|2|onlya|NULL|NULL||NULL|NULL|3|onlyb|+------+------+------+--------+2rowsinset(0.00sec)总结以上七种用法基本可以涵盖各种JOIN查询。七种用法的全家福:看着他们,我好像又回到了学数学求交集的时代……顺便贴一下C.L.Moffatt附SQL语句,一起学,味道会更好:JOIN比上面的多了除了这几种,还有更多的JOIN用法,比如CROSSJOIN(笛卡尔集),SELFJOIN,可以参考SQL加入幻灯片演示学习。1.CROSSJOIN返回左表和右表之间符合条件的笛卡尔集合。说明:示例查询:SELECTA.PKASA_PK,B.PKASB_PK,A.ValueASA_Value,B.ValueASB_ValueFROMTable_AACROSSJOINTable_BB;查询结果:+------+-----+--------+--------+|A_PK|B_PK|A_Value|B_Value|+------+-----+--------+--------+|1|1|bothab|bothba||2|1|onlya|bothba||1|3|bothab|onlyb||2|3|onlya|onlyb|+------+------+--------+----------+4rowsinset(0.00sec)上面提到的几个JOIN查询的结果可以通过在CROSSJOIN中加入条件来模拟,比如INNERJOIN对应CROSSJOIN...WHEREA.PK=B.PK。2.SELFJOIN返回表与自身连接后满足条件的记录。一般在表中有字段以主键作为外键时使用。例如Table_C的结构和数据如下:+--------+----------+------------+|EMP_ID|EMP_NAME|EMP_SUPV_ID|+--------+------------+------------+|1001|Ma|NULL||1002|Zhuang|1001|+------+------------+------------+2rowsinset(0.00sec)EMP_ID字段表示员工ID,EMP_NAME字段表示员工姓名,EMP_SUPV_ID表示主管ID。示例查询:现在我们想查询所有有主管的员工及其对应的主管ID和名称,可以通过SELFJOIN实现。SELECTA.EMP_IDASEMP_ID,A.EMP_NAMEASEMP_NAME,B.EMP_IDASEMP_SUPV_ID,B.EMP_NAMEASEMP_SUPV_NAMEFROMTable_CA,Table_CBWHEREA.EMP_SUPV_ID=B.EMP_ID;查询结果:+-------+----------+------------+------------+|EMP_ID|EMP_NAME|EMP_SUPV_ID|EMP_SUPV_NAME|+--------+---------+------------+------------+|1002|壮|1001|麻|+--------+------------+------------+----------------+1rowinset(0.00秒)
