为什么视图中的ROW_NUMBER不尊重过滤器

时间:2018-02-09 20:54:24

标签: sql sql-server

我正在使用SQL Server 2014.

我创建了一个查看患者历史答案的视图,例如tabaco使用,酒精使用等。这些答案带有时间戳,但没有与预约标识符相关联,我需要找到相对于预约日期的最新答案。

视图表面的数据如下所示:

PATIENT_ID | ANSWER_DATE | TOBACCO_USE
 1         |  1/1/2018   |  No
 1         |  1/5/2018   |  Yes
 1         |  1/10/2018  |  Quit
 2         |  1/1/2018   |  No

我知道当我加入此表以获得我需要的排名时,我可以在内联查询中使用ROW_NUMBER(),但我真的想在视图中添加ROW_NUMBER()OVER(PARTITION BY PATIENT_ID ORDER BY ANSWER_DATE DESC) as 'rnkDesc'列,以使其更简单开发人员正确加入此表。

使用这个新列,视图中的SELECT *如下所示:

PATIENT_ID | ANSWER_DATE | TOBACCO_USE | rnkDesc
 1         |  1/1/2018   |  No         |  3
 1         |  1/5/2018   |  Yes        |  2
 1         |  1/10/2018  |  Quit       |  1
 2         |  1/1/2018   |  No         |  1

这是预期的,现在我从我的约会表中加入如下:

FROM APPOINTMENTS appt 
LEFT JOIN myHistoryView his 
on appt.PATIENT_ID = his.PATIENT_ID 
    and his.ANSWER_DATE <= APPT.APPT_DATE
    and his.rnkDesc = 1

但这不起作用,因为在应用过滤器之前,似乎评估了ROW_NUMBER。如果我过滤了我的视图where PATIENT_ID = 1 and ANSWER_DATE = 1/5/2018,那么rnkDesc仍会显示2,而不是像我在内联查询中使用ROW_NUMBER时那样。

我真的对为什么会这样做感兴趣。我可以使用内联查询来编写它。 我知道这些排名函数是nondeterministic,并且会认为引擎会在生成ROW_NUMBER之前过滤视图中的结果集。我也尝试过RANK和DENSE_RANK,看起来它们的行为也是一样的。 (在应用过滤器之前确定。)

2 个答案:

答案 0 :(得分:2)

如果使用CTE或子查询实现此功能,则会看到相同的结果。这是必要的,因为有时您需要在结果上生成排名,然后通过外部查询保持该排名不变。因此,就好像排名首先作为子查询的一部分生成,然后它被“锁定”,以便您可以根据该行号过滤结果。

让我们假设外部查询中的一个过滤器实际上是rnkDesc = 2,这种方式有时候你可以做到“第二多”。想象一下,如果在生成外部查询过滤器之后才生成行号,这将使这种类型的方法变得不可能。你如何根据尚未确定的东西的价值过滤结果?这与过滤窗口函数通常需要首先嵌套子查询或CTE的原因相同,因此您可以对生成的结果进行过滤,并且这些结果不会在外部动态重新编号/排名。

因此,基于它们发生的嵌套来锁定窗口函数结果是有意义的。你必须根据子结果考虑这种嵌套。

这样就可以回答你的问题“为什么”。我理解你想要实现的目标以及为什么要这样做。您正在尝试简化窗口函数的使用,并将其动态应用于最终结果。老实说,如果不在外部查询中包含窗口函数,就不确定“如何”执行此操作。可能有一种方法可以将其嵌入到UDF中,但我不确定。

答案 1 :(得分:2)

了解SQL中的评估顺序是明确的,确实是确定的,这很重要。

在左连接发生之前,已经评估了视图上的ROW_NUMBERON子句不作为&#34;过滤器&#34;在连接引用的表上,但作为表之间的连接必须满足的条件。

您当然可以创建一个&#34;参数化视图&#34; (表值内联函数),它允许您在select子句中应用ROW_NUMBER之前将过滤日期传递给where子句,然后外部应用于它。如果大量查询使用相同的功能,这可能是合适的。

但是否则我倾向于离开&#34;物质使用历史&#34;使用任何行编号进行纯粹操作(除非它被其他查询独立使用以获得绝对最新答案),并在当前约会之前或之前写下&#34;最新行#34;逻辑内联。