Loop based on parameter value - sql-server-2008

I need to create a temporary table that is populated based on two parameters:
declare #Start date = '01/01/2015'
declare #End date = '12/31/2015'
The temporary table should have a column that will capture YYYYMM for all the years and month that are between #Start and #End parameter.
Here's what I have so far. I want to stop it at 201412 and then start again at 201501. Instead, this loop keeps going in increment of plus 1 (I do not want to see 201413..so on):
declare #Start date = '01/01/2014'
declare #End date = '12/31/2015'
declare #monthstart as int
declare #monthend as int
declare #increment as int
set #monthstart = (SELECT LEFT(CONVERT(varchar, #Start,112),6))
set #monthend = (SELECT LEFT(CONVERT(varchar, #End,112),6))
create table #datetemp (RelevantYYYYMM int)
insert into #datetemp values (#monthstart)
set #increment = #monthstart
While #increment < #monthend
BEGIN
set #increment = (select Max(RelevantYYYYMM) + 1 from #datetemp)
insert into #datetemp values (#increment)
set #increment = (select Max(RelevantYYYYMM) from #datetemp)
IF (select Max(RelevantYYYYMM) from #datetemp) > #monthend
Break
else
continue
END
select * from #datetemp

You can use tally table and avoid loop:
CREATE TABLE #datetemp (RelevantYYYYMM INT);
DECLARE #Start DATE = '01/01/2015', #End DATE = '12/31/2015';
WITH tally_table AS
(
SELECT TOP 1000 rn = ROW_NUMBER() OVER(ORDER BY name) - 1
FROM master..spt_values
)
INSERT INTO #datetemp(RelevantYYYYMM)
SELECT LEFT(CONVERT(varchar, DATEADD(month, rn, #Start),112),6)
FROM tally_table
WHERE YEAR(DATEADD(month, rn, #Start)) <= YEAR(#End)
AND MONTH(DATEADD(month, rn, #Start)) <= MONTH(#End)
SELECT *
FROM #datetemp;
LiveDemo

Related

Insert multiple entries into table using procedure in MySQL

I have created a procedure in mysql to insert into table as below.
DELIMITER $$
CREATE PROCEDURE createOrderPR ( IN iv_productID INT,
IN iv_orderDate DATE,
IN iv_orderTime TIME,
IN iv_shopID INT,
IN iv_mobileNo varchar(10),
IN iv_quantity smallint,
IN iv_total float(10,2),
IN iv_discount float(10,2),
IN iv_taxable float(10,2),
IN iv_CGST float(10,2),
IN iv_SGST float(10,2)
)
BEGIN
DECLARE availQty smallint default 0;
SELECT stockCount INTO availQty FROM product WHERE productID = iv_productID;
SET availQty = availQty - iv_quantity;
start transaction;
set autocommit =0;
INSERT INTO orders(orderNo,productID,orderDate,orderTime,shopID,mobileNo,quantity,total,discount,taxable,CGST,SGST,orderStatus,deletionMark)
VALUES( null,iv_productID,iv_orderDate,iv_orderTime,iv_shopID,iv_mobileNo,iv_quantity,iv_total,iv_discount,iv_taxable,iv_CGST,iv_SGST,'Open',null);
UPDATE product SET stockCount = availQty WHERE productID = iv_productID;
COMMIT;
SELECT MAX(orderNo) FROM orders WHERE shopID = shopID AND mobileNo = mobileNo;
END
$$
Currently it will only allow single record.now I need to insert multiple records in that case how to define the IN parameter of the procedure. Please suggest.
Solved the issue by using the JSON IN parameter these are the sample code (Note:new column added in the table rest all no change)
DELIMITER $$
CREATE PROCEDURE createOrderMulti ( IN orderJson JSON,
IN length INT )
BEGIN
-- Date Declaration
DECLARE availQty smallint default 0;
DECLARE iv_productID INT;
DECLARE iv_orderDate DATE;
DECLARE iv_orderTime TIME;
DECLARE iv_shopID INT;
DECLARE iv_mobileNo varchar(10);
DECLARE iv_quantity smallint;
DECLARE iv_total float(10,2);
DECLARE iv_discount float(10,2);
DECLARE iv_taxable float(10,2);
DECLARE iv_CGST float(10,2);
DECLARE iv_SGST float(10,2);
DECLARE counter smallint default 0;
DECLARE item INT;
DECLARE orderNo INT DEFAULT null;
start transaction;
set autocommit = 0;
WHILE counter < length DO
-- Extract the JSON value
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].productID')) INTO iv_productID;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].orderDate')) INTO iv_orderDate;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].orderTime')) INTO iv_orderTime;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].shopID')) INTO iv_shopID;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].mobileNo')) INTO iv_mobileNo;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].quantity')) INTO iv_quantity;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].total')) INTO iv_total;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].discount')) INTO iv_discount;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].taxable')) INTO iv_taxable;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].CGST')) INTO iv_CGST;
SELECT JSON_VALUE(orderJson, CONCAT('$[', counter, '].SGST')) INTO iv_SGST;
SELECT stockCount INTO availQty FROM product WHERE productID = iv_productID;
SET availQty = availQty - iv_quantity;
SET item = counter + 1;
INSERT INTO orders(orderNo,item,productID,orderDate,orderTime,shopID,mobileNo,quantity,total,discount,taxable,CGST,SGST,orderStatus,deletionMark)
VALUES( orderNo,item,iv_productID,iv_orderDate,iv_orderTime,iv_shopID,iv_mobileNo,iv_quantity,iv_total,iv_discount,iv_taxable,iv_CGST,iv_SGST,'Open',null);
SET orderNo = LAST_INSERT_ID();
UPDATE product SET stockCount = availQty WHERE productID = iv_productID;
SET counter = counter + 1;
END WHILE;
COMMIT;
--SELECT MAX(orderNo) FROM orders WHERE shopID = shopID AND mobileNo = mobileNo;
SELECT orderNo as orderNo;
END
$$

Stored procedure is too slow in mysql

I have a routine. But it' s too slow. How can I improve the query?
My records: http://www.sqlfiddle.com/#!9/14cceb/1/0
My query:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE example.ssa()
BEGIN
drop table if exists gps_table;
drop table if exists exam_datas;
CREATE TEMPORARY TABLE gps_table(ID int PRIMARY KEY AUTO_INCREMENT,timei
int,
trun_date_time datetime, tadd_meter int, tin_here int null);
insert into gps_table(timei,trun_date_time,tadd_meter,tin_here) select
imei, run_date_time, add_meter, in_here from example_table;
CREATE TEMPORARY TABLE exam_datas(ID int PRIMARY KEY AUTO_INCREMENT,vimei
int, vbas_run_date_time datetime, vbit_run_date_time datetime, vdifff int);
select tin_here from gps_table limit 1 into #onceki_durum;
select count(id) from gps_table into #kayit_sayisi;
set #i = 1;
set #min_mes = 0;
set #max_mes = 0;
set #frst_id = 0;
set #imei = 0;
set #run_date_time = '0000-00-00 00:00:00';
set #run_date_time2 = '0000-00-00 00:00:00';
myloop: WHILE (#i <= #kayit_sayisi) DO
select tin_here from gps_table where id = #i into #in_here_true;
if (#in_here_true = 1) then
select id,trun_date_time, tadd_meter from gps_table where id = #i into #frst_id,#run_date_time2, #min_mes;
select id from gps_table where id > #frst_id and tin_here =0 order by id asc limit 1 INTO #id;
SET #id = #id-1;
select id, timei, trun_date_time, tadd_meter from gps_table
where id = #id and tin_here =1 limit 1 into #i, #imei, #run_date_time, #max_mes;
if(#i-#frst_id>3) then
set #i:=#i+1;
insert into exam_datas(vimei,vbas_run_date_time,vbit_run_date_time,vdifff) Values (#imei, #run_date_time2, #run_date_time, #max_mes-#min_mes);
SELECT * FROM exam_datas;
SET #asd =1;
elseif 1=1 then
set #i:=#i+1;
End if;
ELSEIF 1=1
THEN SET #i:=#i+1;
End if;
IF (#i = #kayit_sayisi)
THEN set #tamam =1; LEAVE myloop;
END IF;
END WHILE myloop;
select DISTINCT * from exam_datas;
drop table if exists exam_datas;
drop table if exists gps_table;
END
I need: id= 6 first true and id= 11 last_true
firs_trure - last_true = 304-290= 14
id=14 first true and id=18 last_true
firs_true - last_true = 332-324= 8
This routine is too slow.
MySql version is 5.7 and There are 2 milions record in the table.
UPDATE:
Query is here. HERE
Thank you #LukStorms
It's possible to get such results in 1 query.
Thus avoiding a WHILE loop over records.
This example works without using window functions. Just using variables inside the query to calculate a rank. Which is then used to get the minimums and maximums of the groups.
select
imei,
min(run_date_time) as start_dt,
max(run_date_time) as stop_dt,
max(add_meter) - min(add_meter) as diff
from
(
select imei, id, run_date_time, add_meter, in_here,
case
when #prev_imei = imei and #prev_ih = in_here then #rnk
when #rnk := #rnk + 1 then #rnk
end as rnk,
#prev_imei := imei as prev_imei,
#prev_ih := in_here as prev_ih
from example_table t
cross join (select #rnk := 0, #prev_ih := null, #prev_imei := null) vars
order by imei, id, run_date_time
) q
where in_here = 1
group by imei, rnk
having count(*) > 4
order by imei, min(id);
In the procedure such query can be used to fill that final temporary table.
A test on db<>fiddle here

SQL loop insert using declared table

I have a declared table which has a lot of data in it... I get the data by doing a select query on my InventoryLogs.. now, what I want is to insert this data on another table called MonthlySalesHistoryDetail... However I don't know how to get the values of my declared table...
this is a stored procedure:
Alter Procedure InsertMonthlySalesHistoryEndDate
#CurrentDate date,
#CreatedByID int,
#LastInsertID int
as
Declare #details table
(
RowID int identity(1,1) primary key,
MonthlySalesHistoryID int,
ItemID int,
MeasurementUnitID int,
QuantitySold numeric(18,4),
QuantityReturned numeric(18,4),
TotalSoldAmount numeric(18,4),
TotalReturnedAmount numeric(18,4)
)
Insert Into #details
(
MonthlySalesHistoryID,
ItemID,
MeasurementUnitID,
QuantitySold,
QuantityReturned,
TotalSoldAmount,
TotalReturnedAmount
)
SELECT
#LastInsertID,
il.ItemID,
il.MeasurementUnitID,
SUM(il.Quantity) as QuantitySold,
ISNULL((SELECT SUM(Quantity) FROM InventoryLogs WHERE TransactionType = 15 AND CAST(InventoryLogDate as date) = #CurrentDate),0) as QuantityReturned,
SUM(il.ComputedCost) as TotalSoldAmount,
ISNULL((SELECT SUM(ComputedCost) FROM InventoryLogs WHERE TransactionType = 15 AND CAST(InventoryLogDate as date) = #CurrentDate),0) as TotalReturnedAmount
FROM InventoryLogs il
WHERE il.TransactionType = 9 AND CAST(InventoryLogDate as date) = #CurrentDate
GROUP BY il.ItemID, il.MeasurementUnitID
declare #count int = (SELECT COUNT(*) FROM #details)
declare #counter int = 0
WHILE(#count > #counter)
BEGIN
SET #counter = #counter + 1
SELECT * FROM #details d Where d.RowID = #counter
INSERT INTO MonthlySalesHistoryDetails
(
MonthlySalesHistoryID,
ItemID,
MeasurementUnitID,
QuantitySold,
QuantityReturned,
TotalSoldAmount,
TotalReturnedAmount
)
VALUES
(
//I want to get the values of my
//SELECT * FROM #details d Where d.RowID = #counter here..
)
END
thanks in advance....
I didn't know that it was possible to do this insert... I thought it was only good for declared table
INSERT INTO MonthlySalesHistoryDetails
(
MonthlySalesHistoryID,
ItemID,
MeasurementUnitID,
QuantitySold,
QuantityReturned,
TotalSoldAmount,
TotalReturnedAmount
)
SELECT
d.MonthlySalesHistoryID,
d.ItemID,
d.MeasurementUnitID,
d.QuantitySold,
d.QuantityReturned,
d.TotalSoldAmount,
d.TotalReturnedAmount
FROM #details d Where d.RowID = #counter

Rewrite Stored Procedure

I have the next stored procedure which inserts values into 2 tables. To the 2nd table I insert id's of 2 last inserts from 1st table
However, I would like to rewrite it with one query instead of using temp table and while.
CREATE PROCEDURE CurrencyExhange
AS
DECLARE #TmpTable Table
(
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
BillID int,
Amount decimal,
Rate decimal,
Date date
)
INSERT INTO #TmpTable
SELECT T.[BillID]
,[Amount]
,CR.Rate
,CR.Date
FROM [FinanceLabkovich].[dbo].[Transactions] T
JOIN [FinanceLabkovich].[dbo].Bills B ON B.BillID = T.BillID
JOIN [FinanceLabkovich].[dbo].Currencies C ON C.CurrencyID=B.CurrencyID
JOIN [FinanceLabkovich].[dbo].CurrencyRates CR ON CR.CurrencyRateID=FinanceLabkovich.dbo.GetRate(T.Date)
WHERE LOWER(C.Name)='usd' AND T.Income=1
ORDER BY T.Date
DECLARE #ToBillID int = (SELECT BillID FROM [FinanceLabkovich].[dbo].Bills B WHERE B.Name='Purse')
DECLARE #i int = (SELECT MIN(Id) FROM #TmpTable)
DECLARE #maxId int = (SELECT MAX(Id) FROM #TmpTable)
DECLARE #TransactionID int, #ToTransactionID int, #Amount decimal
DECLARE #date date
WHILE (#i<=#maxId)
BEGIN
SET #date = (SELECT Date FROM #TmpTable WHERE ID=#i)
SET #Amount = (SELECT AmountUSD FROM [FinanceLabkovich].[dbo].Cashflow WHERE Date=#date)
IF #Amount > 0
BEGIN
INSERT INTO [FinanceLabkovich].[dbo].[Transactions] (Name,BillID,ToBillID,Amount,Date,Income)
SELECT "Name"='Currency exhange'
,BillID
,#ToBillID
,#Amount
,T.Date
,"Income"=0
FROM #TmpTable T
WHERE ID=#i
SET #TransactionID = ##IDENTITY
INSERT INTO [FinanceLabkovich].[dbo].[Transactions] (Name,BillID,ToBillID,Amount,Date,Income)
SELECT "Name"='Currency exhange'
,#ToBillID
,BillID
,#Amount*Rate AS Total
,Date
,"Income"=1
FROM #TmpTable WHERE ID=#i
SET #ToTransactionID = ##IDENTITY
INSERT INTO [FinanceLabkovich].[dbo].[Transfers]
SELECT #TransactionID, #ToTransactionID
END
SET #i += 1
END
Any help appreciated.

Better Min/Max Function for Dates in T-SQL

I created the following function to determine the MAX date between two dates. It takes roughly from 00.030 to 00.050 seconds to run one of the SELECT statements in the function's comment.
Is there a better performing and cleaner way?
/* 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
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
I found that a case structure runs about three times faster than a function call.
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
Result:
Function Run Time (ms) Case Run Time (ms)
---------------------- ------------------
940 296
/* 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
I know this is an old question, and already answered, but seeing as there was already a test script I couldn't resist having a go :)
I've created a scalar function that outperforms all the other function tests here, and a table valued function that is even faster than the scalar UDF.
Comparable Result (to AMissico's test)
TVF = 0.022253313291
SVF = 0.04627526226
The Functions
/* 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
I also used AMissico's simple test script to test this, but only bothered to test my version of the functions and the CASE statement to be used as a reference point/baseline:
Results of Test
Case(ms) svfMaxDate tvfMaxDate
0.01343000 0.03907000 0.01837000
Normalise Test Results to AMissico's Baseline
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
Test Script
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
The TVF credit goes to Jeff Moden, the following link was used as a reference for building it: How to Make Scalar UDFs Run Faster (SQL Spackle)
Based on my simple performance testing, I am going with a slightly modified version of my original function (see below).
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
The fastest function method is the CASE statement, but only by approx. 0.003ms The slowest function is the one that uses SELECT UNION by "Pittsburgh DBA". I changed the order of my function to test for the most common result first, which would test both arguments as IS NOT NULL. This change in logic place the performance on par with the CASE function.
Therefore, I am giving up the 0.001ms performance for clarity of the IS NOT NULL function (see below).
Using the following script:
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
Lastly, here is the final version of the function:
/* 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