关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » SQL语言 »T-SQL查询进阶之深入理解子查询(1)

    T-SQL查询进阶之深入理解子查询(1)

    2011-07-18 14:38:00 出处:ITJS
    分享

    子查询本质上是嵌套进其他SELECT,UPDATE,INSERT,DELETE语句的一个被限制的SELECT语句,在子查询中,只有下面几个子句可以使用:SELECT子句(必须)、FROM子句(必选)、 WHERE子句(可选)  、GROUP BY(可选) 、HAVING(可选) 和ORDER BY(只有在TOP关键字被使用时才可用)。

    子查询也可以嵌套在其他子查询中,这个嵌套最多可达32层。子查询也叫内部查询(Inner query)或者内部选择(Inner Select),而包含子查询的查询语句也叫做外部查询(Outter)或者外部选择(Outer Select),子查询的概念可以简单用下图阐述:

    T-SQL查询进阶之深入理解子查询

    T-SQL查询进阶之深入理解子查询

    http://wiki.lessthandot.com/index.php/Subquery_typo_with_using_in。该文的观点是永远不要再用IN和NOT IN关键字,我的观点是存在即合理,我认为只有在IN里面是固定值的时候才可以用IN和NOT IN,比如:

    T-SQL查询进阶之深入理解子查询

    上图是作为数据源使用的一个子查询。

    通常来讲,子查询按照子查询所返回数据的类型,可以分为三种,分别为:

    返回一张数据表(Table)

    返回一列值(Column)

    返回单个值(Scalar)

    下面,我们按照这三种方式来阐述子查询

    子查询作为数据源使用

    当子查询在外部查询的FROM子句之后使用时,子查询被当作一个数据源使用,即使这时子查询只返回一个单一值(Scalar)或是一列值(Column),在这里依然可以看作一个特殊的数据源,即一个二维数据表(Table).作为数据源使用的子查询很像一个View(视图),只是这个子查询只是临时存在,并不包含在数据库中。

    比如这个语句:

    SELECT P.ProductID, P.Name, P.ProductNumber, M.Name   AS ProductModelName   FROM Production.Product AS P   INNER JOIN   (   SELECT Name, ProductModelID   FROM Production.ProductModel)   AS M   ON P.ProductModelID = M.ProductModelID 

    上述子查询语句将ProductModel表中的子集M,作为数据源(表)和Product表进行内连接。结果如下:

    作为数据源使用也是子查询最简单的应用。当然,当子查询作为数据源使用时,也分为相关子查询和无关子查询,这会在文章后面介绍到.

    子查询作为选择条件使用

    作为选择条件的子查询也是子查询相对最复杂的应用.

    作为选择条件的子查询是那些只返回一列(Column)的子查询,假如作为选择条件使用,即使只返回单个值,也可以看作是只有一行的一列.比如,在AdventureWorks中:我想取得总共请病假天数大于68小时的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID IN   (SELECT EmployeeID   FROM [AdventureWorks].[HumanResources].[Employee]   WHERE SickLeaveHours>68) 

    结果如下:

    T-SQL查询进阶之深入理解子查询

    上面的查询中,在IN关键字后面的子查询返回一列值作为外部查询的选择条件使用。

    同样的,与IN关键字的逻辑取反的NOT IN关键字,这里就不再阐述了。

    但是要强调的是,不要用IN和NOT IN关键字,这会引起很多潜在的问题,该文对这个问题有着很好的阐述:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID IN (25,33) 

    只有在上面这种情况下,使用IN和NOT IN关键字才是安全的,其他情况下,最好使用EXISTS,NOT EXISTS,JOIN关键字来进行替代. 除了IN之外,用于选择条件的关键字还有ANY和ALL,这两个关键字和其字面意思一样. 和"<",">",”="连接使用,比如上面用IN的那个子查询:

    我想取得总共请病假天数大于68小时的员工

    用ANY关键字进行等效的查询为:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID =ANY  SELECT EmployeeID   FROM [AdventureWorks].[HumanResources].[Employee]   WHERE SickLeaveHours>68) 

    在作为ANY和ALL关键字在子查询中使用时,所实现的效果如下:

    =ANY 和IN等价
    <>ANY 和NOT IN等价
    >ANY 大于最小的(>MIN)
    <ANY 小于最大的(<MAX)
    >ALL 大于最大的(>MAX)
    <ALL 小于最小的(<MIN)
    =ALL 下面说

    =ALL关键字很少使用,这个的效果在子查询中为假如只有一个返回值,则和“=”相等,而假如有多个返回值,结果为空。

    这里要注意,SQL是一种很灵活的语言,就像子查询所实现的效果可以使用JOIN来实现一样(效果一样,实现思路不同),ANY和ALL所实现的效果也完全可以使用其他方式来替代,按照上面表格所示,>ANY和>MIN完全等价,比如下面两个查询语句完全等价:

    SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>ANY   (SELECT SickLeaveHours   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>68)   SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>  (SELECT MIN(SickLeaveHours)   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>68) 

    相关子查询和EXISTS关键字

    前面所说的查询都是无关子查询(Uncorrelated subquery),子查询中还有一类很重要的查询是相关子查询(Correlated subquery),也叫重复子查询比如,还是上面那个查询,用相关子查询来写:

    我想取得总共请病假天数大于68天的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    结果和使用IN关键字的查询结果相同:

    T-SQL查询进阶之深入理解子查询

    如何区别相关子查询和无关子查询呢 最简单的办法就是直接看子查询本身能否执行,比如上面的例子中的子查询:

    (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    这一句本身执行本身会报错.因为这句引用到了外部查询的表。

    对于无关子查询来说,整个查询的过程为子查询只执行一次,然后交给外部查询,比如:

    SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>ANY   SQLRESULT 

    上面的无关子查询,整个查询过程可以看作是子查询首先返回SQLResult(SQL结果集),然后交给外部查询使用,整个过程子查询只执行一次。

    而相反,作为相关子查询,子查询的执行的次数依赖于外部查询,外部查询每执行一行,子查询执行一次,比如:

    还是上面的例子:我想取得总共请病假天数大于68天的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    step 1:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE 1=e.ContactID AND e.SickLeaveHours>68) 

    step 2:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e/p>  WHERE 2=e.ContactID AND e.SickLeaveHours>68) 

    step n:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE n=e.ContactID AND e.SickLeaveHours>68) 

    如上面代码所示。上面的相关子查询实际上会执行N次(N取决与外部查询的行数),外部查询每执行一行,都会将对应行所用的参数传到子查询中,假如子查询有对应值,则返回TRUE(既当前行被选中并在结果中显示),假如没有,则返回FALSE。然后重复执行下一行。

    子查询作为计算列使用

    当子查询作为计算列使用时,只返回单个值(Scalar) 。用在SELECT语句之后,作为计算列使用。同样分为相关子查询和无关子查询

    相关子查询的例子比如:我想取得每件产品的名称和总共的销量。

    SELECT [Name],   (SELECT COUNT(*) FROM AdventureWorks.Sales.SalesOrderDetail S   WHERE S.ProductID=P.ProductID)AS SalesAmount   FROM [AdventureWorks].[Production].[Product] P 

    部分结果如下:

    当子查询作为计算列使用时,会针对外部查询的每一行,返回唯一的值。

    同样的,SQL子查询都可以使用其他语句达到同样的效果,上面的语句和如下语句达到同样的效果:

    SELECT P.Name,COUNT(S.ProductID)   FROM [AdventureWorks].[Production].[Product] P   LEFT JOIN AdventureWorks.Sales.SalesOrderDetail S   ON S.ProductID=P.ProductID   GROUP BY P.Name 

    子查询作为计算列且作为无关子查询时使用,只会一次性返回但一值,这里就不再阐述了。

    子查询本质上是嵌套进其他SELECT,UPDATE,INSERT,DELETE语句的一个被限制的SELECT语句,在子查询中,只有下面几个子句可以使用:SELECT子句(必须)、FROM子句(必选)、 WHERE子句(可选)  、GROUP BY(可选) 、HAVING(可选) 和ORDER BY(只有在TOP关键字被使用时才可用)。

    子查询也可以嵌套在其他子查询中,这个嵌套最多可达32层。子查询也叫内部查询(Inner query)或者内部选择(Inner Select),而包含子查询的查询语句也叫做外部查询(Outter)或者外部选择(Outer Select),子查询的概念可以简单用下图阐述:

    T-SQL查询进阶之深入理解子查询

    T-SQL查询进阶之深入理解子查询

    http://wiki.lessthandot.com/index.php/Subquery_typo_with_using_in。该文的观点是永远不要再用IN和NOT IN关键字,我的观点是存在即合理,我认为只有在IN里面是固定值的时候才可以用IN和NOT IN,比如:

    T-SQL查询进阶之深入理解子查询

    上图是作为数据源使用的一个子查询。

    通常来讲,子查询按照子查询所返回数据的类型,可以分为三种,分别为:

    返回一张数据表(Table)

    返回一列值(Column)

    返回单个值(Scalar)

    下面,我们按照这三种方式来阐述子查询

    子查询作为数据源使用

    当子查询在外部查询的FROM子句之后使用时,子查询被当作一个数据源使用,即使这时子查询只返回一个单一值(Scalar)或是一列值(Column),在这里依然可以看作一个特殊的数据源,即一个二维数据表(Table).作为数据源使用的子查询很像一个View(视图),只是这个子查询只是临时存在,并不包含在数据库中。

    比如这个语句:

    SELECT P.ProductID, P.Name, P.ProductNumber, M.Name   AS ProductModelName   FROM Production.Product AS P   INNER JOIN   (   SELECT Name, ProductModelID   FROM Production.ProductModel)   AS M   ON P.ProductModelID = M.ProductModelID 

    上述子查询语句将ProductModel表中的子集M,作为数据源(表)和Product表进行内连接。结果如下:

    作为数据源使用也是子查询最简单的应用。当然,当子查询作为数据源使用时,也分为相关子查询和无关子查询,这会在文章后面介绍到.

    子查询作为选择条件使用

    作为选择条件的子查询也是子查询相对最复杂的应用.

    作为选择条件的子查询是那些只返回一列(Column)的子查询,假如作为选择条件使用,即使只返回单个值,也可以看作是只有一行的一列.比如,在AdventureWorks中:我想取得总共请病假天数大于68小时的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID IN   (SELECT EmployeeID   FROM [AdventureWorks].[HumanResources].[Employee]   WHERE SickLeaveHours>68) 

    结果如下:

    T-SQL查询进阶之深入理解子查询

    上面的查询中,在IN关键字后面的子查询返回一列值作为外部查询的选择条件使用。

    同样的,与IN关键字的逻辑取反的NOT IN关键字,这里就不再阐述了。

    但是要强调的是,不要用IN和NOT IN关键字,这会引起很多潜在的问题,该文对这个问题有着很好的阐述:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID IN (25,33) 

    只有在上面这种情况下,使用IN和NOT IN关键字才是安全的,其他情况下,最好使用EXISTS,NOT EXISTS,JOIN关键字来进行替代. 除了IN之外,用于选择条件的关键字还有ANY和ALL,这两个关键字和其字面意思一样. 和"<",">",”="连接使用,比如上面用IN的那个子查询:

    我想取得总共请病假天数大于68小时的员工

    用ANY关键字进行等效的查询为:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact]   WHERE ContactID =ANY  SELECT EmployeeID   FROM [AdventureWorks].[HumanResources].[Employee]   WHERE SickLeaveHours>68) 

    在作为ANY和ALL关键字在子查询中使用时,所实现的效果如下:

    =ANY 和IN等价
    <>ANY 和NOT IN等价
    >ANY 大于最小的(>MIN)
    <ANY 小于最大的(<MAX)
    >ALL 大于最大的(>MAX)
    <ALL 小于最小的(<MIN)
    =ALL 下面说

    =ALL关键字很少使用,这个的效果在子查询中为假如只有一个返回值,则和“=”相等,而假如有多个返回值,结果为空。

    这里要注意,SQL是一种很灵活的语言,就像子查询所实现的效果可以使用JOIN来实现一样(效果一样,实现思路不同),ANY和ALL所实现的效果也完全可以使用其他方式来替代,按照上面表格所示,>ANY和>MIN完全等价,比如下面两个查询语句完全等价:

    SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>ANY   (SELECT SickLeaveHours   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>68)   SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>  (SELECT MIN(SickLeaveHours)   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>68) 

    相关子查询和EXISTS关键字

    前面所说的查询都是无关子查询(Uncorrelated subquery),子查询中还有一类很重要的查询是相关子查询(Correlated subquery),也叫重复子查询比如,还是上面那个查询,用相关子查询来写:

    我想取得总共请病假天数大于68天的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    结果和使用IN关键字的查询结果相同:

    T-SQL查询进阶之深入理解子查询

    如何区别相关子查询和无关子查询呢 最简单的办法就是直接看子查询本身能否执行,比如上面的例子中的子查询:

    (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    这一句本身执行本身会报错.因为这句引用到了外部查询的表。

    对于无关子查询来说,整个查询的过程为子查询只执行一次,然后交给外部查询,比如:

    SELECT *   FROM AdventureWorks.HumanResources.Employee   WHERE SickLeaveHours>ANY   SQLRESULT 

    上面的无关子查询,整个查询过程可以看作是子查询首先返回SQLResult(SQL结果集),然后交给外部查询使用,整个过程子查询只执行一次。

    而相反,作为相关子查询,子查询的执行的次数依赖于外部查询,外部查询每执行一行,子查询执行一次,比如:

    还是上面的例子:我想取得总共请病假天数大于68天的员工:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE c.ContactID=e.ContactID AND e.SickLeaveHours>68) 

    step 1:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE 1=e.ContactID AND e.SickLeaveHours>68) 

    step 2:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e/p>  WHERE 2=e.ContactID AND e.SickLeaveHours>68) 

    step n:

    SELECT [FirstName]   ,[MiddleName]   ,[LastName]   FROM [AdventureWorks].[Person].[Contact] c   WHERE EXISTS   (SELECT *   FROM [AdventureWorks].[HumanResources].[Employee] e   WHERE n=e.ContactID AND e.SickLeaveHours>68) 

    如上面代码所示。上面的相关子查询实际上会执行N次(N取决与外部查询的行数),外部查询每执行一行,都会将对应行所用的参数传到子查询中,假如子查询有对应值,则返回TRUE(既当前行被选中并在结果中显示),假如没有,则返回FALSE。然后重复执行下一行。

    子查询作为计算列使用

    当子查询作为计算列使用时,只返回单个值(Scalar) 。用在SELECT语句之后,作为计算列使用。同样分为相关子查询和无关子查询

    相关子查询的例子比如:我想取得每件产品的名称和总共的销量。

    SELECT [Name],   (SELECT COUNT(*) FROM AdventureWorks.Sales.SalesOrderDetail S   WHERE S.ProductID=P.ProductID)AS SalesAmount   FROM [AdventureWorks].[Production].[Product] P 

    部分结果如下:

    当子查询作为计算列使用时,会针对外部查询的每一行,返回唯一的值。

    同样的,SQL子查询都可以使用其他语句达到同样的效果,上面的语句和如下语句达到同样的效果:

    SELECT P.Name,COUNT(S.ProductID)   FROM [AdventureWorks].[Production].[Product] P   LEFT JOIN AdventureWorks.Sales.SalesOrderDetail S   ON S.ProductID=P.ProductID   GROUP BY P.Name 

    子查询作为计算列且作为无关子查询时使用,只会一次性返回但一值,这里就不再阐述了。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    SQL汉字转换为拼音的函数

    windows 7系统无法运行Photoshop CS3的解决方法

    巧用MySQL加密函数对Web网站敏感数据进行保护

    MySQL基础知识简介

    Windows7和WinXP下如何实现不输密码自动登录系统的设置方法介绍

    windows 7系统ip地址冲突怎么办?windows 7系统IP地址冲突问题的

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。