Reputation: 93694
I am trying to create Multplication aggregate function from the below sql code.
declare @Floats as table (id int,value float)
insert into @Floats values (1,1)
insert into @Floats values (2,3)
insert into @Floats values (3,6)
SELECT *
FROM @Floats a
CROSS apply (SELECT CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * Exp(ABSMult)
ELSE Exp(ABSMult)
END AS mul_value
FROM (SELECT
Sum(Log(Abs(NULLIF(Value, 0)))) AS ABSMult,
Sum(Sign(CASE
WHEN Value < 0 THEN 1
ELSE 0
END)) AS Neg,
Min(Abs(Value)) AS MinVal
FROM @Floats b
WHERE a.id >= b.id) foo) cs
Result
id value mul_value
-- ----- ---------
1 1 1
2 3 3
3 6 18
Here is the idea for function. Since we we need table type to pass table as input to function am creating one below
User defined table type
CREATE TYPE [dbo].[UDT_TEST] AS TABLE( value float )
Function code
CREATE FUNCTION udf_Mul(@values dbo.[UDT_TEST] readonly)
RETURNS FLOAT
AS
BEGIN
DECLARE @mul_value FLOAT
SELECT @mul_value=CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * Exp(ABSMult)
ELSE Exp(ABSMult)
END
FROM (SELECT
--log of +ve row values
Sum(Log(Abs(NULLIF(Value, 0)))) AS ABSMult,
--count of -ve values. Even = +ve result.
Sum(Sign(CASE
WHEN Value < 0 THEN 1
ELSE 0
END)) AS Neg,
--anything * zero = zero
Min(Abs(Value)) AS MinVal
FROM @values) foo
RETURN @mul_value
END
Using the function like this.
SELECT *
FROM @Floats a
CROSS apply (SELECT dbo.udf_mul(value) ast
FROM @Floats b
WHERE a.id >= b.id) cs
Here the problem is the input dbo.udf_mul
expects parameter to be of [dbo].[UDT_TEST]
type but value column is of Float
type.
Error :
Msg 206, Level 16, State 2, Line 7 Operand type clash: float is incompatible with UDT_TEST Is there any way to achieve this?
Upvotes: 1
Views: 72
Reputation: 175596
I would not use scalar function for this type of operation. You end up with poor performance because query optimizer need to run it row-by-row
without any optimalization. More info: SQL Server Functions: The Basics
.
Second with float/decimal and LOG/EXP
you will get approximation errors.
Third to pass data to function you can use XML
like:
CREATE FUNCTION dbo.udf_Mul(@value xml)
RETURNS FLOAT
AS
BEGIN
DECLARE @mul_value FLOAT;
DECLARE @values AS TABLE ([value] float);
INSERT INTO @values([value])
SELECT [value] = t.c.value('(value)[1]', 'float')
FROM @value.nodes('//row') AS t(c);
SELECT @mul_value=CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * Exp(ABSMult)
ELSE Exp(ABSMult)
END
FROM (SELECT
Sum(Log(Abs(NULLIF(Value, 0)))) AS ABSMult,
Sum(Sign(CASE
WHEN Value < 0 THEN 1
ELSE 0
END)) AS Neg,
Min(Abs(Value)) AS MinVal
FROM @values) foo
RETURN @mul_value
END
and calling it:
SELECT *
FROM Floats a
CROSS APPLY (SELECT dbo.udf_mul((SELECT [value]
FROM Floats b
WHERE a.id >= b.id
FOR XML PATH, ROOT('root')))
AS r) as cs(r);
Output:
╔═════╦════════╦════════════════════╗
║ id ║ value ║ r ║
╠═════╬════════╬════════════════════╣
║ 1 ║ 1 ║ 1 ║
║ 2 ║ 3 ║ 3.0000000000000004 ║
║ 3 ║ 6 ║ 17.999999999999996 ║
║ 4 ║ 2 ║ 36 ║
╚═════╩════════╩════════════════════╝
Upvotes: 1