left join on用法_ORDER BY的用法

小伙伴们在进行SQL排序时,都能很自然的使用到ORDER BY。不管是默认ASC的升序,还是DESC降序,几乎都是信手拈来。

今天给大家分享一些你可能不知道的ORDER BY用法。

一、ORDER BY返回的是游标而不是集合

SQL的理论其实是集合论,常见的类似求数据的交集、并集、差集都可以使用集合的思维来求解。

集合中的行之间没有预先定义的顺序,它只是成员的一种逻辑组合,成员之间的顺序无关紧要。

如下图,每一个括号里的内容就是一条记录,在没排序前,他们都是随机分布在集合中。

Student(ID,Name,Age)

e14fc81b050c3b5f9fc03fded8d59259.png

Student集合

但是对于带有排序作用的ORDER BY子句的查询,它返回的是一个对象,其中的行按特定的顺序组织在一起,我们把这种对象称为游标。

如下图,经过对Student表的ID进行ORDER BY排序后,Student表变成了有序对象,也就是我们上面说的游标。

Student(ID,Name,Age)

ffdee84bb0828eb920871289c4fa1055.png

Student对象

二、ORDER BY子句是唯一能重用列别名的一步

注:markdown对代码块的语法是开始和结束行都要添加:```,其中 ` 为windows键盘左上角那

这里涉及SQL语句的语法顺序和执行顺序了,我们常见的SQL语法顺序如下:

SELECT DISTINCT FROM [left_table] JOIN ON WHERE GROUP BY WITH HAVING ORDER BY

而数据库引擎在执行SQL语句并不是从SELECT开始执行,而是从FROM开始,具体执行顺序如下(关键字前面的数字代表SQL执行的顺序步骤):

(8)SELECT (9)DISTINCT (11)
(1)FROM [left_table]
(3) JOIN
(2) ON
(4)WHERE
(5)GROUP BY
(6)WITH
(7)HAVING
(10)ORDER BY

从上面可以看到SELECT在HAVING后才开始执行,这个时候SELECT后面列的别名只对后续的步骤生效,而对SELECT前面的步骤是无效的。所以如果你在WHERE,GROUP BY,或HAVING后面使用列的别名均会报错。

我们举例测试一下。

示例表Customers结构及数据如下:

18f4260a4dfcce79957ebe225c930c18.png

1、WHERE后面不使用别名的情况

SELECT 姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersWHERE 城市='广州'

结果如下:

3eb92e16cc5d783d4c36e2add420a866.png

2、WHERE后面使用列别名的情况

SELECT 姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersWHERE City='广州'

执行结果如下:

aa66736d88d43954cbc4c1be6b46da59.png

从返回的消息中我们可以看到,重命名后的City并不能被WHERE识别,所以才会报“列名'City'无效”的提示。

其他关键字大家也可以使用上述方法进行测试,下面我们测试GROUP BY和HAVING后面使用列别名的情况。

3、测试GROUP BY后使用列别名

SELECT 城市 AS CityFROM CustomersGROUP BY City

结果如下:

cef1005389ea3ee6b25bf4d067e439c3.png

4、测试HAVING后使用列别名

SELECT 城市 AS CityFROM CustomersGROUP BY 城市HAVING COUNT(City)>1

结果如下:

6fb5ff3bfd296313b5bdc94380fa9d53.png

5、测试ORDER BY后面使用列别名

SELECT 姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersORDER BY City

结果如下:

4e34d3eac39277b979a976ebc47808b0.png

从上面的几个测试示例的结果中,可以得出我们的结论是正确的:ORDER BY子句是唯一能重用列别名的一步。

三、谨慎使用ORDER BY 后面接数字的方式来进行排序

有些小伙伴为了图省事,喜欢在ORDER BY后面写数字,具体示例如下:

SELECT 姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersORDER BY 1,2,3

结果如下:

40619741759dc452ca638eab85b11260.png

这样写的结果,针对当前的查询是正确没有问题的,ORDER BY后面的数字1,2,3分别代表SELECT后面的第1,第2,第3个字段(也就是Name,Address,City)。

可是当查询的列发生改变,忘了修改ORDER BY列表。特别是当查询语句很长时,要找到ORDER BY与SELECT列表中的哪个列相对应会非常困难。

例如

SELECT 客户ID AS ID,姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersORDER BY 1,2,3

由于增加了一列“客户ID”,原本的题意还是对Name,Address,City排序,但是因为使用了ORDER BY加数字,排序后的结果如下:

ee0b2513ce738907e159ad71f81d1006.png

得到的结果并不是我们想要的,所以请慎用ORDER BY加数字,尽量使用ORDER BY加列名或列别名

四、表表达式不能使用ORDER BY排序

表表达式包括视图,内联表值函数,派生表(子查询)和公用表表达式(CTE)。

例如下面的视图是无效的

CREATE VIEW V_Customers ASSELECT 客户ID AS ID,姓名 AS Name,地址 AS Address,城市 AS CityFROM CustomersORDER BY ID,Name,Address

结果如下:

7e0e3b13426122ba9c25c97608791a9b.png

这个错误是不是很熟悉?因为很多小伙伴经常喜欢在视图或子查询里面加ORDER BY,然后一执行就会报这个错。

根本原因不敢妄加断定,因为搜寻了很多文献资料也没给出一个具体的说法。

这里我猜测是因为视图,内联表值函数,派生表(子查询)和公用表表达式(CTE)等返回的结果还需要进一步的去使用,加了ORDER BY进行排序是多此一举,反而会浪费系统资源。所以数据库的开发者不希望大家使用这样不规范操作。

所以下次就不要在表表达式里添加ORDER BY了。

五、T-SQL中表表达式加了TOP可以使用ORDER BY

我们从第四点的报错信息中可以看到:在另外还指定了 TOP、OFFSET 或 FOR XML是可以使用ORDER BY的。

e97d481ebd66ea08bd9dc8502f0e3d6e.png

这又是为什么呢?

我们还是先举个栗子给大家看一下

SELECT 客户ID AS ID,姓名 AS Name,地址 AS Address,城市 AS CityFROM(SELECT TOP 3 *FROM CustomersORDER BY 城市) CustomersORDER BY ID,Name,Address

结果如下:

3a30209b6c13c351f8ab53fef341ca47.png

因为T-SQL中带有ORDER BY的表表达式加了TOP后返回的是一个没有固定顺序的表。因此,在这种情况下,ORDER BY子句只是为TOP选项定义逻辑顺序,就是下面这个逻辑子句

SELECT TOP 3 *FROM CustomersORDER BY 城市

结果如下:

d1c2a25524ba496443d3e2ee98b63256.png

而不保证结果集的排列顺序,因为表表达式外面至少还有一层才是我们最终需要的结果集。

这里的ORDER BY只对当前的子查询生效,到了主查询是不起作用的。必须在主查询末尾继续添加一个ORDER BY子句才能对结果集生效,就像我们例子中写的那样。

除非逻辑要求,一般情况下并不推荐大家这样巧妙的避开子查询中不能使用ORDER BY的限制

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以尝试以下优化: 1. 对于子查询中的表,添加合适的索引以提高查询效率; 2. 可以考虑将子查询改为临时表,避免多次重复查询; 3. 在需要排序的字段上添加索引; 4. 尽量避免在 JOIN 条件中使用函数,可以将日期比较条件提取到 WHERE 子句中。 优化后的 SQL 如下: ``` CREATE TEMPORARY TABLE tmp_street AS SELECT STREET AS quyu FROM STREET_YXW WHERE 1=1 GROUP BY STREET; CREATE TEMPORARY TABLE tmp_lastNum AS SELECT T2.STREET_NAME AS quyu, COUNT(*) AS lastNum FROM V_FIRESAFETYREGISTER_DYC T1 LEFT JOIN V_HSE_COMB_BUILDING_INFO_PA T2 ON LEFT(T1.HOUSEID, 19) = T2.BUILDING_CODE WHERE T1.LASTLOGOUTTIME IS NOT NULL AND date_format(T1.LASTLOGOUTTIME,'%Y') = ? GROUP BY T2.STREET_NAME; CREATE TEMPORARY TABLE tmp_jdNum AS SELECT T3.STREET_NAME AS quyu, COUNT(DISTINCT T1.FIREID) AS jdNum FROM V_SX_FIRESAFETYPLAN T1 LEFT JOIN V_FIRESAFETYREGISTER_DYC T2 ON T1.FIREID = T2.ID LEFT JOIN V_HSE_COMB_BUILDING_INFO_PA T3 ON LEFT(T2.HOUSEID, 19) = T3.BUILDING_CODE WHERE T1.DATE_NEW IS NOT NULL AND date_format(T1.DATE_NEW,'%Y') = ? GROUP BY T3.STREET_NAME; CREATE TEMPORARY TABLE tmp_xcNum AS SELECT T3.STREET_NAME AS quyu, COUNT(*) AS xcNum FROM V_SX_FIRESAFETYTRAINING T1 LEFT JOIN V_FIRESAFETYREGISTER_DYC T2 ON T1.FIREID = T2.ID LEFT JOIN V_HSE_COMB_BUILDING_INFO_PA T3 ON LEFT(T2.HOUSEID, 19) = T3.BUILDING_CODE WHERE T1.TRAININGTIME IS NOT NULL AND date_format(T1.TRAININGTIME,'%Y') = ? GROUP BY T3.STREET_NAME; SELECT T1.quyu, T2.lastNum, T3.jdNum, T4.xcNum FROM tmp_street T1 LEFT JOIN tmp_lastNum T2 ON T1.quyu = T2.quyu LEFT JOIN tmp_jdNum T3 ON T1.quyu = T3.quyu LEFT JOIN tmp_xcNum T4 ON T1.quyu = T4.quyu ORDER BY T2.lastNum LIMIT ?,?; ``` 请注意,这只是一种可能的优化方案,具体的优化方法还要根据数据库的实际情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值