在编写SQL脚本中,多表连接查询操作需要使用到on和where条件,但是经常会混淆两者的用法,从而造成取数错误。为此,使用测试数据进行总结如下:

1、现有两张测试表,table_a和table_b。
table_a表中的数据:14条记录,主键是(user_id,brand_id),是用户在每个月份访问的上网品牌及其访问频率。
table_b表中的数据:主键是user_id,是用户基本类别表,Type_Id是用户在该月份的用户类别。
 
(1)通过user_id和month_id连接起来的效果:
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. ;

最终的记录数等于主表的记录数。

主表的选择很重要,这里的记录数等于主表记录数,但不是总是这样的,与非主表中的记录组成也有关系。
(2)添加where条件的效果:
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. WHERE A1.Brand_Id='501B03'
  13. ;

说明where条件是对连接完成产生的查询结果的再次筛选。会影响到最后的记录数。
(3)继续添加where条件的效果:
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. WHERE A1.Brand_Id='501B03'
  13. AND A2.Type_Id=10
  14. ;

进一步说明,where条件是对连接产生的查询结果集的二次筛选。

(4)添加更多个on连接条件的效果:
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. AND A1.Brand_Id='501B03'
  13. ;
总结如下:这里是对主表添加了On条件,它会对主表参与多表连接的记录进行一次过滤,只有满足这个条件的主表记录才会参与与其他表的连接操作,获取其他表中的字段组成一条需要的记录;主表中不符合这个on条件的记录也会进入到最后的结果表中,但不会参与与其他表的连接,因此,记录中其他表中获取的字段处全是NULL。
这里可以体现出,主表的特殊性,在left join 里面,主表就是多表连接最左边的那张表,表里面所有的记录都会出现在最后的结果集中,但是只有满足on条件的记录才会参与与其他表的连接操作。
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. AND A2.Type_Id=10
  13. ;


总结如下:这里添加了对被连接表的On条件,这个条件也不会影响主表在最后结果集中的记录,它的作用主要是对被连接表首先进行一次筛选,它会让符合这个条件的被连接表中的记录去参与与主表的连接操作,其他不符合on条件的记录根本就没有机会参与与主表的连接。
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. AND A1.Brand_Id='501B03'
  13. AND A2.Type_Id=10
  14. ;

这个表就综合了上面两方面,使用Brand_Id='501B03'对主表进行一次筛选,让符合这一条件的记录参与与其他表的连接,其他记录不参与连接,但是仍进入结果表(空位字段补NULL);使用Type_Id=10对被连接表A2首先做一次筛选,让符合这一条件的记录参与与主表A1的连接,不符合这一条件的记录被剔除掉不予考虑,也没有机会参与连接和进入结果表。
(5)更改table_b的主键,将其设为(user_id,month_id),并添加一条记录如下:
这样再table_b中对应user_id=989832就有两条记录。
执行下面的SQL:
  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. ;

这样可以看到,最后查询结果集的记录数不再是14条记录,而是15条,这说明了一点,最后的结果集中的记录数并不是和主表中记录数一致的,而是由主表和被连接表根据连接条件共同确定的,还可以说明的一点是,不论on条件是什么样,多简单多复杂,只要没有where条件,最后的查询结果集中的记录数不会少于主表记录数。对于这个例子,主表中的14条记录都会进入结果表,由于主表中的连接条件只有User_Id相等,因此,对于A2表中的User_Id=989832由于存在两条记录,因此都会与A1表User_Id=989832的那一条记录相连,因此产生了两条连接结果记录,所以使得最后的结果集增至15条记录。

  1. SELECT A1.User_Id
  2. ,A1.Month_Id
  3. ,A1.Brand_Id
  4. ,A1.Rate
  5. ,A2.User_Id
  6. ,A2.Month_Id
  7. ,A2.Type_Id
  8. FROM table_a A1
  9. LEFT JOIN table_b A2
  10. ON A1.User_Id=A2.User_Id
  11. AND A1.Month_Id=A2.Month_Id
  12. ;

上面给A1和A2表的连接条件增加了一个Month_Id相等的条件,这样只有满足这一条件的A2表记录才会参与连接,这样就对A2表中的User_Id=989832剔除了一条记录,所以最后连接只产生了14条记录。

 
 
2、现有table_c,是用户访问上网品牌的频率表。Brand_Id是上网品牌,Rank_Id是它对于该用户的一个访问频率排名(Rate排名)。
现在要提取每个用户访问频率最高的前三项上网品牌,形成宽表,一个用户一条记录,没有的取空值。
SQL1: 
  1. SELECT a1.User_Id
  2. ,a1.User_Id
  3. ,a1.Brand_Id AS brand_rank1
  4. ,a2.Brand_Id AS brand_rank2
  5. ,a3.Brand_Id AS brand_rank3
  6. FROM table_c AS a1
  7. LEFT JOIN table_c AS a2
  8. ON a1.Month_Id=a2.Month_Id
  9. AND a1.User_Id=a2.User_Id
  10. AND a2.Rank_Id=2
  11. LEFT JOIN table_c AS a3
  12. ON a1.Month_Id=a3.Month_Id
  13. AND a1.User_Id=a3.User_Id
  14. AND a3.Rank_Id=3
  15. WHERE a1.Rank_Id=1
  16. ;
查询结果:
这正是我们期望的结果。
SQL2:

  1. SELECT a1.User_Id
  2. ,a1.User_Id
  3. ,a1.Brand_Id AS brand_rank1
  4. ,a2.Brand_Id AS brand_rank2
  5. ,a3.Brand_Id AS brand_rank3
  6. FROM table_c AS a1
  7. LEFT JOIN table_c AS a2
  8. ON a1.Month_Id=a2.Month_Id
  9. AND a1.User_Id=a2.User_Id
  10. LEFT JOIN table_c AS a3
  11. ON a1.Month_Id=a3.Month_Id
  12. AND a1.User_Id=a3.User_Id
  13. WHERE a1.Rank_Id=1
  14. AND a2.Rank_Id=2
  15. AND a3.Rank_Id=3
  16. ;

查询结果:

 
3、综上所述,予以总结如下:
(1)ON条件,不论是A1.COL1=A2.COL2还是A2.COL3=XX,都是对A2表(被连接表)进行的条件筛选,将符合这一条件的记录取出来,参与与主表的连接操作,不符合该条件的记录都会被考虑参与连接。对于条件A1.COL4=XXX,其作用是对A1表(主表)进行条件筛选,符合该条件的记录将会参与与其他表的连接,不符合该条件的记录也会进入最后的结果表,空位补NULL。而A1.COL1=A2.COL2其实也是对A1表的筛选,让满足条件的A1表中的记录参与与其他表的连接,不满足的空位补NULL继续进入结果表。不论怎么使用ON条件,只要没有WHERE条件筛选,最后查询的结果集的记录数都是大于等于主表原始的记录数。
(2)WHERE条件,是对查询完毕后的结果集进行的筛选。可使用的筛选条件字段为任意的,因为多表查询实质是多个表之间的全字段连接查询,只是可以指定最后对外显示多少个字段的记录。where条件会从根本上影响最后查询结果集的记录数。
(3)如果对连接查询后聚集函数结果进行的筛选则需要使用GROUP BY +HAVING搭配完成。
 

SQL中on和where的使用及其差异的更多相关文章

  1. SQL中Round(),Floor(),Ceiling()函数的浅析

    项目中的一个功能模块上用到了标量值函数,函数中又有ceiling()函数的用法,自己找了一些资料,对SQL中这几个函数做一个简单的记录,方便自己学习.有不足之处欢迎拍砖补充 1.round()函数遵循 ...

  2. 关于sql中in 和 exists 的效率问题,in真的效率低吗

    原文: http://www.cnblogs.com/AdamLee/p/5054674.html 在网上看到很多关于sql中使用in效率低的问题,于是自己做了测试来验证是否是众人说的那样. 群众: ...

  3. 学习sql中的排列组合,在园子里搜着看于是。。。

    学习sql中的排列组合,在园子里搜着看,看到篇文章,于是自己(新手)用了最最原始的sql去写出来: --需求----B, C, F, M and S住在一座房子的不同楼层.--B 不住顶层.C 不住底 ...

  4. SQL中distinct的用法

    SQL中distinct的用法   1.作用于单列 2.作用于多列 3.COUNT统计 4.distinct必须放在开头 5.其他 在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出 ...

  5. hibernate中java类的成员变量类型如何映射到SQL中的数据类型变化

    hibernate映射文件??.hbm.xml配置映射元素详解--Hibernate映射类型 在从Hibernate的java的成员类型映射到SQL中的数据类型,其内映射方式它满足,SQL可以自己调制 ...

  6. C#调用SQL中的存储过程中有output参数,存储过程执行过程中返回信息

      C#调用SQL中的存储过程中有output参数,类型是字符型的时候一定要指定参数的长度.不然获取到的结果总是只有第一字符.本人就是由于这个原因,折腾了很久.在此记录一下,供大家以后参考! 例如: ...

  7. sql中case when语句的使用-来自网摘文章

    Case具有两种格式.简单Case函数和Case搜索函数. --简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END ...

  8. SQL中inner join、outer join和cross join的区别

    对于SQL中inner join.outer join和cross join的区别简介:现有两张表,Table A 是左边的表.Table B 是右边的表.其各有四条记录,其中有两条记录name是相同 ...

  9. SQL中对于两个不同的表中的属性取差集except运算

    SQL中对两个集合取差集运算,使用except关键字,语法格式如下: SELECT column_name(s) FROM table_name1 EXCEPT SELECT column_name( ...

随机推荐

  1. 基本控件文档-UISlider属性---iOS-Apple苹果官方文档翻译

    本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址  //转载请注明出处--本文永久链接:http://www.cnblogs.com/C ...

  2. Python 编码问题:出现中文乱码-- (转)

    问题描述: 在写Python代码的过程中,有用到需要输出中文的地方(python2.6.5在中文注释的地方就会出错),但是运行后会出错 我的错误显示: SyntaxError: Non-ASCII c ...

  3. otg device id pin 探討

    Platform : Qualcomm MSM8937 PMIC : Qualcomm PMI8940 OTG base on USB2.0,增加 device 可當 host 也可當 periphe ...

  4. 64_g3

    gimp-resynthesizer-2.0-6.20160601git787ee5a.fc2..> 11-Feb-2017 05:36 77650 gimp-save-for-web-0.29 ...

  5. Java中volatile修饰符,不稳定标记的用法笔记

    今天学java特性时,发现了volatile修饰符,这个修饰符修饰的变量告诉java编译器忽略优化机制,这样的优势是: java优化后,寄存器会缓存内存里的变量,另一个线程修改这个变量的内存时,不会同 ...

  6. 阿里云ECS的使用

    一.阿里云ECS的使用 1.Linux CentOS Ubuntu Readhat 2.远程登录 xshell 远程登录 winScp 远程文件操作 3.Linux命令 cd 目录名 ls . ls ...

  7. keil问题:Error: failed to execute 'C:\Keil\ARM\BIN\ArmCC'

    1.打开cmd,进入相应的路径下 cd C:\Keil\ARM\BIN\ 输入 armcc  若显示如下界面则说明keil已经注册 2.若注册成功还没解决问题,则说明软件是在网上下载的破解版的,建议卸 ...

  8. shell中引号的作用(转)

    引号的作用 1 双引号(“”) 1)使用””可引用除字符$(美元符号).`(反引号).\(反斜线)外的任意字符或字符串.双引号不会阻止shell对这三个字符做特殊处理(标示变量名.命令替换.反斜线转义 ...

  9. jequry_rotate.js用来写旋转类的东西的插件(如:抽奖转盘)

    网上发现一个很有意思的jQuery旋转插件,支持Internet Explorer 6.0+ .Firefox 2.0 .Safari 3 .Opera 9 .Google Chrome,高级浏览器下 ...

  10. Spring + MyBatis 多数据源实现

    近期,在项目中需要做分库,但是因为某些原因,没有采用开源的分库插件,而是采用了同事之前弄得多数据源形式实现的分库.对于多数据源,本人在实际项目也中遇到的不多,之前的项目大多是服务化,以RPC的形式获得 ...