GSe*_*erg 10 sql-server-2008 view window-functions
很久以前我遇到过这个问题,我找到了一个适合我的解决方法并忘记了它。
但是现在在 SO 上有这个问题,所以我愿意提出这个问题。
有一个视图以非常简单的方式(订单 + 订单行)连接几个表。
在没有where
子句的情况下查询时,视图返回几百万行。
然而,从来没有人这样称呼它。通常的查询是
select * from that_nasty_view where order_number = 123456;
Run Code Online (Sandbox Code Playgroud)
这将返回 5m 中的大约 10 条记录。
一件重要的事情:视图包含一个窗口函数,rank()
,它由始终查询视图所使用的字段精确分区:
rank() over (partition by order_number order by detail_line_number)
Run Code Online (Sandbox Code Playgroud)
现在,如果使用查询字符串中的文字参数查询此视图,完全如上所示,它会立即返回行。执行计划很好:
order_number
(返回 10 行)。然而,当以参数化的方式调用视图时,事情变得很糟糕:
Index scan
在所有表上忽略索引。返回 5m 行。partition
s 的窗口(大约 500k 个窗口)。Filter
从 5m 中取出 10 行。在涉及参数的所有情况下都会发生这种情况。它可以是 SSMS:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
Run Code Online (Sandbox Code Playgroud)
它可以是 ODBC 客户端,例如 Excel:
select * from that_nasty_view where order_number = ?
Run Code Online (Sandbox Code Playgroud)
或者它可以是使用参数而不是 sql 连接的任何其他客户端。
如果窗口函数从视图中移除,它运行得非常快,不管它是否使用参数进行查询。
我的解决方法是删除有问题的函数并在稍后阶段重新应用它。
但是,什么给了?它真的是 SQL Server 2008 处理窗口函数的错误吗?
这似乎是一个长期存在的问题,它不断以一种或另一种形式重新出现,并且仍然存在于 SQL Server 2012 中。
一些讨论它的帖子是
直到(包括 2012)的所有当前 SQL Server 版本都无法将分区组上的过滤器推送到参数化谓词的序列项目之外,除非option(recompile)
使用了 if(如果 2008+)。
recompile
提示的替代方法是重写查询以使用@a1ex07建议的参数化内联 TVF )