当前位置: 首页 > 面试题库 >

SQL中的按子句执行顺序

弓方伟
2023-03-14
问题内容

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

在标准执行中是:

  • FROM
  • WHERE
  • GROUP BY
  • HAVING
  • SELECT
  • ORDER BY
  • TOP

编辑:这个问题或多或少是“ 执行ORDER BY表达式时SQL Server是否应用短路评估吗?
”的答案是有时!我只是还没有找到一个合理的理由。参见编辑#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。

这确实是为了优化。从“购买”表中读取数据仅是不得已的方法。在我的应用程序中,从“ Purchases”分组中按“ CustomerID”读取每个“
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

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

好吧,我的问题的原始答案是“是”,它确实可以执行,但是很好的是,我可以在适当的情况下停止执行

编辑3:我们可以在适当的情况下停止执行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

编辑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的某些子句,仅仅是因为看起来它不需要。

唯一的常见例外是CASE函数(在大多数情况下)可用于使执行路径短路(尽管在CASE函数 内部
,而不是在CASE函数外部),但这仅是因为它是专门为此设计的。我想不出可以依靠SQL来执行此操作的任何其他内容。



 类似资料:
  • 问题内容: 我有SQL Server 2008 R2,Windows 7 OS。 在服务器内,我有一个通过以下SQL语句创建的表。 在表中,我具有以下值: 我确实对数据库执行了以下SQL语句: 服务器给我以下错误消息: 转换nvarchar值“ 1”时转换失败。 数据类型为int。 我以为我的内部SQL首先执行并创建了一个临时表,因此它不包含在表中,然后SQL解析器将用于过滤临时表。 但是以上似乎

  • 问题内容: 我想知道是否有按IN()子句中的值顺序进行排序的方法(可能是一种更好的方法)。 问题是我有2个查询,一个查询获取所有ID,第二个查询获取所有信息。第一个创建我要第二个排序的ID的顺序。这些ID以正确的顺序放入IN()子句中。 因此,它类似于(极其简化): 问题在于第二个查询不会以将ID放入IN()子句中的顺序来返回结果。 我发现的一种解决方案是将所有ID放入具有自动递增字段的临时表中,

  • 问题内容: 我想知道是否有按IN()子句中的值顺序进行排序的方法(可能是一种更好的方法)。 问题是我有2个查询,一个查询获取所有ID,第二个查询获取所有信息。第一个创建我要第二个排序的ID的顺序。这些ID以正确的顺序放入IN()子句中。 因此,它类似于(极其简化): 问题在于第二个查询不会以将ID放入IN()子句中的顺序来返回结果。 我发现的一种解决方案是将所有ID放入具有自动递增字段的临时表中,

  • 问题内容: 在MySQL中执行子句的预定义顺序是什么?它是否在运行时确定,并且此顺序正确吗? 问题答案: MySQL语句的实际执行有些棘手。但是,该标准确实指定了查询中元素解释的顺序。这基本上是在您指定的顺序,但我想和能来后: 条款 条款 条款 条款 条款 条款 这对于了解如何解析查询很重要。例如,您不能使用在子句中的定义的列别名,因为会在之前解析。另一方面,这样的别名可以在子句中。 至于实际执行

  • 我在研究手机导航。我对简单的if/else如何工作的理解肯定有根本性的错误(我已经有一段时间没有使用jQ/js了),因为这两个语句是串行执行的。实际上‘else’首先运行,然后——不管我是否包含‘toggleNav()’函数或$(nav)。toggleClass('open') -然后也执行' if '语句。 我认为这可能与事件有关,所以我尝试了两个li上的函数。仪表板切换导航和(a.dashbo

  • 问题内容: 我正在从具有整数id值的大型表(数百万行)中选择一组帐户记录。从某种意义上说,查询是最基本的。我正在做的是建立一个大型的逗号分隔列表,并将其作为“ in”子句传递到查询中。现在,结果是完全无序的。我想做的是按“ in”子句中的值顺序返回结果。 我想我将不得不建立一个临时表并进行联接,这是我想避免的,但可能无法做到。 有什么想法吗?现在,由于我们正试图限制输出大小,每个查询的大小上限为6