`
baby2080
  • 浏览: 4465 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

一段纠结sql

阅读更多
从表中选取不在另一张表中的记录的方法(个人总结)
http://bbs.chinaunix.net/viewthread.php?tid=821114
我转的,原来的地址,写上来,呵呵
前2天把那本sql cookbook拿出阿里复习了下,发现还有很多很多东西没能明白,这就是其中之一了。貌似这个解释的要好多了,书上好像有错。
从表中选取不在另一张表中的记录的方法

    假设我们想从A表中选择一些记录,记录中的部分字段的取值是B表
所不存在的,这里定义A表为源表,B表为参照表。例如,常见的例子
部门表(Department)作为源表,员工表(Employee)作为参照表,可以
从部门表中列举出那些员工表中所不包含的部门id来,即找出那些没有
员工的部门。
    一般类来说,如果数据库支持集合操作符,就可以直接查询,如果
没有,就使用子查询。
(1)
DB2 and PostgreSQL:
select deptno from dept
except
select deptno from emp
(2)
Oracle and Teradata:
select deptno from dept
minus
select deptno from emp
(3)
MySQL and SQL Server:
select deptno
  from dept
where deptno not in (select deptno from emp)

    对于(1)和(2)没有什么多说的,就是需要注意满足集合操作符使用的一般
条件就可以了。而且返回的结果是不重复的(duplicate removed)。所以对于3
使用者如果对结果重复性有要求的话,就需要自己根据情况添加是否使用
Distinct关键词。

    另外一个很显著的区别在于参照表中进行比较的字段是否包含NULL。这个
问题对于集合操作符来说没有任何影响,DBMS本身实现操作符的时候已经解决了。
而使用not in的子查询方式就需要采取处理措施。
    例如:源表dept中deptno取值有10,20,30,40
          参照表new_dept中deptno取值有10,50,null
         
SQL A:
     select a.deptno from dept a where a.deptno
         not in (select deptno from new_dept)
    虽然源表中20,30,40这三个deptno都不存在于参照表中,但是上面这个SQL
的查询结果为空,一条记录也没有。
而想要得到需要的结果需要写成:SQL B
     select a.deptno from dept a where a.deptno
         not in (select deptno from new_dept where deptno is not null)
查询结果为 20,30,40三条记录

SQL A之所以没有任何结果的原因在于NULL, NOT,和OR的几个操作符的综合作用
首先IN操作符等价多个OR的并列,所以SQL A实际上等价为
     A-1:
          select a.deptno from dept a where a.deptno
         not in(10,50,null)
     A-2:
          select deptno from dept where
         not  (deptno=10 or deptno=50 or deptno=null)
下面要注意,必须明确几个操作符之间的结果:
  TRUE or NULL   结果是TRUE
  FALSE or NULL  结果是NULL
 
NOT的运算结果
   bool表达式     NOT 运算结果
     TURE           FALSE
     FALSE           TRUE
     NULL            NULL
尤其注意NOT对未知的字段运算结果还是NULL,这一点一定要和
IS NULL 和IS NOT NULL这个关键词组合区分开。

最后任何字段和NULL进行运算=,+,-,...结果都是NULL
这也是SQL里面强调用IS NULL和IS NOT NULL来区分字段是否为空
而不是用=来区分

明确了上面运算符的运算规则,我们来看
not  (deptno=10 or deptno=50 or deptno=null)
当源表中deptno为10时,则
not ( TRUE or FALSE or NULL )
结果是 not (TRUE) -> FALSE  这条记录不会显示
当源表中dept为20时, 则
not ( FALSE or FALSE or NULL )
结果是 not (NULL) -> NULL  
where的搜索条件是空,也不会有任何结果
(你可以尝试select * from dept where deptno=null这种sql看看结果)

综上,无论源表的记录取何值,最终都得不到查询结果。。。

如果想要得到结果,必须写成SQL B的格式。

如果觉得始终要为总要当心字段是否为空,而不得不在SQL中注意比较麻烦的情况下
(比如在复杂的查询中,源表和参照表之间需要比较的字段比较多,每个字段都要
考虑NOT NULL,并且书写出来,确实比较繁琐),可以使用相关子查询
(correlated subquery)来巧妙的避免这个干扰

SQL C:
        select *
          from dept a
         where not exists (select null from new_dept b where b.deptno =a.deptno )
这个查询同样能够得到需要的结果

Step 1:   子查询中执行b.deptno =a.deptno 两个表join操作
Step 2:   如果子查询返回结果,即子查询包含行,则exists(subquery)返回TRUE,
          而not exists(subquery)返回FALSE,最终查询抛弃当前记录
Step 2:   如果子查询没有结果,即子查询不包含任何行,则exists(subquery)返回FALSE,
          而not exists(subquery)返回TRUE,最终查询返回当前记录
         
这里需要注意的是在使用exists情况下,其子查询中的select部分并不重要。
因为exists的含义是exists(subquery),只要子查询中包含行,无论是什么样的行记录,
结果就是TURE而SQL C中的subquery 中只要b.deptno和a.deptno相等,就会有行记录,
而select null from new_dept b where b.deptno =a.deptno 只不过是说两个deptno相等
返回的行记录是null(null也是一条行记录啊),所以exists(select null)也是TURE
因此,这种写法就保证了,只要源表中的deptno记录不存在在参考表中,就能够得到结果了。

子查询中的select对结果不会有任何影响,所以写成select null好像是一种惯例,
我觉得是为了强调相关查询的等价条件而已,但这种符合英语国家的惯例对很多
初次遇到这种写法的人来说,反而被迷惑了一把。。。

只要把握了几个关键的操作符的运算规则,就能够理解这些不同写法所造成的不同结果了。
希望上面的总结能够对大家理解这种查询方式有所帮助。


至于效率方面,exists是否快于in操作,个人持保留意见,这主要看具体的SQL书写条件
和DBMS系统的优化代码。

但是上面的语句SQL C比SQL B的执行效率要高,这一点是可以解释的。
SQL B使用in 模式,这样实际上是全表扫描,没有任何优化手段,两个表都在遍历全表
而SQL C使用相关查询,两个表是内联操作,大多数数据库系统都会使用Hash Join来
进行优化查询,所以效率会比较高。


总结了这么多,但很多时候由于不同的数据库平台对同样的SQL语句都有不同的优化策略,
所以大家可以把SQL语句用目标平台的执行计划工具查看一下就可以知道大概的执行流程
对理解SQL也有很好的帮助。

有什么问题也希望大家一起讨论。呵呵,希望我的总结没有把大家绕晕....

现在的数据库技术发展的也很迅速,可能有更好的办法来实现这种查询方式,本人能力
有限,所以有更好的方法,或者不同的见解,欢迎一起讨论

flycoco @ 2006-08-31
分享到:
评论

相关推荐

    最近一段时间关于sql一段时间

    有关学习的踪迹有关学习的踪迹有关学习的踪迹有关学习的踪迹 关于sql一段时间的总结与学习关于sql一段时间的总结与 学习关于sql一段时间的总结与学习关于sql一段时间的总 结与学习关于sql一段时间的总结与学习

    sql获取上一周时间段

    ### SQL 获取上一周的时间段 在SQL Server中,获取上一周的时间段对于数据分析、报表制作等应用场景非常有用。本文将详细介绍如何使用SQL Server中的函数来实现这一功能,并提供实际的SQL语句示例。 #### 1. 获取...

    Oracle–查询时间段内执行的sql、Produce

    1.查询时间段内执行的sql、Produce select * from v$sqlarea a where 1=1 and a.LAST_ACTIVE_TIME >= to_date( '2013-02-21 18:23:00','yyyy-MM-dd HH24:mi:ss') and a.LAST_ACTIVE_TIME < to_date( '2013-...

    SQL 语法 SQL 总结 SQL教程

    SQL 基础 SQL 首页 SQL 简介 SQL 语法 SQL select SQL distinct SQL where SQL AND & OR SQL Order By SQL insert SQL update SQL delete SQL 高级 SQL Top SQL Like SQL 通配符 SQL In SQL Between ...

    C# 在sql server数据时间段内,间隔时间查询数据方法

    在sql server 数据库中,在一段时间范围内,间隔几条数据取出一条信息的检索方法。如:时间段内“2020-10-10 00:00:00” 到“2020-10-11 00:00:00”内,间隔30秒取出一套数据方法

    一个简单的sql循环语句脚本

    SQL循环语句主要包括WHILE循环和FOR循环,它们允许我们在满足特定条件时重复执行一段代码块。在PL/SQL和T-SQL中,还有BEGIN-END结构来定义代码块。 1. WHILE循环:在满足特定条件时反复执行代码块,直到条件不再...

    一段新写的SQL

    一段新写的SQL

    sql server 导入超大SQL脚本文件

    SQL Server 是一种关系型数据库管理系统,广泛应用于各种行业。然而,在实际应用中,我们经常会遇到导入超大 SQL 脚本文件的问题。本文将介绍如何使用 osql 工具来导入超大 SQL 脚本文件。 什么是 osql? osql 是 ...

    sqlserver自动生成sql语句工具sqlserver转oracle

    市场上存在许多这样的工具,如SSMA(SQL Server Migration Assistant) for Oracle,这是一个官方提供的迁移工具,能自动分析SQL Server的架构,并生成适合Oracle的DDL(Data Definition Language)和DML(Data ...

    SQLPrompt for SQLServer2016 智能提示插件 SQL2016 提示

    SQL Prompt 是一款拥有SQL智能提示功能的SQL Server和VS插件。SQL Prompt能根据数据库的对象名称,语法和用户编写的代码片段自动进行检索,智能的为用户提供唯一合适的代码选择。自动脚本设置为用户提供了简单的代码...

    SqlParser C++实现的SQL语法解释器

    SqlParser是一款基于C++实现的SQL语法解释器,它的主要任务是解析SQL语句,将其转化为计算机可理解的形式,从而能够执行相应的数据库操作。在数据库系统中,SQL(Structured Query Language)是用于管理关系数据库的...

    SQLTracker,抓取sql语句的工具

    SQLTracker是一款专为数据库操作监控设计的工具,它在IT领域中主要用于跟踪和记录SQL语句的执行情况。SQL(Structured Query Language)是用于管理关系数据库的编程语言,包括查询、更新、插入和删除数据等操作。SQL...

    SQLMonitor oracle跟踪SQL工具

    针对Oracle数据库,有一款名为SQLMonitor的工具,它专为跟踪和监控SQL语句而设计,帮助开发者和DBA们找出程序操作的数据表,从而提升程序性能并进行精准的问题定位。本文将深入探讨SQLMonitor的功能、使用方法及其在...

    一款免安装sqlserver客户端管理工具,好用

    标题中的“一款免安装sqlserver客户端管理工具,好用”指的是SqlDbxPersonal,这是一款专为SQL Server设计的轻量级、无需安装的数据库管理工具。它允许用户方便地进行SQL查询、数据库管理和开发工作,而无需经历传统...

    SQLPrompt5.3破解

    本人在Windows7 64位+SQL Server 2012环境下测试通过(系统是全新安装) 使用方法: 1,安装SQLPrompt v5.3,这个不多说。 2,安装完毕后,断开网络连接。 3,打开Visual Studio或者SQL Server Management Studio(版本...

    kettle链接SQL server驱动 sqljdbc

    SQL Server是微软公司推出的一款关系型数据库管理系统,广泛应用于企业级数据存储与管理。在Kettle中配置SQL Server数据库连接时,我们需要依赖特定的数据库驱动,这就是SQL JDBC驱动。 SQL JDBC驱动是微软提供的...

    Oracle 11g SQL和PL SQL从入门到精通 pdf格式电子书 下载(一)

    Oracle 11g SQL和PL SQL从入门到精通 pdf格式电子书 下载(一) http://download.csdn.net/source/3268267 Oracle 11g SQL和PL SQL从入门到精通 pdf格式电子书 下载(二) ...

    sqlserver驱动包:sqljdbc4.jar

    在这个特定的场景中,`sqljdbc4.jar`是一个特定于SQL Server的实现,它包含了必要的类和方法,让Java程序能够连接到SQL Server数据库,执行SQL查询,处理结果集,并进行其他数据库操作。 首先,让我们深入了解`sql...

    SqlServer2005 打开 SqlServer2008 mdf文件

    1. 右击 Sql Server 2008 中的 jmmaj 数据库,选择“任务”->“生成脚本”,勾选“为所选数据库中的所有对象编写脚本”,然后选择“为服务器版本编写脚本”,最后选择 Sql Server 2005,并将脚本保存到一个.sql 脚本...

    Microsoft SQL Server Native Client (SQL Native Client)

    Microsoft SQL Server Native Client (SQL Native Client) 是一个同时包含 SQL OLE DB 访问接口和 SQL ODBC 驱动程序的动态链接库 (DLL)。它对使用本机代码 API(ODBC、OLE DB 和 ADO)连接到 Microsoft SQL Server ...

Global site tag (gtag.js) - Google Analytics