Top应用选项及SQL执行顺序

摘要:
小结:事实上,在使用顶级查询时,如果不使用orderby子句,那么查询结果也是不确定的。它返回SQL Server物理访问的第一行。如果您想在使用top时获得正确的结果,请确保使用orderby子句。感谢您谈论SQL数据库中的“简单”SELECTTOP,其中可能有您从未注意到的细节。这篇文章让我意识到,表面上看起来简单的事情实际上并不简单。关于CSDN网站子查询执行顺序的文章也被许多学生讨论过
写在前面的话

我们常常使用top来查询前几条语句,或使用嵌套的top的方式获取分页的数据,那么我们对top及SQL执行的顺序真正了解吗.

具体如下例.

实例

建表toptest,表结构如下:

类型
sidint
snamenvarchar(50)

建表语句如下:

create table toptest
(
    sid int,
    sname nvarchar(50)
)

表内数据如下:

sidsname
1张三
2李四
3王五
4赵六
5刘七
6宋八

插入语句如下:

insert into toptest values (1,'张三')

insert into toptest values (2,'李四')

insert into toptest values (3,'王五')

insert into toptest values (4,'赵六')

insert into toptest values (5,'刘七')

insert into toptest values (6,'宋八')

现在我们想要获取第三条、第四条数据,执行下面的语句:

select top 2 * from (select top 4 * from toptest) t order by sid desc

能够得到我们需要的结果呢?

答案是:不能.

返回的是:

  6    宋八
  5    刘七

为什么会出现这样的结果呢?

那么我们来看看执行(select top 4 * from toptest) 返回的是什么结果呢?

结果是:

  1    张三
  2    李四
  3    王五
  4    赵六

那么为什么从表t中返回的就不是

  3    王五
  4    赵六

了呢?

这里我们可以在SQLServer里查看执行计划,执行SQL语句,在执行计划标签中得到下图:

image

我们再来执行下列语句:

set showplan_all on

然后在执行SQL语句,在结果标签中,得到如下结果:

|--select top 2 sid,sname from (select top 4 sid,sname from toptest ) t order by sid desc;
    |--Top(TOP EXPRESSION:((2)))
         |--Sort(TOP 4, ORDER BY:([SQLTest].[dbo].[toptest].[sid] DESC))
              |--Table Scan(OBJECT:([SQLTest].[dbo].[toptest]))

此处,我们看到执行步骤如下:

1.表扫描

2.先按照sid进行降序排序,然后再选取top 4,

此处为何先执行排序再进行top操作请看下面的SQL逻辑处理步骤

SQL逻辑处理步骤:

(8)  SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1)  FROM <left_table>
(3)    <join_type> JOIN <right_table>
(2)      ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

3.选取top 2 sid,sname

4.获取整个结果集

由此,我们可以看到 order by sid desc 虽然是写在外部的,但是却在子查询时已经被执行了.所以,在第二步时,实际上获取了:

  6    宋八
  5    刘七
  4    赵六
  3    王五

然后,再执行第四步的top 2,即获取了:

  6    宋八
  5    刘七

现在,我们来执行下列语句,看执行计划和执行结果是什么?

select top 2 sid,sname from (select top 4 sid,sname from toptest order by sid asc) t order by sid desc;

注意,此处的子查询中加入了order by sid asc

image

|--select top 2 sid,sname from (select top 4 sid,sname from toptest order by sid asc) t order by sid desc;
    |--Sort(TOP 2, ORDER BY:([SQLTest].[dbo].[toptest].[sid] DESC))
         |--Sort(TOP 4, ORDER BY:([SQLTest].[dbo].[toptest].[sid] ASC))
              |--Table Scan(OBJECT:([SQLTest].[dbo].[toptest]))

至此,我们得到了需要的数据.

由此,我们也想到了公用表表达式(CTE),CTE中如何实现该功能呢?

with t
as
(
    select top 4 sid,sname 
    from toptest 
    order by sid asc
)
select top 2 sid,sname
from t 
order by sid desc

执行计划图:

image

执行计划:

|--with t as (select top 4 sid,sname from toptest order by sid asc) select top 2 sid,sname from t order by sid desc;
    |--Sort(TOP 2, ORDER BY:([SQLTest].[dbo].[toptest].[sid] DESC))
         |--Sort(TOP 4, ORDER BY:([SQLTest].[dbo].[toptest].[sid] ASC))
              |--Table Scan(OBJECT:([SQLTest].[dbo].[toptest]))

结果:

  4    赵六
  3    王五

其实,上述错误的查询中之所以会被认为是正确的,那是因为我们在默认的情况下已经给表中的数据按照sid进行排序了,可实际上:

select top 4 sid,sname 
from toptest 

默认是按照sid来进行排序的吗?

现在我们来执行下列语句:

insert into toptest 
values (0,'玲玲')

update toptest
set sid=1000
where sid=2

此时,数据库中的数据发生了变化,具体如下:

sidsname
1张三
1000李四
3王五
4赵六
5刘七
6宋八
0玲玲

此时,我们来执行

select top 4 sid,sname from toptest 

会是什么结果呢?返回的会是:

  0    玲玲
  1    张三
  3    王五
  4    赵六

吗?

各位同学,如果你看到此处,亲手执行一下,看是何结果呢?

此时,如果再执行

select top 2 * from (select top 4 * from toptest) t order by sid desc

得到的又是什么结果呢?

小结:

实际上,在使用top查询时,如果不使用order by子句,那么其查询结果也是不确定.它返回的是SQLServer物理上最先访问到的行.

如果想要在使用top时得到正确的那么,那么请务必使用order by子句.

鸣谢

追索来谈谈SQL数据库中"简单的"SELECT TOP—可能有你从未注意到的细节一文使我意识到表面上看起来简单的实际上却并不简单.

CSDN网站的子查询执行的顺序问题一文也有许多同学进行了讨论.

免责声明:文章转载自《Top应用选项及SQL执行顺序》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇在centos7中安装groovy,运行groovy命令报错:java.lang.SecurityException: Prohibited package name: java.lang判断QQ,微信,QQ浏览器下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#学习笔记(6)

在我们编写一个带有存储数据的功能的程序的时候,总免不了要与数据库打交道,于是LINQ(语言集成查询)就应运而生了。LINQ是一组C#和VB的语言拓展,它允许编写C#或者VB代码以查询数据库相同的方式操作内存数据。 与一般的SQL语言不同的,用户在使用LINQ语言的时候可以自定义数据类型,这样就为程序员们提供了很大的便利,LINQ中包含40多个查询操作,其基...

centos7查看CPU的利用率

  top 的全屏对话模式可分为3部分:系统信息栏、命令输入栏、进程列表栏。 第一部分 — 最上部的 系统信息栏 : 第一行(top):“14:23:28”为系统当前时刻;6:17”为系统启动后到现在的运作时间;“3user”为当前登录到系统的用户,更确切的说是登录到用户的终端数 —同一个用户同一时间对系统多个终端的连接将被视为多个用户连接到系统,这里的...

微信小程序页面导航功能

页面导航功能无论是在app和web中都是一个极其常见的功能,如首字母导航,tabs导航等等。但是由于微信小程序无法都dom节点进行操作,所以怎么才能在小程序中快速的导航到用户的想要到达的地方呢。 那么下面我们就以A-Z的首字母导航来讲解一下吧! Step1 首先,当然我们需要简单写出我们的页面,右侧为我们A-Z的导航栏,中间显示主要的内容,当然我们这里也简...

TOP 子句用于规定要返回的记录的数目。

TOP 子句用于规定要返回的记录的数目。 1、SQL server的语法: SELECT TOP number|percent column_name(s) FROM table_name; 例子:从表persons中选取前2行的数据; SELECT TOP 2 * FROM persons; 从表persons中去前50%的 数据: SELECT TOP...

Jquery中用offset().top和offsetTop的比较

今天,想测试一个div与顶部的距离,用的是.offsetTop,但是offsetTop获得的值,怎么都打印不出来。折腾了半天,打印的结果都是undefined,虽然网上很多资料都说返回的是数值。虽然这个函数永不了,但是黄显钦找到了一个可以替代offsetTop的函数。那就是jquery的offset().top   我们先来了解一下,什么是offset(...

在Linux上显示某个进程的线程的几种方式

方法一:PS 在ps命令中,“-T”选项可以开启线程查看。下面的命令列出了由进程号为的进程创建的所有线程。 1.$ ps -T -p 方法二: Top top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。 1.$...