我创建了以下函数来确定两个日期之间的MAX日期。在函数注释中运行其中一个SELECT语句大约需要00.030到00.050秒。
有更好的表现和清洁方式吗?
/* Returns the greater of two dates.
SELECT dbo.fnMaxDate(NULL , NULL)
SELECT dbo.fnMaxDate('1/1/2011', NULL)
SELECT dbo.fnMaxDate(NULL , '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/2/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/2/2011')
*/
ALTER FUNCTION dbo.fnMaxDate
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS datetime
AS
BEGIN
DECLARE @Result DATETIME
IF @Date1 IS NULL AND @Date2 IS NULL
SET @Result = NULL;
ELSE IF @Date1 IS NULL
SET @Result = @Date2
ELSE IF @Date2 IS NULL
SET @Result = @Date1
ELSE
IF @Date1 >= @Date2
SET @Result = @Date1
ELSE
SET @Result = @Date2
RETURN @Result
END
答案 0 :(得分:5)
ALTER FUNCTION dbo.fnMaxDate
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS datetime
AS
BEGIN
RETURN
CASE
WHEN ISNULL(@Date1, @Date2) > ISNULL(@Date2, @Date1)
THEN ISNULL(@Date1, @Date2)
ELSE ISNULL(@Date2, @Date1)
END
END
答案 1 :(得分:2)
我发现case
结构的运行速度比函数调用快三倍。
declare @d table(d1 date, d2 date);
insert into @d values(null,null)
, ('2/19/2012',null)
, (null,'2/19/2012')
, ('2/1/2012','2/15/2012')
, ('2/1/2012','1/15/2012');
declare @md date
, @i int=1
, @ts datetime2
, @ms1 int
, @ms2 int;
-- function
select @ts=GETDATE();
while @i<10000 begin select @md=dbo.fnMaxDate(d1,d2) from @d; set @i+=1; end
select @ms1=DATEDIFF(ms,@ts,GETDATE());
-- recommended case structure
set @i=1;
select @ts=GETDATE();
while @i<10000 begin select @md=case when ISNULL(d1,d2)<ISNULL(d2,d1) then ISNULL(d2,d1) else ISNULL(d1,d2) end from @d; set @i+=1; end
select @ms2=DATEDIFF(ms,@ts,GETDATE());
select [Function Run Time (ms)]=@ms1, [Case Run Time (ms)]=@ms2
go
结果:
Function Run Time (ms) Case Run Time (ms)
---------------------- ------------------
940 296
答案 2 :(得分:1)
/* Returns the greater of two dates.
SELECT dbo.fnMaxDate(NULL , NULL)
SELECT dbo.fnMaxDate('1/1/2011', NULL)
SELECT dbo.fnMaxDate(NULL , '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/2/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/2/2011')
*/
ALTER FUNCTION dbo.fnMaxDate
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS datetime
AS
BEGIN
DECLARE @Result datetime
SELECT TOP 1 @Result = T.DateValue
FROM
(
SELECT @Date1 DateValue
UNION ALL
SELECT @Date2 DateValue
) T
ORDER BY
T.DateValue DESC
RETURN @Result
END
答案 3 :(得分:1)
我知道这是一个老问题,已经回答了,但是看到已经有一个测试脚本,我无法抗拒:)
我创建的标量函数优于所有其他函数测试here,并且表值函数甚至比标量UDF更快。
TVF = 0.022253313291
SVF = 0.04627526226
/* Returns the greater of two dates.
SELECT dbo.fnMaxDate(NULL , NULL)
SELECT dbo.fnMaxDate('1/1/2011', NULL)
SELECT dbo.fnMaxDate(NULL , '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/2/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/2/2011')
*/
CREATE FUNCTION dbo.svfMaxDate
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS datetime
AS
BEGIN
RETURN coalesce(dateadd(dd, ((datediff(dd, 0, @Date1) + datediff(dd, 0, @Date2)) + abs(datediff(dd, 0, @Date1) - datediff(dd, 0, @Date2))) / 2, 0), @Date1, @Date2)
END
GO
CREATE FUNCTION dbo.tvfMaxDate
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN SELECT [MaxDate] = coalesce(dateadd(dd, ((datediff(dd, 0, @Date1) + datediff(dd, 0, @Date2)) + abs(datediff(dd, 0, @Date1) - datediff(dd, 0, @Date2))) / 2, 0), @Date1, @Date2)
;
GO
我还使用了AMissico的简单测试脚本来测试它,但只是考虑将我的函数版本和CASE语句用作参考点/基线:
Case(ms) svfMaxDate tvfMaxDate
0.01343000 0.03907000 0.01837000
Baseline Case(ms) / This Test Case(ms) = Normalisation Factor
0.01616 / 0.01334000 = 1.21139430
svfMaxDate * Normalisation Factor= Comparable Result
0.03820000 * 1.21139430 = 0.04627526226
tvfMaxDate * Normalisation Factor= Comparable Result
0.01837 * 1.21139430 = 0.022253313291
declare @d table(d1 date, d2 date);
insert into @d values(null,null)
, ('2/19/2012',null)
, (null,'2/19/2012')
, ('2/1/2012','2/15/2012')
, ('2/1/2012','1/15/2012');
declare @md date
, @i int=1
, @ts datetime2
, @ms0 int
, @ms1 int
, @ms2 int
;
-- case structure
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=case when ISNULL(d1,d2)<ISNULL(d2,d1) then ISNULL(d2,d1) else ISNULL(d1,d2) end from @d; set @i+=1; end
select @ms0=DATEDIFF(ms,@ts,GETDATE());
-- svfMaxDate, Arithmetic
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.svfMaxDate(d1,d2) from @d; set @i+=1; end
select @ms1=DATEDIFF(ms,@ts,GETDATE());
-- tvfMaxDate, Arithmetic in TVF with CROSS APPLY
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md = tf.MaxDate from @d cross apply dbo.tvfMaxDate(d1,d2) as tf; set @i+=1; end
select @ms2=DATEDIFF(ms,@ts,GETDATE());
select [Case(ms)]=@ms0/100000.0, fnMaxDate=@ms1/100000.0, tvfMaxDate=@ms2/100000.0
go
TVF的归功于Jeff Moden,以下链接被用作构建它的参考:How to Make Scalar UDFs Run Faster (SQL Spackle)
答案 4 :(得分:0)
根据我的简单性能测试,我将使用原始功能的略微修改版本(见下文)。
IS NULL CASE IS NOT NULL UNION
Case(ms) Empty Func fnMaxDate1 fnMaxDate2 fnMaxDate3 fnMaxDate4
0.01616 0.0446 0.0518 0.04934 0.05036 0.06177
最快的函数方法是CASE语句,但只有大约。 0.003ms最慢的功能是使用“Pittsburgh DBA”的SELECT UNION。我改变了我的函数的顺序,首先测试最常见的结果,这将测试两个参数为IS NOT NULL。这种逻辑变化使性能与CASE功能相当。
因此,为了清晰显示IS NOT NULL功能,我放弃了0.001ms的性能(见下文)。
使用以下脚本:
declare @d table(d1 date, d2 date);
insert into @d values(null,null)
, ('2/19/2012',null)
, (null,'2/19/2012')
, ('2/1/2012','2/15/2012')
, ('2/1/2012','1/15/2012');
declare @md date
, @i int=1
, @ts datetime2
, @ms0 int
, @ms1 int
, @ms3 int
, @ms2 int
, @ms4 int
, @ms5 int
;
-- case structure
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=case when ISNULL(d1,d2)<ISNULL(d2,d1) then ISNULL(d2,d1) else ISNULL(d1,d2) end from @d; set @i+=1; end
select @ms0=DATEDIFF(ms,@ts,GETDATE());
-- fnMaxDate1, IF IS NULL
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.fnMaxDate1(d1,d2) from @d; set @i+=1; end
select @ms1=DATEDIFF(ms,@ts,GETDATE());
-- fnMaxDate2, CASE
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.fnMaxDate2(d1,d2) from @d; set @i+=1; end
select @ms2=DATEDIFF(ms,@ts,GETDATE());
-- fnMaxDate3, IF IS NOT NULL
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.fnMaxDate3(d1,d2) from @d; set @i+=1; end
select @ms3=DATEDIFF(ms,@ts,GETDATE());
-- fnMaxDate4, SELECT UNION
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.fnMaxDate4(d1,d2) from @d; set @i+=1; end
select @ms4=DATEDIFF(ms,@ts,GETDATE());
-- fnMaxDate5, empty function call
set @i=1;
select @ts=GETDATE();
while @i<100000 begin select @md=dbo.fnMaxDate5(d1,d2) from @d; set @i+=1; end
select @ms5=DATEDIFF(ms,@ts,GETDATE());
select [Case(ms)]=@ms0/100000.0, [fnMaxDate5 (empty function call)]=@ms5/100000.0, fnMaxDate1=@ms1/100000.0, fnMaxDate2=@ms2/100000.0, fnMaxDate3 = @ms3/100000.0, fnMaxDate4=@ms4/100000.0
go
最后,这是函数的最终版本:
/* Returns the greater of two dates.
SELECT dbo.fnMaxDate(NULL , NULL)
SELECT dbo.fnMaxDate('1/1/2011', NULL)
SELECT dbo.fnMaxDate(NULL , '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/2/2011', '1/1/2011')
SELECT dbo.fnMaxDate('1/1/2011', '1/2/2011')
*/
ALTER FUNCTION [dbo].[fnMaxDate]
(
@Date1 DATETIME,
@Date2 DATETIME
)
RETURNS DATETIME
AS
BEGIN
DECLARE @Result DATETIME
IF @Date1 IS NOT NULL AND @Date2 IS NOT NULL
IF @Date1 >= @Date2
SET @Result = @Date1
ELSE
SET @Result = @Date2
ELSE IF @Date1 IS NULL
SET @Result = @Date2
ELSE IF @Date2 IS NULL
SET @Result = @Date1
RETURN @Result
END