为啥来自 VIEW 的 SELECT * 比来自同一个 VIEW 的 SELECT(特定列)执行得更快?

Posted

技术标签:

【中文标题】为啥来自 VIEW 的 SELECT * 比来自同一个 VIEW 的 SELECT(特定列)执行得更快?【英文标题】:Why is SELECT * from a VIEW executing faster than a SELECT (specific columns) from that same VIEW?为什么来自 VIEW 的 SELECT * 比来自同一个 VIEW 的 SELECT(特定列)执行得更快? 【发布时间】:2018-02-27 10:19:58 【问题描述】:

我正在使用SQL Server 2012。我有一个名为MyViewVIEW,我有2 个T_SQL 查询从VIEW 中提取数据。

查询 1 非常简单,如下所示:

SELECT * 
FROM MyView
WHERE StayDate >= '2018-03-01'

上述查询在大约 28 秒内执行并返回 151,000 行。

查询 2 如下所示:

SELECT [Col A], [Col B], [Col C], [Col D], [Col E]
FROM MyView
WHERE StayDate >= '2018-03-01'

查询 2 的执行时间不确定。我不得不在大约 8 分钟后取消执行!!

我的问题是我需要将查询 2 用于特定任务。是什么导致了执行时间的这种差异?

VIEW 'MyView' 有几个与其他表的连接和一些内置的CASE 语句。我认为这个问题与VIEW 本身无关,否则这两个查询在执行过程中的行为方式或多或少是相同的。

我该如何处理这个问题?

【问题讨论】:

会不会是缓存结果? 您可以查看每个查询的估计执行计划吗? 您可以查看查询计划并将其粘贴到此处。估计的和实际的。请记住,使用* 可能会导致您的查询不使用索引,即使它们已经到位,请查看blog article。 如果您没有查询某些列,并且优化器能够证明它们不会影响结果(行数和行数都不存在),它将得出不同的 (更轻量级)计划完全忽略这些列。你可能有一个基数估计问题,服务器在提出一个轻量级计划时,认为某些东西会变得非常便宜,而事实证明并非如此,并且完整的计划根本不会尝试使用那个出乎意料的昂贵的东西,因此轻量级计划最终变得更重。可以更新统计信息吗? 如果网速好,如果指定列,连续流所有页面不是比检索部分更快吗? 【参考方案1】:

当您执行查询时,您的代码会传递给 SQL 查询优化器。它创建执行计划并将其缓存到内存池。当您再次执行相同的查询时,它会使用该执行计划并更快地检索信息。有时,如果两个查询以相同的执行计划结束,它们都可以使用内存池中缓存的一个。

由于视图只不过是一个存储的代码(字面意思就是你保存和执行一个 sql 文件),它应该被重复使用。正因为如此,您完全可以期望有一些缓存的 EP 用于视图。

唉,这里不是这样。

【讨论】:

解决办法是什么?索引视图? 如果有视图,为什么还需要执行查询?对我来说,解决方案是只使用视图 - 在选择、连接等中。如果您希望查询快速工作,您可以尝试通过 StayDate 将索引添加到 StayDate 列或分区。【参考方案2】:

您的服务器可能出于某种原因决定采用另一个执行计划。我不知道正确的解决方案,但您可能应该更新统计信息或其他内容。

以下是我遇到类似问题时使用的一些解决方法示例:

SELECT [Col A], [Col B], [Col C], [Col D], [Col E] 
FROM MyView 
WHERE StayDate >= '2018-03-01'
option(recompile)

这将忽略统计数据并即时计算一个新的执行计划,希望不会是慢的一个

SELECT * 
INTO #temp 
FROM MyView
WHERE StayDate >= '2018-03-01'

SELECT [Col A], [Col B], [Col C], [Col D], [Col E] 
FROM #temp 
WHERE StayDate >= '2018-03-01'

也许它会在插入语句中保持快速执行,然后您可以从那里提取数据,我猜这些数据将包含更少的行

【讨论】:

以上是关于为啥来自 VIEW 的 SELECT * 比来自同一个 VIEW 的 SELECT(特定列)执行得更快?的主要内容,如果未能解决你的问题,请参考以下文章

为啥“SELECT DISTINCT a, b FROM...”返回的记录少于“SELECT DISTINCT A + '|' + B 来自...”?

MySQL SELECT 来自 1 个表的结果,但根据另一个表排除结果?

EventLog来自哪里,为啥会出错?

在单个外部 SELECT 查询中使用来自 SELECT 子查询的两个聚合值

为啥 BufferedReader read() 比 readLine() 慢得多?

为啥 view.height 比实际高度大 3 倍?