sql - SQL 中的 Order by 子句执行

标签 sql sql-server sql-order-by

这个问题与执行顺序无关。这只是关于 ORDER BY。

标准执行是:

  • 来自
  • 哪里
  • 分组依据
  • 拥有
  • 选择
  • 排序依据
  • 顶部

编辑:这个问题或多或少是“SQL Server 在执行 ORDER BY 表达式时是否应用短路求值?”的问题,答案是有时!我只是还没有找到合理的理由。请参阅编辑#4。

现在假设我有这样的声明:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  (
   SELECT
     MAX(PurchaseDateTime)
   FROM
     Purchases
   WHERE
     Purchases.CustomerID = Customers.CustomerID
  ) DESC --STATEMENT3

这不是我要执行的真正语句,而只是一个示例。 一共有三个 ORDER BY 语句。 第三条语句仅用于姓氏和名字匹配的极少数情况。

如果没有重复的姓氏,SQL Server 是否不执行 ORDER BY 语句 #2 和 #3?并且,从逻辑上讲,如果没有重复的姓氏和名字,SQL Server 是否会执行语句 #3。

这确实是为了优化。从购买表中读取数据应该只是最后的手段。就我的应用程序而言,从按“CustomerID”分组的“Purchases”中读取每个“PurchaseDateTime”的效率并不高。

请保留与我的问题相关的答案,而不是诸如为购买中的 CustomerID、PurchaseDateTime 建立索引之类的建议。真正的问题是,SQL Server 是否会跳过不必要的 ORDER BY 语句?

编辑:显然,只要有一行,SQL Server就会始终执行每一条语句。即使只有一行,这也会给您带来被零除​​的错误:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  1/(Contacts.ContactID - Contacts.ContactID) --STATEMENT3

编辑2: 显然,这不会被零除:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  CASE WHEN 1=0
    THEN Contacts.ContactID
    ELSE 1/(Contacts.ContactID - Contacts.ContactID)
  END --STATEMENT3

嗯,我的问题的原始答案是"is",它确实执行,但很好的是我可以用适当的 CASE WHEN 停止执行

编辑 3:我们可以使用适当的 CASE WHEN 来停止执行 ORDER BY 语句。我想,关键在于弄清楚如何正确使用它。 CASE WHEN 会给我我想要的,即 ORDER BY 语句中的短路执行。我比较了 SSMS 中的执行计划,根据 CASE WHEN 语句,购买表根本没有被扫描,即使它是一个清晰可见的 SELECT/FROM 语句:

DECLARE @dt18YearsAgo AS DATETIME = DATEADD(YEAR,-18,GETDATE());
SELECT
  Customers.Name
FROM
  Customers
WHERE
  Customers.DateOfBirth > @dt18YearsAgo
ORDER BY
  Contacts.LastName ASC, --STATEMENT1
  Contacts.FirstName ASC, --STATEMENT2
  CASE WHEN 1=0
    THEN
    (
     SELECT
       MAX(PurchaseDateTime)
     FROM
       Purchases
     WHERE
       Purchases.CustomerID = Customers.CustomerID
    )
    ELSE Customers.DateOfBirth
  END DESC

编辑4:现在我完全困惑了。这是@Lieven 的示例

WITH Test (name, ID) AS
(SELECT 'Lieven1', 1 UNION ALL SELECT 'Lieven2', 2)

SELECT * FROM Test ORDER BY name, 1/ (ID - ID)

这不会产生被零除的情况,这意味着 SQL Server 实际上会对某些表进行短路评估,特别是那些使用 WITH 命令创建的表。

使用 TABLE 变量尝试:

DECLARE @Test TABLE
(
    NAME nvarchar(30),
    ID int
);
INSERT INTO @Test (Name,ID) VALUES('Lieven1',1);
INSERT INTO @Test (Name,ID) VALUES('Lieven2',2);
SELECT * FROM @Test ORDER BY name, 1/ (ID - ID)

将产生除以零的错误。

最佳答案

首先,你所说的“声明”并不是这样的东西。它们是 ORDER BY(主)子句的子子句。区别很重要,因为“语句”意味着可分离的、有序的和过程性的东西,而 SQL 子句则不是这些东西。

具体来说,SQL 子子句(即 SQL 主子句的各个项目(SELECT、FROM、WHERE、ORDER BY 等))没有自己的隐式(也不显式)执行顺序。 SQL 会以它认为方便的方式对它们重新排序,并且如果执行其中任何一个,几乎总是会执行所有它们。简而言之,SQL Server 不会进行这种“短路”优化,因为它们的效果微乎其微,而且会严重妨碍它所进行的不同类型的优化(即统计数据访问/运算符优化)。

所以你原来的问题(你不应该改变)的正确答案是“否”,不可靠。您不能依赖 SQL Server 不使用 ORDER BY 的某些子条款,仅仅因为它看起来不需要。

唯一常见的异常(exception)是 CASE 函数(在大多数情况下)可用于短路执行路径(在 CASE 函数内,而不是在其外部),但仅因为它是专门为此设计的。我想不出 SQL 中有任何其他东西可以让您像这样执行操作。

关于sql - SQL 中的 Order by 子句执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11475343/

相关文章:

mysql - 从关系数据库表中删除重复记录

java - 从搜索表单动态构建 WHERE 子句时如何防止 SQL 注入(inject)?

php - 如何在 Mac OS 中安装 PHP 的 MSSQL 驱动程序

MySQL 'Order By' - 正确排序字母数字

php - 在 codeigniter 上的 foreach 中的条件(排序依据)

sql - 一个关于oracle sql developer的问题?

php - 向表中插入数据并立即选择该行的ID号

sql - 带 JOIN 的条件更新语句

sql-server - 使用 SSIS 将数据从 Oracle 导入到 SQL Server 时出现代码页错误

Mysql - 按 SQL 查询排序