我有一种情况,其中一个相当简单的选择语句需要几分钟才能完成;这句话看起来像:
SELECT *
FROM MyView
WHERE MyFunction(Col_1, Col_2, Col_3, Col_4) = 1
AND Col_8 = 20
;
现在,时间被分配(大致)为选择的1/3和MyFunction
的调用的2/3(通过简单地注释函数调用并与完整的选择时间进行比较来测量)。 / p>
现在,仅第二个条件(即Col_8 = 20
)已经减少了记录数量。
我尝试了两次运行相同的查询,首先调用函数并将函数调用为第二个,并且两者都返回相同的值(当然)但也花了相同的时间。
我想知道如果第一个条件已经失败,如何阻止函数的调用,并想到两个选择:
将第一个条件设置为Col_8 = 20
,将第二个条件设置为第一个条件(即如果第一个失败则返回FALSE,否则调用该函数),
将查询构建为选择中的选择。
最好(因为可能的原因!)会有类似于某些编程语言(Ada是第一个弹出的),你可以写下这样的东西:
<condition 1> AND THEN <condition 2>...
答案 0 :(得分:2)
标量函数是问题!
这些操作会强制优化器执行RBAR操作,即表扫描。
更多信息:http://www.sqlservercentral.com/articles/T-SQL/135321/
所以要修复,你需要加入标量函数!你有几个选择...
为了说明这一点,我将使用以下代码作为标量函数的[非常]基本近似来运行这些选项:
CREATE FUNCTION MyFunction (
@a int
, @b int
, @c int
, @d int
)
RETURNS bit
AS
BEGIN
DECLARE @return_value bit = 0;
IF @a + @b + @c + @c > 5
BEGIN
SET @return_value = 1;
END
;
RETURN @return_value
END
;
内联逻辑:
SELECT *
FROM MyView
WHERE Col_8 = 20
AND Col_1 + Col_2 + Col_3 + Col_4 > 5
;
制作TVF:
CREATE FUNCTION MyNewFunction (
@a int
, @b int
, @c int
, @d int
)
RETURNS TABLE
AS
RETURN
SELECT Cast(CASE WHEN @a + @b + @c + @c > 5 THEN 1 ELSE 0 END AS bit) AS return_value
;
然后叫它
SELECT *
FROM MyView
CROSS
APPLY dbo.MyNewFunction(Col_1, Col_2, Col_3, Col_4) AS x
WHERE MyView.Col_8 = 20
AND x.return_value = 1
;
答案 1 :(得分:2)
WHERE
语句中的函数很糟糕,因为必须为每一行执行它们。此外,在这种情况下,索引是不可能的。如果your function is deterministic及其使用的列来自一个表,则可以在视图的基表中使用它来创建持久计算列。该列可以在您的WHERE
语句中使用,也可以编制索引以获得更好的性能:
ALTER TABLE MyBaseTable
ADD ComputedCol AS MyFunction(Col_1, Col_2, Col_3, Col_4) PERSISTED
然后,您可以使用计算列进行选择:
SELECT *
FROM MyView
WHERE ComputedCol = 1 AND Col_8 = 20
答案 2 :(得分:1)
实际上通过使用子选择你不保证任何东西,执行的顺序是未知的。
我有一个不同的建议,不确定它是否会提高性能。使用派生表:
SELECT *
INTO TMP_FOR_SELECT
FROM MyView
WHERE Col_8 = 20;
SELECT * FROM TMP_FOR_SELECT
WHERE MyFunction(Col_1, Col_2, Col_3, Col_4) = 1;
除此之外,我只能在Col_8
上建议一个索引,这将有助于优化器更快地找到结果。
答案 3 :(得分:1)
SELECT *
FROM MyView
WHERE case
when Col_8 = 20
then case
when MyFunction(Col_1, Col_2, Col_3, Col_4) = 1
then 1
end
end = 1
;
如果你有Col_8的索引
SELECT *
FROM MyView
WHERE Col_8 = 20
and case
when Col_8 = 20
then case
when MyFunction(Col_1, Col_2, Col_3, Col_4) = 1
then 1
end
end = 1
;