在SQL中执行order by子句

Sho*_*use 5 sql sql-server sql-order-by

这个问题不是关于处决的顺序.这只是关于ORDER BY.

在标准执行中是:

  • 哪里
  • 通过...分组
  • HAVING
  • 选择
  • 订购
  • 最佳

编辑:这个问题或多或少是" 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
Run Code Online (Sandbox Code Playgroud)

这不是我试图执行的真实陈述,而只是一个例子.有三个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
Run Code Online (Sandbox Code Playgroud)

Edit2:显然,这不会给出除以零:

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
Run Code Online (Sandbox Code Playgroud)

好吧,我的问题的原始答案是肯定的,它确实执行了,但更好的是我可以用适当的CASE WHEN停止执行

编辑3:我们可以使用适当的CASE WHEN停止执行ORDER BY语句.我想,诀窍是弄清楚如何正确使用它.CASE WHEN将给出我想要的东西,即ORDER BY语句中的短路执行.我比较了SSMS中的执行计划,并且根据CASE WHEN语句,不会扫描Purchases表,即使它是一个清晰可见的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
Run Code Online (Sandbox Code Playgroud)

编辑4:现在我完全糊涂了.这是@Lieven的一个例子

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

SELECT * FROM Test ORDER BY name, 1/ (ID - ID)
Run Code Online (Sandbox Code Playgroud)

这不会产生除零,这意味着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)
Run Code Online (Sandbox Code Playgroud)

将产生除以零误差.

RBa*_*ung 5

首先,你所谓的"陈述"不是这样的.它们是ORDER BY(主要)条款的子条款.区别很重要,因为"Statement"意味着可分离,有序和程序化的东西,而SQL子句则不是那些东西.

具体来说,SQL子子句(即SQL主要子句(SELECT,FROM,WHERE,ORDER BY等)的各个项)没有自己的隐式(也没有显式)执行顺序.SQL将重新排序他们无论如何它找到方便,几乎总是将执行所有这些,如果它执行任何人.简而言之,SQL Server不会进行那种"短路"优化,因为它们非常有效并严重妨碍它所做的非常不同的优化(即统计数据访问/运算符优化).

因此,对原始问题(您不应该更改)的正确答案是否定的,不可靠.您不能依赖SQL Server不使用ORDER BY的某些子子句,只是因为它看起来不需要.

唯一常见的例外是CASE函数可以(在大多数情况下)用于短路执行路径(虽然在CASE函数内部,但不在其外部),但仅仅因为它是专门为此设计的.我不能想到你可以依赖的任何其他SQL行为.