简单的SQL UNION ALL非常慢

时间:2017-05-18 18:57:43

标签: sql sql-server-2008

sql语句是两个查询之间的简单联合。每一个都是瞬间的。然而联合所有它们变得慢20倍。任何帮助表示赞赏。

SELECT 
     1
FROM 
     [fnGetEmployeeProfileDoctorsMultiplePeriods](
          @FromPeriodId, 
          @ToPeriodId, 
          @ProfileId, 
          @EmployeeId, 
          @GeographicalAreaId, 
          @DoctorId, 
          @TeamId
     )
UNION ALL   
SELECT 
     1
FROM 
     [fnGetEmployeeProfileOrganizationsMultiplePeriods](
          @FromPeriodId, 
          @ToPeriodId, 
          @ProfileId, 
          @EmployeeId, 
          @GeographicalAreaId, 
          @DoctorId, 
          @TeamId
      )

更新

似乎添加union运算符会导致两个子查询的执行计划发生变化;查询引擎不会简单地连接两个原始计划。使用选项重新编译标志可以修复此问题(或者更确切地说,创建一个更快的计划),那么如何强制它使用重新编译的计划呢?

计划链接:

慢速版

https://www.brentozar.com/pastetheplan/?id=SyZq2Ybbb

带选项(重新编译)的快速版本设置

https://www.brentozar.com/pastetheplan/?id=HJ5bpYbZZ

更新2:

在两个子查询中进行了大量优化后,症状仍然存在,但现在执行计划在添加union运算符时不会更改。此外,如果您以仅从第一个子查询获得结果的方式使用顶级运算符(或者,令人惊讶,只有第二个子查询),结果会立即返回。然而,“越过边界”并请求两者的结果,并加上5秒的罚款。

4 个答案:

答案 0 :(得分:1)

我打赌你的表值函数是多语句表值函数。如果可能,将它们重写为内联表值函数。

内联表值函数与标量和多语句表值函数

  

“如果它不是内联的,那就是垃圾。” - Rob Farley

参考:

答案 1 :(得分:1)

您是否尝试将每个函数插入临时表,然后查询临时表?例如,您将创建临时表,然后为每个函数分别使用插入语句。

CREATE TABLE #Employee
(
Record INT 
) 


INSERT INTO #Employee
    ( Record )

SELECT 1
FROM [fnGetEmployeeProfileDoctorsMultiplePeriods](@FromPeriodId, @ToPeriodId, @ProfileId, @EmployeeId, @GeographicalAreaId, @DoctorId, @TeamId)


INSERT INTO #Employee
    ( Record )

SELECT 1
FROM [fnGetEmployeeProfileOrganizationsMultiplePeriods](@FromPeriodId, @ToPeriodId, @ProfileId, @EmployeeId, @GeographicalAreaId, @DoctorId, @TeamId)


SELECT * 
FROM #Employee

答案 2 :(得分:1)

感谢所有贡献的人。

对此的研究为我打开了庞大的SQL查询优化领域。我的意思是我深入研究了索引视图,sql提示(noexpand,merge / hash join等等)和物化分层表。这是一次忙碌的旅程。

答案 3 :(得分:0)

我遇到了同样的问题。

--Requete 67. Performance cost 6%
SELECT * 
INTO #temp_arbo_of_6 
FROM (
SELECT * FROM #temp_arbo_of_4
UNION ALL
SELECT * FROM #temp_arbo_of_5
) AS tmp;

每个表有60列。占用分析存储过程的全局计时的6%。不应该。

以这种方式解决:

--Requete 67. Performance cost 0%
INSERT INTO #temp_arbo_of_4
SELECT * FROM #temp_arbo_of_5;

,然后使用#temp_arbo_of_4而不是#temp_arbo_of_6。我不需要两者。