我正在使用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,看起来它们的行为也是一样的。 (在应用过滤器之前确定。)
答案 0 :(得分:2)
如果使用CTE或子查询实现此功能,则会看到相同的结果。这是必要的,因为有时您需要在结果上生成排名,然后通过外部查询保持该排名不变。因此,就好像排名首先作为子查询的一部分生成,然后它被“锁定”,以便您可以根据该行号过滤结果。
让我们假设外部查询中的一个过滤器实际上是rnkDesc = 2
,这种方式有时候你可以做到“第二多”。想象一下,如果在生成外部查询过滤器之后才生成行号,这将使这种类型的方法变得不可能。你如何根据尚未确定的东西的价值过滤结果?这与过滤窗口函数通常需要首先嵌套子查询或CTE的原因相同,因此您可以对生成的结果进行过滤,并且这些结果不会在外部动态重新编号/排名。
因此,基于它们发生的嵌套来锁定窗口函数结果是有意义的。你必须根据子结果考虑这种嵌套。
这样就可以回答你的问题“为什么”。我理解你想要实现的目标以及为什么要这样做。您正在尝试简化窗口函数的使用,并将其动态应用于最终结果。老实说,如果不在外部查询中包含窗口函数,就不确定“如何”执行此操作。可能有一种方法可以将其嵌入到UDF中,但我不确定。
答案 1 :(得分:2)
了解SQL中的评估顺序是明确的,确实是确定的,这很重要。
在左连接发生之前,已经评估了视图上的ROW_NUMBER
。 ON
子句不作为&#34;过滤器&#34;在连接引用的表上,但作为表之间的连接必须满足的条件。
您当然可以创建一个&#34;参数化视图&#34; (表值内联函数),它允许您在select子句中应用ROW_NUMBER
之前将过滤日期传递给where子句,然后外部应用于它。如果大量查询使用相同的功能,这可能是合适的。
但是否则我倾向于离开&#34;物质使用历史&#34;使用任何行编号进行纯粹操作(除非它被其他查询独立使用以获得绝对最新答案),并在当前约会之前或之前写下&#34;最新行#34;逻辑内联。