SQL Select 下的函数使其非常慢
SQL a Function under Select make it very Slow
我正在执行 SQL select 查询。在[E]字段下,有一个名为“[defaultDB].[dbo].HighFirst”的存储函数,用于判断“high”或“low”先发生。
不幸的是,这个判断函数使查询超级慢。如果我跳过函数(将函数替换为 1),只需 2 秒即可完成查询。但是,如果我使用该功能,则需要 30 多分钟。可不可以不用store函数,直接把判断嵌入到query中?
USE defaultDB;
IF OBJECT_ID (N'[defaultDB].[dbo].[HighFirst]', N'FN') IS NOT NULL
DROP FUNCTION HighFirst;
GO
CREATE FUNCTION HighFirst(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS BIT
AS
BEGIN
Declare @result Bit
IF
(SELECT min([time]) FROM [defaultDB].[dbo].[Table2] WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime and [H] > @high) <= (SELECT min([time]) FROM [defaultDB].[dbo].[Table2] WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime and [L] < @Low)
Set @result = 1
ELSE
Set @result = 0;
Return @result
END;
Select
[Stime],
[Etime],
[target],
[CL],
[WinRate] = count(CASE WHEN [Max] > [target] THEN 1 END) / cast( count(*) as float),
[E] = AVG( Case
WHEN [Max] >= [target] AND [Min]< [CL] THEN IIF ([defaultDB].[dbo].HighFirst([date], [Stime], [Etime], [Target], [CL]) =1 , [Target] ,[CL])
WHEN [Max] >= [target] AND [Min] > [CL] THEN [target] /*HIT*/
WHEN [Max] < [target] AND [Min] < [CL] THEN [CL] /*CL*/
WHEN [Max] < [target] AND [Min] > [CL] THEN [EndReturn] /*both not */
END ) ,
[count] = count(*)
FROM [defaultDB].[dbo].[Table1] M
JOIN (values (0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008) ) as T([target])
on 1 =1
JOIN (values (-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115) ) as C([CL])
on [M].[date] in ( 20120307,20120601,20121109,20130826,20131002,20140117,20140122,20140311,20140529,20140718,20150619,20151014,20151022,20160411,20160516,20160721,20160818,20160909,20170127,20170213,20170921,20171025,20171229,20180116,20180315,20180926,20181022,20181128,20181211,20190104,20190329,20190502,20190521,20190528,20190611,20190627,20190823,20190930,20191104,20191211,20200214,20200318,20200529,20200706,20200828,20201230,20210112,20210305,20210318,20210408,20210525,20210617,20210625)
AND [Stime] >= 133000
Group By [Stime], [Etime], [Target], [CL]
按照函数使用模式,您可以尝试将函数重新定义为 return 值而不是标志,这样您就不需要 IIF 检查了。您可以使用条件聚合来做到这一点。这样 [defaultDB].[dbo].[Table2] 只扫描一次:
CREATE FUNCTION HighFirstVal(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS float
AS
BEGIN
RETURN (SELECT case when
min(case when [H] > @high then [time] end) <= min(case when [L] < @Low then [time] end)
then @high else @low end
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime)
END
或者你可以在查询中直接使用RETURN中的子查询,将params替换成columns后直接查询
我看到了两种优化方法。
第一种稍作改动的方法是将 table2 转换为内存 table,然后将 HighFirst 转换为本机编译函数
另一种方法是使用外部应用而不是函数
SELECT IIF (TargetTime.MinTime <= CLTime.MinTime , [Target] ,[CL])
FROM [defaultDB].[dbo].[Table1] AS M
JOIN(VALUES(0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008)) AS T([target])
ON 1 = 1
JOIN(VALUES(-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115)) AS C([CL])
ON 1 = 1
OUTER APPLY
(
SELECT MIN([time]) AS MinTime
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = M.[DATE]
AND [Start] >= M.Stime
AND [Time] <= M.etime
AND [H] > T.target
) as TargetTime
OUTER APPLY
(
SELECT MIN([time]) AS MinTime
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = M.[DATE]
AND [Start] >= M.Stime
AND [Time] <= M.etime
AND L < C.CL
) as CLTime
WHERE [M].[date] IN(20120307, 20120601, 20121109, 20130826, 20131002, 20140117, 20140122, 20140311, 20140529, 20140718, 20150619, 20151014, 20151022, 20160411, 20160516, 20160721, 20160818, 20160909, 20170127, 20170213, 20170921, 20171025, 20171229, 20180116, 20180315, 20180926, 20181022, 20181128, 20181211, 20190104, 20190329, 20190502, 20190521, 20190528, 20190611, 20190627, 20190823, 20190930, 20191104, 20191211, 20200214, 20200318, 20200529, 20200706, 20200828, 20201230, 20210112, 20210305, 20210318, 20210408, 20210525, 20210617, 20210625)
AND [Stime] >= 133000;
您可以将此函数转换为 内联 Table 值函数, 这可能会执行得更好。
CREATE FUNCTION dbo.HighFirst
(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS TABLE
AS RETURN
SELECT Result =
CASE WHEN MIN(CASE WHEN t2.H > @high THEN t2.[time] END) >
MIN(CASE WHEN t2.L < @Low THEN t2.[time] END)
THEN @High ELSE @Low END
FROM dbo.Table2 t2
WHERE t2.[date] = @Date
AND t2.Start >= @Stime
AND t2.[Time] <= @etime;
GO
您可以像这样与 APPLY
一起使用它:
Select
[Stime],
[Etime],
[target],
[CL],
[WinRate] = count(CASE WHEN [Max] > [target] THEN 1 END) / cast( count(*) as float),
[E] = AVG( Case
WHEN [Max] >= [target] AND [Min]< [CL] THEN h.Result
WHEN [Max] >= [target] AND [Min] > [CL] THEN [target] /*HIT*/
WHEN [Max] < [target] AND [Min] < [CL] THEN [CL] /*CL*/
WHEN [Max] < [target] AND [Min] > [CL] THEN [EndReturn] /*both not */
END ) ,
[count] = count(*)
FROM [defaultDB].[dbo].[Table1] M
CROSS APPLY [defaultDB].[dbo].HighFirst([date], [Stime], [Etime], [Target], [CL]) h
JOIN (values (0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008) ) as T([target])
on 1 =1
JOIN (values (-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115) ) as C([CL])
on [M].[date] in ( 20120307,20120601,20121109,20130826,20131002,20140117,20140122,20140311,20140529,20140718,20150619,20151014,20151022,20160411,20160516,20160721,20160818,20160909,20170127,20170213,20170921,20171025,20171229,20180116,20180315,20180926,20181022,20181128,20181211,20190104,20190329,20190502,20190521,20190528,20190611,20190627,20190823,20190930,20191104,20191211,20200214,20200318,20200529,20200706,20200828,20201230,20210112,20210305,20210318,20210408,20210525,20210617,20210625)
AND [Stime] >= 133000
Group By [Stime], [Etime], [Target], [CL]
我正在执行 SQL select 查询。在[E]字段下,有一个名为“[defaultDB].[dbo].HighFirst”的存储函数,用于判断“high”或“low”先发生。
不幸的是,这个判断函数使查询超级慢。如果我跳过函数(将函数替换为 1),只需 2 秒即可完成查询。但是,如果我使用该功能,则需要 30 多分钟。可不可以不用store函数,直接把判断嵌入到query中?
USE defaultDB;
IF OBJECT_ID (N'[defaultDB].[dbo].[HighFirst]', N'FN') IS NOT NULL
DROP FUNCTION HighFirst;
GO
CREATE FUNCTION HighFirst(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS BIT
AS
BEGIN
Declare @result Bit
IF
(SELECT min([time]) FROM [defaultDB].[dbo].[Table2] WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime and [H] > @high) <= (SELECT min([time]) FROM [defaultDB].[dbo].[Table2] WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime and [L] < @Low)
Set @result = 1
ELSE
Set @result = 0;
Return @result
END;
Select
[Stime],
[Etime],
[target],
[CL],
[WinRate] = count(CASE WHEN [Max] > [target] THEN 1 END) / cast( count(*) as float),
[E] = AVG( Case
WHEN [Max] >= [target] AND [Min]< [CL] THEN IIF ([defaultDB].[dbo].HighFirst([date], [Stime], [Etime], [Target], [CL]) =1 , [Target] ,[CL])
WHEN [Max] >= [target] AND [Min] > [CL] THEN [target] /*HIT*/
WHEN [Max] < [target] AND [Min] < [CL] THEN [CL] /*CL*/
WHEN [Max] < [target] AND [Min] > [CL] THEN [EndReturn] /*both not */
END ) ,
[count] = count(*)
FROM [defaultDB].[dbo].[Table1] M
JOIN (values (0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008) ) as T([target])
on 1 =1
JOIN (values (-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115) ) as C([CL])
on [M].[date] in ( 20120307,20120601,20121109,20130826,20131002,20140117,20140122,20140311,20140529,20140718,20150619,20151014,20151022,20160411,20160516,20160721,20160818,20160909,20170127,20170213,20170921,20171025,20171229,20180116,20180315,20180926,20181022,20181128,20181211,20190104,20190329,20190502,20190521,20190528,20190611,20190627,20190823,20190930,20191104,20191211,20200214,20200318,20200529,20200706,20200828,20201230,20210112,20210305,20210318,20210408,20210525,20210617,20210625)
AND [Stime] >= 133000
Group By [Stime], [Etime], [Target], [CL]
按照函数使用模式,您可以尝试将函数重新定义为 return 值而不是标志,这样您就不需要 IIF 检查了。您可以使用条件聚合来做到这一点。这样 [defaultDB].[dbo].[Table2] 只扫描一次:
CREATE FUNCTION HighFirstVal(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS float
AS
BEGIN
RETURN (SELECT case when
min(case when [H] > @high then [time] end) <= min(case when [L] < @Low then [time] end)
then @high else @low end
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = @Date AND [Start] >= @Stime AND [Time]<= @etime)
END
或者你可以在查询中直接使用RETURN中的子查询,将params替换成columns后直接查询
我看到了两种优化方法。
第一种稍作改动的方法是将 table2 转换为内存 table,然后将 HighFirst 转换为本机编译函数
另一种方法是使用外部应用而不是函数
SELECT IIF (TargetTime.MinTime <= CLTime.MinTime , [Target] ,[CL])
FROM [defaultDB].[dbo].[Table1] AS M
JOIN(VALUES(0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008)) AS T([target])
ON 1 = 1
JOIN(VALUES(-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115)) AS C([CL])
ON 1 = 1
OUTER APPLY
(
SELECT MIN([time]) AS MinTime
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = M.[DATE]
AND [Start] >= M.Stime
AND [Time] <= M.etime
AND [H] > T.target
) as TargetTime
OUTER APPLY
(
SELECT MIN([time]) AS MinTime
FROM [defaultDB].[dbo].[Table2]
WHERE [date] = M.[DATE]
AND [Start] >= M.Stime
AND [Time] <= M.etime
AND L < C.CL
) as CLTime
WHERE [M].[date] IN(20120307, 20120601, 20121109, 20130826, 20131002, 20140117, 20140122, 20140311, 20140529, 20140718, 20150619, 20151014, 20151022, 20160411, 20160516, 20160721, 20160818, 20160909, 20170127, 20170213, 20170921, 20171025, 20171229, 20180116, 20180315, 20180926, 20181022, 20181128, 20181211, 20190104, 20190329, 20190502, 20190521, 20190528, 20190611, 20190627, 20190823, 20190930, 20191104, 20191211, 20200214, 20200318, 20200529, 20200706, 20200828, 20201230, 20210112, 20210305, 20210318, 20210408, 20210525, 20210617, 20210625)
AND [Stime] >= 133000;
您可以将此函数转换为 内联 Table 值函数, 这可能会执行得更好。
CREATE FUNCTION dbo.HighFirst
(@Date INT, @Stime INT, @ETime INT, @High Float, @Low Float)
RETURNS TABLE
AS RETURN
SELECT Result =
CASE WHEN MIN(CASE WHEN t2.H > @high THEN t2.[time] END) >
MIN(CASE WHEN t2.L < @Low THEN t2.[time] END)
THEN @High ELSE @Low END
FROM dbo.Table2 t2
WHERE t2.[date] = @Date
AND t2.Start >= @Stime
AND t2.[Time] <= @etime;
GO
您可以像这样与 APPLY
一起使用它:
Select
[Stime],
[Etime],
[target],
[CL],
[WinRate] = count(CASE WHEN [Max] > [target] THEN 1 END) / cast( count(*) as float),
[E] = AVG( Case
WHEN [Max] >= [target] AND [Min]< [CL] THEN h.Result
WHEN [Max] >= [target] AND [Min] > [CL] THEN [target] /*HIT*/
WHEN [Max] < [target] AND [Min] < [CL] THEN [CL] /*CL*/
WHEN [Max] < [target] AND [Min] > [CL] THEN [EndReturn] /*both not */
END ) ,
[count] = count(*)
FROM [defaultDB].[dbo].[Table1] M
CROSS APPLY [defaultDB].[dbo].HighFirst([date], [Stime], [Etime], [Target], [CL]) h
JOIN (values (0.003), (0.0035), (0.004), (0.0045), (0.005), (0.0055), (0.006), (0.0065), (0.007), (0.0075), (0.008) ) as T([target])
on 1 =1
JOIN (values (-0.003), (-0.0035), (-0.004), (-0.0045), (-0.005), (-0.0055), (-0.006), (-0.0065), (-0.007), (-0.0075), (-0.008), (-0.0085), (-0.009), (-0.0095), (-0.01), (-0.0105), (-0.011), (-0.0115) ) as C([CL])
on [M].[date] in ( 20120307,20120601,20121109,20130826,20131002,20140117,20140122,20140311,20140529,20140718,20150619,20151014,20151022,20160411,20160516,20160721,20160818,20160909,20170127,20170213,20170921,20171025,20171229,20180116,20180315,20180926,20181022,20181128,20181211,20190104,20190329,20190502,20190521,20190528,20190611,20190627,20190823,20190930,20191104,20191211,20200214,20200318,20200529,20200706,20200828,20201230,20210112,20210305,20210318,20210408,20210525,20210617,20210625)
AND [Stime] >= 133000
Group By [Stime], [Etime], [Target], [CL]