T-SQL 排序规则的优先顺序

排序规则的优先顺序

排序规则的优先顺序(也称为排序规则类型强制规则)是赋予一组规则的术语,而这组规则决定:

  1. 取值为字符串的表达式的最终结果的排序规则。

  2. 区分排序规则的运算符所使用的排序规则,这些运算符使用字符串输入但不返回字符串,如 LIKE 和 IN。

排序规则的优先顺序规则只适用于字符串数据类型:charvarchartextncharnvarcharntext。具有其它数据类型的对象不参加排序规则的评估。

所有对象的排序规则可归为四类中的一种。每类的名称叫做排序规则标志。

排序规则标志对象类型
强制默认任何 Transact-SQL 字符串变量、参数、字面值、目录内置函数的输出、或不使用字符串输入但产生字符串输出的内置函数。

如果在用户定义函数、存储过程或触发器中声明某个对象,则将该对象指派为在其中创建函数、存储过程或触发器的数据库的默认排序规则。如果在批处理中声明该对象,则将其指派为连接所使用的当前数据库的默认排序规则。

隐性 X列引用。从为表或视图中的列定义的排序规则得到表达式(由 X 表示)的排序规则。

即使 CREATE TABLE 或 CREATE VIEW 语句中的 COLLATE 子句给列显式指派了排序规则,该列引用仍归为隐性。

显式 X使用表达式中的 COLLATE 子句显式投影到特定排序规则(由 X 表示)的表达式。
无排序规则表示表达式的值是两个字符串之间操作的结果,而这两个字符串具有隐性排序规则标志的冲突排序规则。表达式的结果被定义为不具有排序规则。

只引用一个字符串对象的简单表达式的排序规则标志是被引用对象的排序规则标志。

如果复杂表达式所引用的两个操作数表达式的排序规则标志相同,则该复杂表达式的排序规则标志为数表达式的排序规则标志。

如果复杂表达式所引用的两个操作数表达式的排序规则不同,则该复杂表达式最终结果的排序规则标志基于下列规则:

  1. 显式优先于隐性。隐性优先于强制默认。换言之,

    显式 > 隐性 > 强制默认

  2. 组合两个已指派给不同排序规则的显式表达式将生成错误。

    显式 X + 显式 Y = 错误

  3. 组合两个具有不同排序规则的隐性表达式将产生无排序规则。

    隐性 X + 隐性 Y = 无排序规则

  4. 将无排序规则的表达式与任何标志的表达式组合,除了显式排序规则外(参见下一项),都产生无排序规则标志的结果。

    无排序规则 + 任何标志 = 无排序规则

  5. 将无排序规则的表达式与显式排序规则的表达式组合将产生具有显式标志的表达式。

    无排序规则 + 显式 X = 显式

下面的示例说明上述规则。

USE tempdb
GO

CREATE TABLE TestTab (
   id int, 
   GreekCol nvarchar(10) collate greek_ci_as, 
   LatinCol nvarchar(10) collate latin1_general_cs_as
   )
INSERT TestTab VALUES (1, N'A', N'a')
GO

下面查询中的谓词具有排序规则冲突,因此产生错误:

SELECT * 
FROM TestTab 
WHERE GreekCol = LatinCol

下面是结果集。

Msg 446, Level 16, State 9, Server CTSSERV, Line 1
Cannot resolve collation conflict for equal to operation.

下面查询中的谓词在排序规则 greek_ci_as 中取值,因为右边表达式具有显式标志,优先于右边表达式的隐性标志:

SELECT * 
FROM TestTab 
WHERE GreekCol = LatinCol COLLATE greek_ci_as

下面是结果集。

id          GreekCol             LatinCol
 ----------- -------------------- --------------------
           1 a                    A

(1 row affected)

下面查询中的 case 表达式没有排序规则标志,所以不能出现在选择列表中,也不能由区分排序规则的运算符操作。不过,这些表达式可由不区分排序规则的运算符操作。

SELECT (CASE WHEN id > 10 THEN GreekCol ELSE LatinCol END) 
FROM TestTab

下面是结果集。

Msg 451, Level 16, State 1, Line 1
Cannot resolve collation conflict for column 1 in SELECT statement.

SELECT PATINDEX((CASE WHEN id > 10 THEN GreekCol ELSE LatinCol END), 'a')
FROM TestTab

下面是结果集。

Msg 446, Level 16, State 9, Server LEIH2, Line 1
Cannot resolve collation conflict for patindex operation.

SELECT (CASE WHEN id > 10 THEN GreekCol ELSE LatinCol END) COLLATE Latin1_General_CI_AS 
FROM TestTab

下面是结果集。

--------------------
a

(1 row affected)

下表汇总了上述规则。

操作数强制标志
显式 X

隐性 X
强制默认
无排序规则
显式 Y生成错误结果为显式 Y结果为显式 Y结果为显式 Y
隐性 Y结果为显式 X结果为无排序规则结果为隐性 Y结果为无排序规则
强制默认结果为显式 X结果为隐性 X结果为强制默认结果为无排序规则
无排序规则结果为显式 X结果为无排序规则结果为无排序规则结果为无排序规则

运算符和函数区分排序规则或不区分排序规则:

  1. 区分排序规则表示指定无排序规则操作数是编译时错误。表达式结果不能是无排序规则。

  2. 不区分排序规则表示操作数和结果可以是无排序规则。

比较运算符以及 MAX、MIN、BETWEEN、LIKE 和 IN 运算符都区分排序规则。运算符所使用的字符串被赋以具有较高优先顺序的操作数的排序规则标志。UNION 运算符也区分排序规则,且所有的字符串操作数和最终结果被赋以具有最高优先顺序的操作数的排序规则。按列评估 UNION 操作数和结果的排序规则优先顺序。

赋值运算符不区分排序规则,右边的表达式投影到左边的排序规则上。

字符串串联运算符不区分排序规则,两个字符串操作数和结果被赋以具有最高排序规则优先顺序的操作数的排序规则标志。UNION ALL 和 CASE 运算符不区分排序规则,所有的字符串操作数和最终结果都被赋以具有最高优先顺序的操作数的排序规则标志。按列评估 UNION ALL 操作数和结果的排序规则优先顺序。

CAST、CONVERT 和 COLLATE 函数区分 charvarchartext 数据类型的排序规则。如果 CAST 和 CONVERT 函数的输入和输出是字符串,则输出字符串具有输入字符串的排序规则标志。如果输入不是字符串,则输出字符串为强制默认并被赋以连接所使用的当前数据库的排序规则,或是包含引用 CAST 或 CONVERT 的用户定义函数、存储过程或触发器的数据库的排序规则。

对于返回字符串但不使用字符串输入的内置函数,结果字符串为强制默认并被赋以当前数据库的排序规则,或是包含引用该函数的用户定义函数、存储过程或触发器的数据库的排序规则。

下列函数区分排序规则,其输出字符串具有输入字符串的排序规则标志:

  1. CHARINDEX

  2. DIFFERENCE

  3. ISNUMERIC

  4. LEFT

  5. LEN

  6. LOWER

  7. PATINDEX

  8. REPLACE

  9. REVERSE

  10. RIGHT

  11. SOUNDEX

  12. STUFF

  13. SUBSTRING

  14. UPPER

下列附加规则也适用于排序规则优先顺序:

  1. 在已经是显式表达式的表达式上不能有多个 COLLATE 子句。例如,下面的 WHERE 子句是不合法的,因为将 COLLATE 子句指定给的表达式已经是显式表达式:
    WHERE ColumnA = ( 'abc' COLLATE French_CI_AS) COLLATE French_CS_AS
    
  2. text 数据类型不允许代码页转换。如果排序规则的代码页不同,不能将文本表达式从一种排序规则投影到另一种排序规则。如果右边文本操作数的排序规则代码页与左边文本操作数不同,则赋值运算符不能赋值。

在数据类型转换后确定排序规则的优先顺序。产生结果排序规则的操作数可以与提供最终结果数据类型的操作数不同。例如,考察下面的批处理:

CREATE TABLE TestTab
   (PrimaryKey int PRIMARY KEY,
    CharCol char(10) COLLATE French_CI_AS
   )

SELECT *
FROM TestTab
WHERE CharCol LIKE N'abc'

简单表达式 N'abc' 的 Unicode 数据类型具有较高的数据类型优先顺序,所以结果表达式将 Unicode 数据类型指派给 N'abc'。然而,表达式 CharCol 具有隐性排序规则标志,而 N'abc' 具有较低的强制默认排序规则标志,所以使用的排序规则是 CharCol French_CI_AS 排序规则。

<think>好的,我现在需要帮助用户了解SQL的执行顺序,并通过实例进行详细解析。用户之前提到了几个引用,我需要结合这些引用来构建回答。 首先,我需要回忆一下SQL语句的执行顺序。通常,SQL的执行顺序并不是按照我们写查询的顺序来的,而是数据库优化器会按照特定的步骤来处理。比如,虽然SELECT写在最前面,但实际上执行的时候可能是在后面处理的。 接下来,我需要确定正确的执行步骤。常见的顺序应该是:FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET。不过,不同的数据库系统可能会有些微差异,但总体流程类似。 然后,我需要找一个合适的例子来演示这个过程。用户提供的引用中有一个例子是“select * from T where ID=10”,可能可以用这个作为基础。或者自己构造一个稍微复杂点的例子,包含JOIN、WHERE、GROUP BY、HAVING等子句,这样能更全面地展示执行顺序。 现在,结合用户提供的引用,引用3提到了词法分析的过程,分解出关键字、表名、列名等。引用1提到了解析SELECT语句时,SQLSelectStatement会保存相关的语法树节点,比如查询的列、表名、条件等。引用2则涉及动态SQL的解析,可能和预处理阶段有关。不过用户的问题更关注执行顺序,所以可能需要重点引用1和3。 接下来,我需要详细分解每个步骤,并对应到实例中。例如,在实例中,先执行FROM确定数据来源,然后JOIN连接表,接着应用WHERE过滤,再进行GROUP BY分组,之后处理HAVING,选择列,去重,排序,最后限制结果数量。每个步骤都要用实例中的具体部分来说明。 同时,需要注意用户要求使用中文回答,数学表达式用$...$格式,独立公式用$$...$$,但在这个问题中可能数学公式较少,更多是步骤说明。但如果有涉及表达式的地方,比如聚合函数中的计算,可能需要用LaTeX格式。 另外,用户要求生成相关问题,我需要根据内容提出几个相关的问题,比如执行顺序的原理、优化器的影响、如何查看执行计划等。 最后,确保回答结构清晰,分步骤讲解,结合实例,并正确引用提供的资料。比如在解析FROM的时候提到引用3中的词法分析,或者在处理子查询时提到引用1中的语法树节点保存信息。</think>### SQL 执行顺序及实例解析 SQL 的执行顺序并非按书写顺序执行,而是由数据库优化器根据逻辑处理阶段动态调整。以下是典型的执行顺序及示例解析: --- #### **1. 基础执行顺序** 1. **FROM**(确定数据来源) 从表或子查询中加载数据。若涉及多表,优先处理连接条件(如`JOIN`)[^3]。 **示例**:`FROM employees e JOIN departments d ON e.dept_id = d.id` 2. **WHERE**(行级过滤) 筛选满足条件的记录,*不涉及聚合函数*。 **示例**:`WHERE e.salary > 5000` 3. **GROUP BY**(分组聚合) 按指定列分组,通常与聚合函数(如`SUM`、`COUNT`)配合使用。 **示例**:`GROUP BY d.name` 4. **HAVING**(分组后过滤) 对聚合后的结果进行筛选。 **示例**:`HAVING COUNT(e.id) > 10` 5. **SELECT**(选择列) 确定最终输出的列,可能包含计算字段或别名。 **示例**:`SELECT d.name AS dept_name, AVG(e.salary)` 6. **ORDER BY**(排序) 按指定规则排序结果集。 **示例**:`ORDER BY AVG(e.salary) DESC` 7. **LIMIT/OFFSET**(限制结果) 控制返回的行数。 **示例**:`LIMIT 10` --- #### **2. 实例解析** **示例 SQL**: ```sql SELECT d.dept_name, AVG(e.salary) AS avg_salary FROM employees e JOIN departments d ON e.dept_id = d.id WHERE e.join_year >= 2020 GROUP BY d.dept_name HAVING AVG(e.salary) > 8000 ORDER BY avg_salary DESC LIMIT 5; ``` **执行步骤分解**: 1. **FROM + JOIN** 加载`employees`和`departments`表,按`dept_id`连接,生成临时数据集。 2. **WHERE** 过滤`join_year >= 2020`的员工记录。 3. **GROUP BY** 按部门名称`d.dept_name`分组。 4. **HAVING** 筛选平均工资超过 8000 的部门。 5. **SELECT** 提取部门名称和平均工资,并为`AVG(e.salary)`设置别名`avg_salary`[^1]。 6. **ORDER BY** 按`avg_salary`降序排列。 7. **LIMIT** 仅返回前 5 条结果。 --- #### **3. 关键注意事项** - **优化器影响**:实际执行顺序可能因索引、统计信息等调整。 - **子查询处理**:嵌套子查询可能被优化器重写为连接操作[^2]。 - **执行计划查看**:通过`EXPLAIN`命令可获取具体数据库(如MySQL)的执行顺序[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值