在 SQL 中除以 'Over' 子句有效但除以 'Alias' 不

Divide by 'Over' clause in MSSQL works but divide by 'Alias' does not

在 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',

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

Msg 207, Level 16, State 1, Line 131 Invalid column name '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'

怎么会?谢谢大家!

我认为您可能误解了别名列 available/able 被别名引用的地方,特别是因为您说(释义)"an alias I created on line 3 of the sql wasn't available on line 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

您不能为列设置别名并在同一个 select 块中再次使用该别名;您只能在 inner/subquery 中使用别名并在外部查询中使用别名。 SQL不像像一种逐行操作的编程语言:

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

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

这是另一个例子:

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 中创建指令块时,您都有机会 re-alias 数据列。

因为 SQL 是逐块进行的,所以我在块中缩进了 SQL 以便于查看一起处理的内容。 SELECT、FROM、WHERE、GROUP BY 和 ORDER BY 等关键字表示可以为 SELECT 中的列和 FROM 中的 table 的列创建块和别名。在上面的示例中,我不仅对计算和列应用了别名,而且还对 table 应用了别名。它使查询在整个过程中缩进和别名时更容易阅读 - 给你的 table 名称一个别名而不是在每个列名称

之前写 dbo.factinvoiceheader.

这里有一组技巧可以让您的 SQL 更整洁、更易于阅读和调试:

  • 不要将它们全部放在一行或同一缩进级别 - 根据指令块的深度或浅度缩进
  • select、from、where、group by、order by 等表示操作块的开始 - 将它们全部缩进到同一级别并将它们的 sub-instructions 缩进另一个级别(如果您select 是缩进级别 2,被 selected 的列应该是缩进级别 3)
  • 当你有一个内部查询时也缩进,除非它真的很简单并且作为一个衬里读起来很好
  • 对列和 table 名称使用小写,对保留字、函数、数据类型使用大写(有些人更喜欢函数的驼峰式大小写)
  • 决定是使用 canelCase 还是 underscore_style 拆分您的单词并遵守它
  • 总是别名 tables,并且总是 select 列作为 tablealias.columnname - 如果 table 添加了一个列,这可以防止你的查询在将来中断与您 select 编辑的原始专栏同名,但未限定 table 原始专栏的来源
  • 别名 tables 允许另一个重要的操作;重复将相同的 table 连接到一个查询中。如果你的人 table 有一个工作地址和一个家庭地址,你可以两次加入地址 table 以获得一个人的两个地址的唯一方法是为 table 添加别名(person join address h on p.homeaddressid = h.id join address w on p.workaddressid = w.id)

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

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

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

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