AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 21965
Accepted
Shauna
Shauna
Asked: 2012-08-04 06:51:34 +0800 CST2012-08-04 06:51:34 +0800 CST 2012-08-04 06:51:34 +0800 CST

为什么以不允许在大多数子句中使用列别名的方式解析查询?

  • 772

在尝试编写查询时,我发现(很难)SQL Server 在执行查询时解析 SELECT 之前很久就解析查询中的 WHERE。

MSDN 文档说,一般的逻辑解析顺序使得 SELECT 几乎在最后被解析(因此在尝试在其他子句中使用列别名时导致“没有这样的对象 [别名]”错误)。甚至有一个建议允许在任何地方使用别名,但被微软团队否决了,理由是 ANSI 标准合规性问题(这表明这种行为是 ANSI 标准的一部分)。

作为一名程序员(不是 DBA),我发现这种行为有点令人困惑,因为在我看来,它在很大程度上违背了拥有列别名的目的(或者,至少,如果列别名是在查询执行的早期解析),因为您可以实际使用别名的唯一位置是在 ORDER BY 中。作为一名程序员,它似乎错过了一个让查询更强大、更方便和更干的机会。

看起来这是一个如此明显的问题,因此有理由认为,除了 SELECT 和 ORDER BY 之外,还有其他原因决定不允许在任何其他内容中使用列别名,但这些原因是什么?

sql-server alias
  • 3 3 个回答
  • 3488 Views

3 个回答

  • Voted
  1. Best Answer
    ErikE
    2012-08-04T13:20:13+08:002012-08-04T13:20:13+08:00

    概括

    没有理由不能这样做,但好处很小,而且有些陷阱可能不会立即显现出来。

    研究成果

    我做了一些研究,发现了一些很好的信息。以下是格林威治标准时间 2012 年 8 月 9 日 17:49 来自可靠的主要来源(希望保持匿名)的直接引用:

    最初发明 SQL 时,它在 SELECT 子句中没有别名。这是一个严重的缺陷,大约在 1986 年被 ANSI 标准化时,该语言得到了纠正。

    该语言旨在“非程序化” - 换句话说,描述您想要的数据而不指定如何找到它。因此,据我所知,SQL 实现没有理由不能在处理整个查询之前对其进行解析,并允许在任何地方定义别名并在任何地方使用。例如,我看不出以下查询无效的任何原因:

    select name, salary + bonus as pay
    from employee
    where pay > 100000
    

    虽然我认为这是一个合理的查询,但某些基于 SQL 的系统可能会出于某些与实现相关的原因对别名的使用进行限制。听到 SQL Server 这样做,我并不感到惊讶。

    我有兴趣进一步研究 SQL-86 标准以及为什么现代 DBMS 不支持别名重用,但还没有时间深入研究它。对于初学者,我不知道从哪里获得文件或如何找出委员会的确切成员。任何人都可以帮忙吗?我还想了解更多有关 SQL Server 的原始 Sybase 产品的信息。

    通过这项研究和一些进一步的思考,我开始怀疑在其他子句中使用别名虽然很有可能,但与其他语言功能相比,DBMS 制造商从未如此重视。由于它不是那么大的障碍,查询编写者很容易解决它,因此将精力放在其他改进上并不是最佳的。此外,它将是专有的,因为它显然不是 SQL 标准的一部分(尽管我正在等待确定更多信息),因此将是一个小的改进,破坏了 DBMS 之间的 SQL 兼容性。相比之下,CROSS APPLY(实际上只不过是一个允许外部引用的派生表)是一个巨大的变化,虽然专有提供了令人难以置信的表达能力,但其他方式并不容易实现。

    到处使用别名的问题

    如果您允许将 SELECT 项放在 WHERE 子句中,您不仅可以增加查询的复杂性(以及因此找到一个好的执行计划的复杂性),还可能提出完全不合逻辑的东西。尝试:

    SELECT X + 5 Y FROM MyTable WHERE Y = X
    

    如果 MyTable 已经有 Y 列,WHERE 子句指的是哪一列呢?解决方案是使用 CTE 或派生表,在大多数情况下,这不会产生额外费用,但可以达到相同的最终结果。CTE 和派生表至少通过允许别名仅使用一次来强制解决歧义。

    此外,不在 FROM 子句中使用别名也很有意义。你不能这样做:

    SELECT
       T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
    FROM
       Table1 T
       INNER JOIN Table2 T2
          ON T2.ID = CalcID
       INNER JOIN Table3 T3
          ON T2.ID = T3.ID
    

    这是一个循环引用(从某种意义上说,T2 秘密地引用了来自 T3 的值,在该表出现在 JOIN 列表中之前),而且很难看到。这个怎么样:

    INSERT dbo.FinalTransaction
    SELECT
       newid() FinalTransactionGUID,
       'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
       T.*
    FROM
       dbo.MyTable T
    

    你有多少赌注 newid() 函数将被放入执行计划两次,完全出乎意料地使两列显示不同的值?当上面的查询在 CTE 或派生表中使用 N 层时会怎样。我保证问题比你想象的还要严重。关于何时只评估一次或在查询计划中的什么时间点已经存在严重的不一致问题,微软表示不会修复其中一些是因为它们正确地表达了查询代数——如果得到意想不到的结果,请将查询分成几部分。允许链式引用,通过可能很长的链检测循环引用——这些都是非常棘手的问题。引入并行性,您将面临一场噩梦。

    注意:在 WHERE 或 GROUP BY 中使用别名不会对 newid() 或 rand() 等函数的问题产生影响。

    创建可重用表达式的 SQL Server 方法

    CROSS APPLY/OUTER APPLY 是 SQL Server 中创建可在查询中其他任何地方使用的表达式的一种方式(只是在 FROM 子句中不更早):

    SELECT
       X.CalcID
    FROM
       Table1 T
       INNER JOIN Table3 T3
          ON T.ID = T3.ID
       CROSS APPLY (
          SELECT
             T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
       ) X
       INNER JOIN Table2 T2
          ON T2.ID = X.CalcID
    

    这做了两件事:

    1. 使 CROSS APPLY 中的所有表达式都获得一个“命名空间”(表别名,此处为 X)并在该命名空间中是唯一的。
    2. 不仅 CalcID 来自 X,而且在连接表 T1 和 T3 时不能使用 X 中的任何内容,因为 X 还没有被引入,所以在任何地方都很明显。

    我其实很喜欢 CROSS APPLY。它已成为我忠实的朋友,我一直在使用它。需要部分 UNPIVOT(这需要使用本机语法的 PIVOT/UNPIVOT 或 UNPIVOT/PIVOT)?完成交叉应用。需要一个可以重复使用多次的计算值?完毕。需要严格执行链接服务器上的调用的执行顺序?完成——速度有了惊人的提高。只需要一种类型的行拆分为 2 行或附加条件?完毕。

    因此,至少,在 DBMS SQL Server 2005 及更高版本中,您没有进一步的抱怨理由:CROSS APPLY 是您按照您想要的方式进行干燥的方式。

    • 19
  2. Aaron Bertrand
    2012-08-04T07:04:36+08:002012-08-04T07:04:36+08:00

    我不能告诉你确切的原因,但我会告诉你重复表达式有一些变通方法,例如使用 CTE、子查询、派生表等来避免重复。

    如果您显示带有重复表达式的查询,我们可能会向您展示如何重写它,以便表达式只列出一次。然而,这只是降低了写入/读取查询的复杂性,不太可能改变效率。SQL Server 通常可以很好地识别重复的表达式,并且不会执行两次该工作。也有相反的例外情况,但只有在实际观察到这种情况发生时,您才应该关心效率。我怀疑您编写的大多数重复表达式实际上都折叠为计划中的一个操作。

    综上所述,我还将重复我对这个问题的部分回答:

    https://dba.stackexchange.com/questions/19762/why-is-the-select-clause-listed-first


    这是 Joe Celko 对如何根据标准处理查询的解释(我从我自己的 aspfaq.com 文章中偷了这个,这可能是从 Celko 的新闻组帖子中偷来的):

    以下是 SELECT 在 SQL 中的工作原理……至少在理论上是这样。真正的产品会尽可能优化事物。

    从 FROM 子句开始,根据所有的连接、联合、交集和任何其他表构造函数构建一个工作表。AS 选项允许您为此工作表命名,然后您必须将其用于包含查询的其余部分。

    转到 WHERE 子句并删除不通过条件的行;也就是说,不测试为 TRUE(拒绝 UNKNOWN 和 FALSE)。WHERE 子句适用于 FROM 子句中的工作。

    转到可选的 GROUP BY 子句,创建组并将每个组减少为一行,用新的分组表替换原始工作表。分组表的行必须是组特征:(1) 分组列 (2) 关于组的统计信息(即聚合函数) (3) 函数或 (4) 由这三个项目组成的表达式。

    转到可选的 HAVING 子句并将其应用于分组工作表;如果没有 GROUP BY 子句,则将整个表视为一个组。

    转到 SELECT 子句并构造列表中的表达式。这意味着 SELECT 中的标量子查询、函数调用和表达式是在所有其他子句完成之后完成的。AS 运算符也可以为 SELECT 列表中的表达式命名。这些新名称同时出现,但在 WHERE 子句执行之后;由于这个原因,您不能在 SELECT 列表或 WHERE 子句中使用它们。

    嵌套查询表达式遵循您对块结构语言(如 C、Pascal、Algol 等)所期望的通常范围规则。也就是说,最里面的查询可以引用包含它们的查询中的列和表。

    这意味着 SELECT 的列不能多于 GROUP BY;但它当然可以有更少的列。

    现在,Celko 是早期版本标准的主要贡献者之一。我不知道你是否会得到这个问题的明确答案WHY?,除了猜测。我的猜测是,首先列出实际操作会使解析器很容易准确地知道操作的类型。想象一下一个 20 表的连接,它最终可能是SELECTorUPDATE或DELETE,请记住,这些引擎的代码最初是在字符串解析非常昂贵的时候写的。

    请注意,如果 SQL 标准规定FROM要先出现,供应商可能已经独立决定以不同的顺序解析语法,因此期望编写的子句顺序完全遵守处理 100% 的顺序可能仍然没有意义。时间。

    类似的事情也是如此CASE。例如,我们已经在此站点上看到了场景,例如,以前认为CASE总是按顺序处理和短路的神话是错误的。这也延伸到其他常见的信念,例如 SQL Server 按编写顺序评估连接,从左到右短路WHERE子句,或者处理 CTE 一次或按特定顺序处理,即使它们被多次引用。产品可以自由优化他们认为合适的方式,即使它不能准确反映您所说的查询应该如何以声明方式工作。

    • 14
  3. ErikE
    2013-09-12T10:29:54+08:002013-09-12T10:29:54+08:00

    在Entity SQL中,在某些情况下,您可以在查询的其他位置使用来自表达式的别名:

    select k1, count(t.a), sum(t.a)
    from T as t
    group by t.b + t.c as k1
    

    请注意,在这里您必须在子句中定义表达式GROUP BY才能在子句中使用它SELECT。

    显然可以在 SQL 查询中允许一些这种别名作为可重用表达式。

    • 2

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何查看 Oracle 中的数据库列表?

    • 8 个回答
  • Marko Smith

    mysql innodb_buffer_pool_size 应该有多大?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    从 .frm 和 .ibd 文件恢复表?

    • 10 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve