3

在 MS SQL Server 中,我花了太多时间试图解决这个问题。我终于想通了,只是我不知道原因。怎么会,除以第 4 行中的 cast 语句在下面起作用

SELECT 
  cast(dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT AS decimal(18,8))
AS TOTAL_NET_AMOUNT_AMOUNT,
  cast((SUM(dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT)
OVER (PARTITION BY dbo.DIMPROJECT.PROJECT_KEY)) AS decimal(18,8))
AS ActualAmountPaidOnProjectGroupedByInvoice,
  ((dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT)
/
  (cast((SUM(dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT)
OVER (PARTITION BY dbo.DIMPROJECT.PROJECT_KEY)) AS decimal(18,8))))
AS 'Allocation_Amount',

但是当我尝试除以我创建的别名时,第 3 行中的 ''ActualAmountPaidOnMatterGroupedByInvoice' 我收到一条错误消息:

消息 207,级别 16,状态 1,第 131 行无效的列名称“ActualAmountPaidOnMatterGroupedByInvoice”

示例不正确的代码:

SELECT 
    cast(dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT AS decimal(18,8))
  AS TOTAL_NET_AMOUNT_AMOUNT,
    cast((SUM(dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT)
    OVER (PARTITION BY dbo.DIMPROJECT.PROJECT_KEY))
  AS decimal(18,8))
  AS ActualAmountPaidOnProjectGroupedByInvoice,
    ((dbo.FACTINVOICEHEADER.TOTAL_NET_AMOUNT_AMOUNT)
  /
    (ActualAmountPaidOnProjectGroupedByInvoice) AS decimal(18,8))))
  AS 'Allocation_Amount'

怎么来的?谢谢大家!

4

2 回答 2

3

您不能在查询中使用别名的原因是该别名尚未被查询引擎识别。引擎按以下顺序分阶段评估查询:

FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT

SELECT阶段的最后一步是将查询中指定的别名应用于结果数据集。由于这些直到SELECT阶段结束才应用,因此它们在评估要返回的数据时不可用,也不在WHEREGROUP BYHAVING阶段。

此外,一些查询引擎确实允许在ORDER BY阶段中使用别名(或序号位置)。正如 Julian 在评论中指出的那样,MSSQL 确实允许顺序位置排序语法。

于 2019-07-21T00:17:48.020 回答
1

我认为您可能会误解别名列在哪里可用/可以被别名引用,特别是因为您说(释义)“我在 sql 的第 3 行创建的别名在第 4 行不可用”:

错误的:

SELECT
  1200 as games_won,
  25 as years_played,
  --can't use these aliases below in the same select block that they were declared in
  games_won / years_played as games_won_per_year 
  ...

正确的:

SELECT
  1200 as games_won,
  25 as years_played,
  --can use the values though 
  1200 / 25 as games_won_per_year

正确的:

SELECT
  games_won / years_played as games_won_per_year  --alias from inner scope is available in this outer scope
FROM
(
  SELECT 
    --these aliases only become available outside the brackets
    1200 as games_won,
    25 as years_played
) x

您不能为列设置别名并在同一选择块中再次使用该别名;您只能在内部/子查询中使用别名,并在外部查询中使用别名。SQL不像是逐行操作的编程语言:

int gameswon = 1200;
int yearsplayed = 25;
int winsperyear = gameswon / yearsplayed;

在这个 C# 中,您可以看到我们在前面的行中声明变量(别名)并在后面的行中使用它们,但这是因为编程语言逐行操作。较早行执行的结果可用于后面的行。SQL 不是这样工作的。SQL 一次对查询的整个部分起作用。在整个选择块完成处理之前,您的列不会获取您为它们提供的那些别名,因此您不能为列或计算提供别名,然后在同一个选择块中再次使用该别名。解决此问题并创建稍后将重复使用的别名的唯一方法是在子查询中创建别名。

这是另一个例子:

SELECT 
  fih.tot_amt / fih.amt_per_proj AS allocation_amount

FROM
  (
    SELECT
      CAST(f.total_net_amount_amount AS DECIMAL(18,8)) as tot_mt,
      CAST(SUM(f.total_net_amoun_amount) OVER (PARTITION BY p.project_key)) AS DECIMAL(18,8)) AS amt_per_proj
    FROM
      dbo.factinvoiceheader f
      INNER JOIN
      dbo.dimproject p
      ON ...
  ) fih

在这里你可以看到我拉出我想要的列并在内部查询中为它们加上别名,然后在外部查询中使用别名 - 它之所以有效,是因为内部块内贴花的别名可供外部块使用

永远记住,SQL 不是典型的编程语言逐行逐行,而是逐块编写。事实上,在大多数编程语言中,内部代码块中声明的内容在外部代码块中不可用(除非它们是一些全球化的东西,如 javascript var),因此 SQL 与您习惯的不同。每次在 SQL 中创建指令块时,您都有机会重新为数据列设置别名。

因为 SQL 是基于块的,所以我将我的 SQL 缩进块中以便于查看一起处理的内容。SELECT、FROM、WHERE、GROUP BY 和 ORDER BY 等关键字表示可以为 SELECT 中的列和 FROM 中的表创建块和别名。在以上面的示例为例,我不仅将别名应用于计算和列,还应用于表。它使查询在整个缩进和别名时更易于阅读 - 为您的表名提供别名,而不是dbo.factinvoiceheader.在每个列名之前写入

下面是一组使 SQL 更整洁、更易于阅读和调试的技巧:

  • 不要将它们全部放在一行或相同的缩进级别 - 根据指令块的深度或浅度进行缩进
  • select,from,where,group by,order by 等表示操作块的开始 - 将它们全部缩进同一级别并将其子指令缩进另一个级别(如果您的选择是缩进级别 2,则选择的列应该缩进级别 3)
  • 当你有一个内部查询时也会缩进,除非它真的很简单并且作为一个衬里读起来很好
  • 对列名和表名使用小写,对保留字、函数、数据类型使用大写(有些人更喜欢函数的驼峰式大小写)
  • 决定是否使用 canelCase 或 underscore_style 来分割你的单词并坚持下去
  • 始终为表取别名,并始终将列选择为 tablealias.columnname - 如果表添加的列与您选择的原始列同名,但未限定原始列来自哪个表,这可以防止您的查询在将来中断
  • 别名表允许另一个重要的操作;重复将同一个表加入到查询中。如果您的 Person 表有一个 WorkAddress 和一个 HomeAddress,那么您可以两次加入地址表以获取一个人的两个地址的唯一方法是为该表设置别名 ( person join address h on p.homeaddressid = h.id join address w on p.workaddressid = w.id)
于 2019-07-21T00:12:34.733 回答