首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 旧帖釋疑(Order by NewID()) [已结贴,结贴人:Garnett_KG]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:02:45 楼主
    无意中翻到旧帖
    http://topic.csdn.net/t/20051227/08/4483047.html
    发现这的确是个很有趣的问题,可是遍观整帖,还是没有一个让我觉得滿意的答案,你知道吗?

    SQL code
    --- create table tb (aa int,bb char(1)) insert tb values(1,'A') insert tb values(1,'B') insert tb values(1,'C') insert tb values(1,'D') insert tb values(2,'A') insert tb values(2,'B') insert tb values(2,'C') insert tb values(2,'D') insert tb values(3,'A') insert tb values(3,'B') insert tb values(3,'C') insert tb values(3,'D') ------ --SQL1 SELECT * FROM tb a WHERE bb IN ( SELECT TOP 1 bb FROM tb WHERE aa=a.aa ORDER BY NEWID() ) --SQL2 SELECT * FROM tb a WHERE bb = ( SELECT TOP 1 bb FROM tb WHERE aa=a.aa ORDER BY NEWID() )


    注意比较SQL1跟SQL2之间的实际执行计划的差别,并注意节点之间的实际行数.
    那谁能具体解釋一下执行计划为什么会产生这样的差异?


    120  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:05:571楼 得分:0
    SQL1执行计划 & IO
    SQL code
    資料表 'tb'。掃描次數 13,邏輯讀入 13,實體讀取 0,先讀讀入 0。 Rows Executes StmtText ----------- ----------- -------------------------------------------------------------------------------------- 4 1 |--Nested Loops(Left Semi Join, OUTER REFERENCES:([a].[aa], [a].[bb])) 12 1 |--Table Scan(OBJECT:([tempdb].[dbo].[tb] AS [a])) 4 12 |--Row Count Spool 4 12 |--Filter(WHERE:([a].[bb]=[tb].[bb])) 12 12 |--Sort(TOP 1, ORDER BY:([Expr1003] ASC)) 48 12 |--Compute Scalar(DEFINE:([Expr1003]=newid())) 48 12 |--Table Scan(OBJECT:([tempdb].[dbo].[tb]), WHERE:([tb].[aa]=[a].[aa]))
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:07:292楼 得分:0
    SQL2执行计划 & IO.
    SQL code
    資料表 'tb'。掃描次數 4,邏輯讀入 4,實體讀取 0,先讀讀入 0。 Rows Executes StmtText ----------- ----------- -------------------------------------------------------------------------------------- 3 1 |--Filter(WHERE:([a].[bb]=[tb].[bb])) 12 1 |--Nested Loops(Inner Join, OUTER REFERENCES:([a].[aa])) 12 1 |--Table Scan(OBJECT:([tempdb].[dbo].[tb] AS [a])) 12 12 |--Hash Match(Cache, HASH:([a].[aa]), RESIDUAL:([a].[aa]=[a].[aa])) 3 3 |--Sort(TOP 1, ORDER BY:([Expr1004] ASC)) 12 3 |--Compute Scalar(DEFINE:([tb].[bb]=[tb].[bb], [Expr1004]=newid())) 12 3 |--Table Scan(OBJECT:([tempdb].[dbo].[tb]), WHERE:([tb].[aa]=[a].[aa]))

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:23:593楼 得分:8
    请楼主解疑!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:30:044楼 得分:8
    执行计划显示了不同的写法,对BB列过滤的执行顺序是有区别的,不明白楼主要解释的是什么?
    你认为的差异疑惑在哪?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:40:015楼 得分:0
    引用 4 楼 M1CR0S0FT 的回复:
    执行计划显示了不同的写法,对BB列过滤的执行顺序是有区别的,不明白楼主要解释的是什么?
    你认为的差异疑惑在哪?


    如果不看执行计划,你会认为它们结果会不一样吗?

    SQL1跟SQL2的区别仅仅是将 IN 改成 = 而已.

    另外,别误会,我不是来解释啥的,我所知道的也只是一点皮毛而已.

    我是希望有人来解释一下SQL1跟SQL2之间为什么会有不同的执行计划.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:49:186楼 得分:0
    引用 5 楼 Garnett_KG 的回复:
    引用 4 楼 M1CR0S0FT 的回复:
    执行计划显示了不同的写法,对BB列过滤的执行顺序是有区别的,不明白楼主要解释的是什么?
    你认为的差异疑惑在哪?


    如果不看执行计划,你会认为它们结果会不一样吗?

    SQL1跟SQL2的区别仅仅是将 IN 改成 = 而已.

    另外,别误会,我不是来解释啥的,我所知道的也只是一点皮毛而已.

    我是希望有人来解释一下SQL1跟SQL2之间为什么会有不同的执行计划.

    没太明白楼主的意思,你的意思是不是如果没有NEWID(),肯定结果是一样,为什么有了NEWID,结果就可能会不一样?
    我也不是来解释,我喜欢这样的讨论.
    稍后我也会贴个有趣的现象.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:50:537楼 得分:0
    不是

    我关注的重点是 IN 跟 = 之间的区别.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:56:298楼 得分:0
    我知道看过执行计划就很容易知道这两段sql之间为什么会造成不一样的结果

    但我是在想,为什么仅仅将SQL1 的 IN 改成 = 后,执行计划就变了呢?

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:59:059楼 得分:8
    引用 5 楼 Garnett_KG 的回复:
    引用 4 楼 M1CR0S0FT 的回复:
    执行计划显示了不同的写法,对BB列过滤的执行顺序是有区别的,不明白楼主要解释的是什么?
    你认为的差异疑惑在哪?


    如果不看执行计划,你会认为它们结果会不一样吗?

    SQL1跟SQL2的区别仅仅是将 IN 改成 = 而已.

    另外,别误会,我不是来解释啥的,我所知道的也只是一点皮毛而已.

    我是希望有人来解释一下SQL1跟SQL2之间为什么会有不同的执行计划.

    -----
    不认为查询计划不同是造成结果不同的原因。
    结果不同是因为newid是变化的

    查询计划不同是因为in和=
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 16:59:5010楼 得分:0
    楼主可以把order by newid()去掉,再看一下查询计划
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:02:5711楼 得分:0
    引用 9 楼 wangdehao 的回复:
    不认为查询计划不同是造成结果不同的原因。
    结果不同是因为newid是变化的

    查询计划不同是因为in和=



    我所指的结果不同,是指SQL1的返回的行数是不固定的,而SQL2返回的行数是固定的3行.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:04:2112楼 得分:0
    [引用 9 楼 wangdehao 的回复:]

    不认为查询计划不同是造成结果不同的原因。
    结果不同是因为newid是变化的

    查询计划不同是因为in和=

    [/Quote]

    对...是 IN 跟 =


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:04:4313楼 得分:8
    估计是IN和=的问题....
    这个问题值得深究~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:09:4814楼 得分:0
    引用 8 楼 Garnett_KG 的回复:
    我知道看过执行计划就很容易知道这两段sql之间为什么会造成不一样的结果

    但我是在想,为什么仅仅将SQL1 的 IN 改成 = 后,执行计划就变了呢?

    ......,加不加NEWID,执行计划都是那样啊,但是结果为什么不同,也因为是NEWID起的效果,至于IN和=的执行计划为什么不同
    这个应该是微软来解释吧,而且如果你认为是等价的,那么就没有存在的必要了啊,实际上处理子表有NULL的情况下,IN和=就会返回
    不同的结果,而且如果子查询里没有TOP语句,返回多个结果集,使用=还会错误.


    所以,还是不明白楼主想考虑什么,呵呵.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:10:4215楼 得分:0
    通俗的说,结果不同是因为每次生成的newid()都是不同的,即使你sql1和sql2完全一致,结果也是不同的

    查询计划只是到达同一结果的不同途径而已
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:12:4316楼 得分:8
    这个问题搂主应该先执行下这两个语句,你会发现,第一个语句可能返回多条记录,也可能两条
    而第二个语句肯定三条记录

    这会不会是SQL的bug?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:16:4917楼 得分:0
    楼主的目的就是让人给解释下sql server的思维!
    楼主当然知道查询计划不同了!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:18:1218楼 得分:0
    引用 16 楼 Haiwer 的回复:
    这个问题搂主应该先执行下这两个语句,你会发现,第一个语句可能返回多条记录,也可能两条
    而第二个语句肯定三条记录

    这会不会是SQL的bug?


    噢,我终于明白楼主的意思了,是不是这个意思?为什么第2种写法永远返回3条
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:20:0019楼 得分:0
    引用 17 楼 perfectaction 的回复:
    楼主的目的就是让人给解释下sql server的思维!
    楼主当然知道查询计划不同了!



    SQLSERVER内部执行顺序和原理,在T-SQL Querying一书里有说明,大家可以去看下.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:21:3020楼 得分:0
    .......我也理解错了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:26:3421楼 得分:0
    引用 19 楼 M1CR0S0FT 的回复:
    引用 17 楼 perfectaction 的回复:
    楼主的目的就是让人给解释下sql server的思维!
    楼主当然知道查询计划不同了!


    SQLSERVER内部执行顺序和原理,在T-SQL Querying一书里有说明,大家可以去看下.


    那本书我看了好几遍了...书本上的知识,不足以解釋得清楚这个问题.

    各位可以想一下
    SQL1跟SQL2的逻辑上意思应是等价的吧,
    但得出来的结果为什么会不一样?
    (再重申一次,我指的不一样是返回的行数不一致)

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:29:5322楼 得分:0
    引用 18 楼 M1CR0S0FT 的回复:
    引用 16 楼 Haiwer 的回复:
    这个问题搂主应该先执行下这两个语句,你会发现,第一个语句可能返回多条记录,也可能两条
    而第二个语句肯定三条记录

    这会不会是SQL的bug?


    噢,我终于明白楼主的意思了,是不是这个意思?为什么第2种写法永远返回3条


    晕菜了...
    讨论了这么久,你都没有执行过我的SQL?
    执行几次你就知道我所指的问题了。

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:34:5623楼 得分:8
    从计划中可以,

    SQL1的子查询执行了12次,应该分别是aa=1,aa=1,aa=1,aa=1,aa=2,aa=2,aa=2,aa=2,aa=2,aa=3,aa=3,aa=3,aa=3

    SQL2的子查询执行了3次,应该分别是aa=1,aa=2,aa=3,那么,其它9条记录的数从哪来呢?Hash Match


    所以SQL1的得到的记录数是不固定的,可能记录为0,也可能记录为12,因为有时它可能得不到匹配的记录
    但SQL2每次都是三条记录,因为子查询执行的结果会用四次,也就是说,总有一条记录是匹配的。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 17:39:3824楼 得分:0
    呵呵,楼上说的好.
    执行计划的解读是这样,如你所说,而且我开始也打开了SET STATISCTIS IO 验证没错.

    我的不解的地方:

    将 IN 改成 = , SQL Server会认为我的逻辑不一样了吗?

    我的子查询里都搭配上了 TOP 1 的哦.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 18:31:0325楼 得分:0
    引用 24 楼 Garnett_KG 的回复:
    呵呵,楼上说的好.
    执行计划的解读是这样,如你所说,而且我开始也打开了SET STATISCTIS IO 验证没错.

    我的不解的地方:

    将 IN 改成 = , SQL Server会认为我的逻辑不一样了吗?

    我的子查询里都搭配上了 TOP 1 的哦.


    IN和=对于SQL来说是不同的运算符呀,每种运算符都有它自己的运算方法和规则,
    就像3+2=5 , 3-2=1

    至于TOP 1,对于IN来说,这并不影响它的算法;而对于=来说,这是必然的,否则就出错了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 18:52:2226楼 得分:0
    看来sql server在 in 和 = 两个方面就是采用不同的方式处理的。
    我试了下aa为主键,或是不为主键但为聚集
    结果发现就算是查询的结果相同,查询的方式也是不同的。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 18:54:1227楼 得分:0
    那倒也是,IN 跟 = 是不同的运算,
    可是抛开执行计划不看,你会认为SQL1跟SQL2会返回不一样的行数吗?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 19:00:0928楼 得分:0
    估计是SQL Server的解析问题,
    IN 解析为 Left semi Join,
    = 解析为Inner Join.
    所以匹配就不一致了,
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-07-23 19:06:2729楼 得分:0
    引用 27 楼 Garnett_KG 的回复:
    那倒也是,IN 跟 = 是不同的运算,
    可是抛开执行计划不看,你会认为SQL1跟SQL2会返回不一样的行数吗?



    一般我们写这样的语句,都是aa值是唯一的,所以一般就认为两种写法是相同的。
    我们如果单看这个查询:

    SELECT * FROM tb a
    WHERE  bb in
        (
        SELECT TOP 1 bb FROM tb
        WHERE aa=a.aa
        ORDER BY NEWID()
        )
    其实想想,它本来就应该有两种表达意思。但对于sql server来讲,只能取一种,另一种就只能用其它的办法,如=更能表达这个意思了。

    修改 删除 举报 引用 回复