数据库允许null值子查询in允许重复吗

    在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题:
结果不准确
查询性能低下
    下面我们来看一下为什么尽量不使用Not In子句。
结果不准确问题
    在SQL Server中,Null值并不是一个值,而是表示特定含义,其所表示的含义是“Unknow”,可以理解为未定义或者未知,因此任何与Null值进行比对的二元操作符结果一定为Null,包括Null值本身。而在SQL Server中,Null值的含义转换为Bool类型的结果为False。让我们来看一个简单的例子,如图1所示。
图1.Null值与任何值进行对比结果都为Null
    SQL Server提供了“IS”操作符与Null值做对比,用于衡量某个值是否为Null。
    那么Not In 的问题在哪呢,如图2所示。
   
图2.Not In产生不准确的值
     在图2中,条件3不属于Not In后面列表的任意一个,该查询却不返回任何值,与预期的结果不同,那么具体原因就是Not In子句对于Null值的处理,在SQL Server中,图2中所示的Not In子句其实可以等价转换为如图3所示的查询。
图3.对于Not In子句来说,可以进行等价转换
    在图3中可以看到Not In可以转换为条件对于每个值进行不等比对,并用逻辑与连接起来,而前面提到过Null值与任意其他值做比较时,结果永远为Null,在Where条件中也就是False,因此3&&null就会导致不返回任何行,导致Not In子句产生的结果在意料之外。
    因此,Not In子句如果来自于某个表或者列表很长,其中大量值中即使存在一个Null值,也会导致最终结果不会返回任何数据。
解决办法?
    解决办法就是不使用Not In,而使用Not Exists作为替代。Exists的操作符不会返回Null,只会根据子查询中的每一行决定返回True或者False,当遇到Null值时,只会返回False,而不会由某个Null值导致整个子查询表达式为Null。对于图2中所示的查询,我们可以改写为子查询,如图4所示。
图4.Not Exists可以正确返回结果
Not In导致的查询性能低下
    前面我们可以看出,Not In的主要问题是由于对Null值的处理问题所导致,那么对Null值的处理究竟为什么会导致性能问题?让我们来看图5的示例。图5中,我们使用了Adventurework示例数据库,并为了演示目的将SalesOrderDetail表的ProductId的定义由Not Null改为Null,此时我们进行一个简单的Not In查询。如图5所示。
图5.Not In的执行计划
    在图5中,我们看到一个Row Count Spool操作符,该操作符用于确认ProductId列中是否有Null值(过程是对比总行数和非Null行数,不想等则为有Null值,虽然我们知道该列中没有Null值,但由于列定义是允许Null的,因此SQL Server必须进行额外的确认),而该操作符占用了接近一半的查询成本。因此我们对比Not Exists,如图6所示。
图6.Not In Vs Not Exists
    由图6可以看出,Not In的执行成本几乎是Not Exists的3倍,仅仅是由于SQL Server需要确认允许Null列中是否存在Null。根据图3中Not In的等价形式,我们完全可以将Not In转换为等价的Not Exist形式,如图7所示。
图7.Not In转换为Not Exists
    我们来对比图7和其等价Not In查询的成本,如图8所示。
图8.成本上完全等价
    因此我们可以看到Not In需要额外的步骤处理Null值,上述情况是仅仅在SalesOrderDetail表中的ProductId列定义为允许Null,如果我们将SalesOrderHeader的SalesOrderID列也定义为允许Null时,会发现SQL Server还需要额外的成本确认该列上是否有Null值。如图9所示。
图9.SQL Server通过加入Left Anti Semi Join操作符解决列允许Null的问题
此时Not In对应的等价Not Exist形式变为如代码清单1所示。
Sales.SalesOrderHeader a
NOT EXISTS ( SELECT *
Sales.SalesOrderDetail b
a.SalesOrderID = b.ProductID )
AND NOT EXISTS ( ( SELECT
Sales.SalesOrderDetail b
b.ProductID IS NULL
AND NOT EXISTS ( SELECT 1
Sales.SalesOrderHeader
c.SalesOrderID IS NULL )
代码清单1.当连接列两列定义都允许Null时,Not In等价的Not Exists形式
    此时我们简单对比Not In和Not Exists的IO情况,如图10所示。
图10.Not In吃掉很高的IO
    本文阐述了Not In 的实现原理以及所带来的数据不一致和性能问题,在写查询时,尽量避免使用Not In,而转换为本文提供的Not Exists等价形式,将会减少很多麻烦。
阅读(...) 评论()后使用快捷导航没有帐号?
查看: 3049|回复: 9
查询语句中exists和in子句不走索引的不解
高级会员, 积分 535, 距离下一级还需 465 积分
论坛徽章:2
有以下一个简单例子:
t1(共1000行,属于小表):
SQL& SELECT * FROM T1 where rownum&10;
& && &&&ID NAME
---------- --------
& && && &1 boobooke
& && && &2 boobooke
& && && &3 boobooke
& && && &4 boobooke
& && && &5 boobooke
& && && &6 boobooke
& && && &7 boobooke
& && && &8 boobooke
& && && &9 boobooke
9 rows selected
t2:(共1百万行,为了让其占用较大的数据块,增大后面两列的数据,使其跟索引的大小拉大)
SQL& SELECT * FROM T2 where rownum&10;
& && &&&ID NAME& &&&A& && && && && && && && && && && && && && && && && && && && && && && && && &&&B
---------- -------- ----------------------------------------------------------------------------- --------------------------------------------------
& && && &1 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && && &2 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && && &3 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1004 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1005 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1006 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1007 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1008 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
& && &1009 boobooke aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
其中ID在t1和t2中均是主键,并且ID为1,2,3的为其交集,如果用普通的查询语句连接是会走索引的:
普通连接:
SQL& set autotrace on
SQL& select t1.* from t1,t2 where t2.id=t1.
& && &&&ID NAME
---------- --------
& && && &1 boobooke
& && && &2 boobooke
& && && &3 boobooke
----------------------------------------------------------
Plan hash value:
--------------------------------------------------------------------------------
| Id&&| Operation& && && && & | Name& &&&| Rows&&| Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& && &|& && && & |&&1000 | 25000 |& & 36& &(9)| 00:00:01
|*&&1 |&&HASH JOIN& && && && &|& && && & |&&1000 | 25000 |& & 36& &(9)| 00:00:01
|& &2 |& &TABLE ACCESS FULL& &| T1& && & |&&1000 | 12000 |& &&&7& &(0)| 00:00:01
|& &3 |& &INDEX FAST FULL SCAN| IX_T2_ID | 54958 |& &697K|& & 27& &(4)| 00:00:01
--------------------------------------------------------------------------------
但问题如果用exists或用in子句来代替,执行计划就选择了全表扫描:
SQL& select * from t1 where&&exists (select 1 from t2 where t2.id=t1.id);
& && &&&ID NAME
---------- --------
& && && &1 boobooke
& && && &2 boobooke
& && && &3 boobooke
----------------------------------------------------------
Plan hash value:
-------------------------------------------------------------------------------
| Id&&| Operation& && && &&&| Name& & | Rows&&| Bytes | Cost (%CPU)| Time& &&&|
-------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& & |& && && &|&&1000 | 25000 |& &240& &(2)| 00:00:03 |
|*&&1 |&&HASH JOIN SEMI& &&&|& && && &|&&1000 | 25000 |& &240& &(2)| 00:00:03 |
|& &2 |& &TABLE ACCESS FULL | T1& && &|&&1000 | 12000 |& &&&7& &(0)| 00:00:01 |
|& &3 |& &VIEW& && && && &&&| VW_SQ_1 | 54958 |& &697K|& &232& &(2)| 00:00:03 |
|& &4 |& & TABLE ACCESS FULL| T2& && &| 54958 |& &697K|& &232& &(2)| 00:00:03 |
-------------------------------------------------------------------------------
SQL& select * from t1 where t1.id in (select id from t2 where t2.id=t1.id);
& && &&&ID NAME
---------- --------
& && && &1 boobooke
& && && &2 boobooke
& && && &3 boobooke
----------------------------------------------------------
Plan hash value:
-------------------------------------------------------------------------------
| Id&&| Operation& && && &&&| Name& & | Rows&&| Bytes | Cost (%CPU)| Time& &&&|
-------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& & |& && && &|& &&&1 |& & 38 |& &240& &(2)| 00:00:03 |
|*&&1 |&&HASH JOIN SEMI& &&&|& && && &|& &&&1 |& & 38 |& &240& &(2)| 00:00:03 |
|& &2 |& &TABLE ACCESS FULL | T1& && &|&&1000 | 12000 |& &&&7& &(0)| 00:00:01 |
|& &3 |& &VIEW& && && && &&&| VW_SQ_1 | 50116 |&&1272K|& &232& &(2)| 00:00:03 |
|& &4 |& & TABLE ACCESS FULL| T2& && &| 50116 |& &244K|& &232& &(2)| 00:00:03 |
-------------------------------------------------------------------------------
网上的查过
in操作的原理是先进性子查询操作,再进行主查询操作(适合主查询是大表,子查询是小表)。
exists操作的原理是先进行主查询操作,再到子查询中进行过滤(适合主查询是小表,子查询是大表)。
exists好像会有可能导致错误的执行计划,不知什么原理?
请大家帮忙分析一下什么原因造成IN和EXISTS不走索引?
论坛徽章:8
sys@MAA& select * from v$version where rownum &= 1;
BANNER
----------------------------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
luocs@MAA& show user
USER is &LUOCS&
luocs@MAA& select * from t1;
& && &&&ID NAME& && && && && && && && && &LOGINDATE
---------- ------------------------------ -----------------------
& && && &1 LTB& && && && && && && && && & 17-OCT-:54
& && && &2 LUOCS& && && && && && && && &&&17-OCT-:54
& && && &3 LTB& && && && && && && && && & 17-OCT-:54
& && && &4 LUOCS& && && && && && && && &&&17-OCT-:54
luocs@MAA& select * from t2;
& && &&&ID ADDRESS
---------- ------------------------------------------------------------
& && && &1 CHANGPING
& && && &2 HAIDIAN
& && && &3 CHANGPING
& && && &4 HAIDIAN
luocs@MAA& col INDEX_NAME for a15
luocs@MAA& col INDEX_TYPE for a15
luocs@MAA& col TABLE_NAME for a15
luocs@MAA& select INDEX_NAME, INDEX_TYPE, TABLE_NAME from user_
INDEX_NAME& && &INDEX_TYPE& && &TABLE_NAME
--------------- --------------- ---------------
INX_ID_T2& && & NORMAL& && && & T2
INX_T1& && && & NORMAL& && && & T1
luocs@MAA& set autot traceonly exp
luocs@MAA& select * from t1, t2 where t1.id = t2.
Execution Plan
----------------------------------------------------------
Plan hash value:
------------------------------------------------------------------------------------------
| Id&&| Operation& && && && && && &&&| Name& && &| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
------------------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& && && && & |& && && &&&|& &&&4 |& &112 |& &&&6&&(17)| 00:00:01 |
|& &1 |&&MERGE JOIN& && && && && && &|& && && &&&|& &&&4 |& &112 |& &&&6&&(17)| 00:00:01 |
|& &2 |& &TABLE ACCESS BY INDEX ROWID| T2& && &&&|& &&&4 |& & 48 |& &&&2& &(0)| 00:00:01 |
|& &3 |& & INDEX FULL SCAN& && && &&&| INX_ID_T2 |& &&&4 |& && & |& &&&1& &(0)| 00:00:01 |
|*&&4 |& &SORT JOIN& && && && && && &|& && && &&&|& &&&4 |& & 64 |& &&&4&&(25)| 00:00:01 |
|& &5 |& & TABLE ACCESS FULL& && && &| T1& && &&&|& &&&4 |& & 64 |& &&&3& &(0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
& &4 - access(&T1&.&ID&=&T2&.&ID&)
& && & filter(&T1&.&ID&=&T2&.&ID&)
luocs@MAA& select * from t1 where exists (select 1 from t2 where t1.id = t2.id);
Execution Plan
----------------------------------------------------------
Plan hash value:
--------------------------------------------------------------------------------
| Id&&| Operation& && && & | Name& && &| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
--------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& &|& && && &&&|& &&&4 |& & 76 |& &&&3& &(0)| 00:00:01 |
|& &1 |&&NESTED LOOPS SEMI |& && && &&&|& &&&4 |& & 76 |& &&&3& &(0)| 00:00:01 |
|& &2 |& &TABLE ACCESS FULL| T1& && &&&|& &&&4 |& & 64 |& &&&3& &(0)| 00:00:01 |
|*&&3 |& &INDEX RANGE SCAN | INX_ID_T2 |& &&&4 |& & 12 |& &&&0& &(0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
& &3 - access(&T1&.&ID&=&T2&.&ID&)
luocs@MAA& select * from t1 where t1.id in (select id from t2 where t1.id=t2.id);
Execution Plan
----------------------------------------------------------
Plan hash value:
--------------------------------------------------------------------------------
| Id&&| Operation& && && & | Name& && &| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
--------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& &|& && && &&&|& &&&4 |& & 76 |& &&&3& &(0)| 00:00:01 |
|& &1 |&&NESTED LOOPS SEMI |& && && &&&|& &&&4 |& & 76 |& &&&3& &(0)| 00:00:01 |
|& &2 |& &TABLE ACCESS FULL| T1& && &&&|& &&&4 |& & 64 |& &&&3& &(0)| 00:00:01 |
|*&&3 |& &INDEX RANGE SCAN | INX_ID_T2 |& &&&4 |& & 12 |& &&&0& &(0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
& &3 - access(&T1&.&ID&=&ID&)
复制代码
高级会员, 积分 535, 距离下一级还需 465 积分
论坛徽章:2
楼上的,你的测试结果为何跟我的不一样?你觉得原因在于?表统计信息分析我是已经做过的
论坛徽章:1
看SQL的执行计划,不是比较执行计划是否一样,而要看执行计划是否是最优的,如果Oracle选择了一个很差的执行计划,这个可以作为讨论的话题,至于为什么一会这个,一会那个,那是CBO计算的结果。
同样的SQL,不同的数据,很可能执行计划不同,这很正常啊。
论坛徽章:17
看SQL的执行计划,不是比较执行计划是否一样,而要看执行计划是否是最优的,如果Oracle选择了一个很差的执行 ...[/quote]
我想楼主的意思,是原本预计的查询会走索引,但是结果没有。
楼主应该是想整明白CBO的选择是否是正确的。
对于数据量很小的表,大概就能预测到什么样的查询走什么样的执行计划是最优的。
但是对于数据量很大的表,怎么快速判断CBO选择的执行计划是最优的?或者说是正确的?
论坛徽章:8
你看你的数据值,重复行多与重复行少等,都会影响执行计划。
比如:大多数为重复行:
luocs@BBK& select t1.id, t1.name from t1, t2 where t1.id = t2.
Execution Plan
----------------------------------------------------------
Plan hash value:
-----------------------------------------------------------------------------------
| Id&&| Operation& && && && & | Name& && &| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
-----------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& && &|& && && &&&|& &888M|& &&&9G|&&1025& &(1)| 00:00:13 |
|& &1 |&&NESTED LOOPS& && && &|& && && &&&|& &888M|& &&&9G|&&1025& &(1)| 00:00:13 |
|& &2 |& &INDEX FAST FULL SCAN| INX_T1& & |&&1000 |&&8000 |& &&&3& &(0)| 00:00:01 |
|*&&3 |& &INDEX RANGE SCAN& & | INX_ID_T2 |& &888K|&&3472K|& &&&2& &(0)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
& &3 - access(&T1&.&ID&=&T2&.&ID&)
大多数为不重复行:
luocs@BBK& select t1.id, t1.name from t1, t2 where t1.id = t2.
Execution Plan
----------------------------------------------------------
Plan hash value:
-----------------------------------------------------------------------------------
| Id&&| Operation& && && && & | Name& && &| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
-----------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& && &|& && && &&&|&&3559K|& & 50M|& &557& &(3)| 00:00:07 |
|*&&1 |&&HASH JOIN& && && && &|& && && &&&|&&3559K|& & 50M|& &557& &(3)| 00:00:07 |
|& &2 |& &INDEX FAST FULL SCAN| INX_T1& & |&&1000 | 11000 |& &&&3& &(0)| 00:00:01 |
|& &3 |& &INDEX FAST FULL SCAN| INX_ID_T2 |&&1000K|&&3906K|& &542& &(1)| 00:00:07 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
& &1 - access(&T1&.&ID&=&T2&.&ID&)
复制代码他们走的执行计划都不一样
高级会员, 积分 535, 距离下一级还需 465 积分
论坛徽章:2
谢谢楼上几位指示,我想应该是因为T1,T2其他字段都有重复值的原因,我改成不重复后T2就走了索引了(T1走全表正常,因为查询结果是要显示T1全部的),至于为什么有重复值,CBO选择了全表,这个还真有点想不通。
---------------------------------------------------------
lan hash value:
------------------------------------------------------------------------------
Id&&| Operation& && && & | Name& &&&| Rows&&| Bytes | Cost (%CPU)| Time& &&&|
------------------------------------------------------------------------------
& &0 | SELECT STATEMENT& &|& && && & |& & 99 |&&7821 |& &&&5& &(0)| 00:00:01 |
*&&1 |&&FILTER& && && && &|& && && & |& && & |& && & |& && && && &|& && && & |
& &2 |& &TABLE ACCESS FULL| T1& && & |& & 99 |&&7821 |& &&&2& &(0)| 00:00:01 |
*&&3 |& &INDEX RANGE SCAN | IX_T2_ID |& &100 |&&1300 |& &&&1& &(0)| 00:00:01 |
------------------------------------------------------------------------------
redicate Information (identified by operation id):
--------------------------------------------------
&&1 - filter( EXISTS (SELECT /*+ */ 0 FROM &T2& &T2& WHERE
& && && && & &T2&.&ID&=:B1))
&&3 - access(&T2&.&ID&=:B1)
高级会员, 积分 535, 距离下一级还需 465 积分
论坛徽章:2
我只要把T1的name字段数据变成重复,执行计划就错误地选择T2全表扫描了(此时T2的name字段是重复的也没问题),消耗成本明显第高于索引访问:
select * from t1 where&&exists (select 1 from t2 where t2.id=t1.id);
----------------------------------------------------------
Plan hash value:
-------------------------------------------------------------------------------
| Id&&| Operation& && && &&&| Name& & | Rows&&| Bytes | Cost (%CPU)| Time& &&&|
-------------------------------------------------------------------------------
|& &0 | SELECT STATEMENT& & |& && && &|& & 99 |&&1980 |& & 13& &(8)| 00:00:01 |
|*&&1 |&&HASH JOIN SEMI& &&&|& && && &|& & 99 |&&1980 |& & 13& &(8)| 00:00:01 |
|& &2 |& &TABLE ACCESS FULL | T1& && &|& & 99 |& &693 |& &&&2& &(0)| 00:00:01 |
|& &3 |& &VIEW& && && && &&&| VW_SQ_1 |&&9999 |& &126K|& & 10& &(0)| 00:00:01 |
|& &4 |& & TABLE ACCESS FULL| T2& && &|&&9999 | 49995 |& & 10& &(0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
这正是我不解的地方,ORACLE明显是选择了一条错误的代价更高的路吧个人认为。
论坛徽章:1
ftc007 发表于
我只要把T1的name字段数据变成重复,执行计划就错误地选择T2全表扫描了(此时T2的name字段是重复的也没问题 ...
麻烦楼主给出同样数据时,几种执行计划的资源消耗情况。
可以用hint来强制按照你的意志进行数据访问,产生几种不同的执行计划(比如强制走索引)
论坛徽章:8
建议LZ贴代码的时候使用code标签,这样看着方便请教:SQLSERVER中IN子查询能用变量么?
[问题点数:100分,结帖人tgbd]
请教:SQLSERVER中IN子查询能用变量么?
[问题点数:100分,结帖人tgbd]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2009年5月 MS-SQL Server大版内专家分月排行榜第三
2009年5月 MS-SQL Server大版内专家分月排行榜第三
2009年5月 MS-SQL Server大版内专家分月排行榜第三
2010年1月 MS-SQL Server大版内专家分月排行榜第二2009年11月 MS-SQL Server大版内专家分月排行榜第二
2012年10月 MS-SQL Server大版内专家分月排行榜第三2010年7月 MS-SQL Server大版内专家分月排行榜第三2010年3月 MS-SQL Server大版内专家分月排行榜第三2010年2月 MS-SQL Server大版内专家分月排行榜第三2009年8月 MS-SQL Server大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。关于SQL语法,将子查询结果做为值,用做主查询的条件。主要是子查询去重复的问题。_百度知道
关于SQL语法,将子查询结果做为值,用做主查询的条件。主要是子查询去重复的问题。
有A,B两张表,A为学生表,表中字段为A_ID,A_NAME,B_ID;B为选修课程表,表中字段为B_ID,B_NAME,C_ID。C_ID为年级。A与B是N:N,B与C也是N:N.查询条件为统计年级是”3“的选课学生数量。下面是我的查询语句:Select Count(Distinct A_ID) from A where B_ID in(Select Distinct B_ID from B where C_ID='3')。一个学生可选多门课程,根据B_ID查出来的COUNT(A_ID)是会有重复的,那么主查询中用Distinct 是没有问题。但是子查询中根据C_ID查询出来的B_ID是不会重复的,所以,在子查询中的Distinct是不是必要的?如果没有Distinct,那么对查询速度会不会有影响?在程序中写的SQL语句,没有报错没有异常,但是子查询不加Distinct的时候速度就非常慢,加了就正常了,请问有人知道这是为什么吗?
提问者采纳
给你说一下有distinct和无distinct的执行过程吧有distinct的时候,是Select Distinct B_ID from B wher珐揣粹废诔肚达莎惮极e C_ID='3' 这句去表里读id,最后取出不重复的,然后Select Count(Distinct A_ID) from A where B_ID in
这个查询去刚才查的结果里循环取数 但是如果=3的那个不用distinct的话,也是取出一堆数来,但是有很多应该是重复的,前一句还是要到这个结果集中去循环,这个主要看数据量吧 如果你后边括号里的数据量有几万,那就要循环几万次,这个时候后边用distinct的话比较合适如果后边不加distinct,而数据量几百几千条,循环也就是几百几千次
子查询不用distinct查出的数据和使用了distinct查出的数据是一样的呢。in 就是where 的一个循环,这个我知道。子查询查出的B_ID只有一个的时候是没有问题的,但如果查出2个不同的B_ID就会速度很慢了。会不会在内部有什么执行过程会影响到速度?
数据一样,因为distinct b_id 的时候数据是1,2,3,4,5类似这样的但不用distinct的时候,是1,1,1,2,3,4,4,5,就是可能会出现N个1,2,3,4,5所以这个查询要根据你实际情况而定,如果B表里数据量很多条,但是基本都是雷同的,可用distinct,如果基本每条都不一样的话,不建议用distinct
是查询出来的结果一样啊,就是每个B_ID都是单独存在的。不会出现N个的现象的。我把子查询单独拎出来执行的也是一样的结果。调试了半天,才知道是因为这个的原因,就是弄不明白,为什么一定要加上distinct,程序运行才会正常。而不加distinct又会很慢。
看看执行哪个地方耗费的资源多吧,没实际数据还真不太好说,顺便看看都哪些字段有索引
提问者评价
谢谢各位,有点明白了。
其他类似问题
为您推荐:
其他2条回答
子查询中的distinct是对查询速度应该是有影响的。 例如 张三和李四都选修了一门课程,且都在3年级。那么你的子查询不加distinct查出结果就是2个,加了distinct查出的B_ID就只有一个。所以如果你用子查询来查的话,建珐揣粹废诔肚达莎惮极议加上distinct。 如果可以不用子查询,建议使用如下语句Select Count(discint A.A_ID) From A, B Where A.B_ID=B.B_ID and B.C_ID='3')
表的设计就有问题,太乱
额,表设计是我后来想的,和原来的差不多。原来的是这样的:表A是客户信息表,主键客户ID,表B是客户组项目表,主键客户组ID,表C是项目表,主键项目ID。A中有客户组ID,B中有项目ID,一个客户可以属于多个客户组。然后一个项目有多个客户组。条件就是,某一个项目中包含的客户数量。可能有点难以理解。。
子查询的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁}

我要回帖

更多关于 数据库表不允许修改 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信