Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indefinite number of arguments for SQL User Defined Functions?

In Oracle and MySQL, how can I create a function that takes an indefinite number of parameters so that it may be called like GREATEST(value1,value2,...)?

Comparing two values by a certain standard is pretty easy but passing the "greater" value to yet another comparison is what I don't seem to get work in SQL.

Thanks!

Edit (after Mike's comment below): I am looking for a solution for comparing multiple columns. In concrete terms, my question is how to implement GREATEST() as a UDF. The following code compares three columns.

SELECT CASE WHEN CASE WHEN col_1 < col_2 THEN col_2
                 ELSE col_1 END < col_3 THEN col_3
       ELSE CASE WHEN col_1 < col_2 THEN col_2
                 ELSE col_1 END END AS greatest
  FROM figures;

Apparently, this does not scale so well. It'll be much more useful to have a general function that applies the same comparison method over and over to a list of values.

By SQL I mean any SQL database product but I prefer a solution that works in Oracle or MySQL

like image 210
amemus Avatar asked Nov 14 '11 10:11

amemus


1 Answers

In Oracle (instances that support unpivot)

SELECT MyID, MAX(GreatestVal) 
FROM figures
  UNPIVOT (
          GreatestVal 
          FOR MyID 
          IN (col_1, col_2, col_3,...)
          );

Oracle query is untested because I don't have an instance handy Further details on unpivot are located here and it does the same thing as the SQL Server unpivot.

MySQL I'm unsure of how to do this in MySQL but may investigate as I have opportunity (unless someone beats me to it. ;-) )

The following are SQL Server answers:

Doing it in a UDF is not pretty because all of the input parameters would be REQUIRED in every case. If you could get away with implementing it as a stored procedure then you could specify default values for the input parameters and call it with a dynamic number of columns. Either way, you'll have to decide on a maximum number of input parameters. Here is an example in UDF on SQL Server:

SELECT dbo.GREATESTof3(col_1, col_2, col_3)
FROM figures;

CREATE FUNCTION GREATESTof3(@col_1 sql_variant = null, @col_2 sql_variant = null, @col_3 sql_variant = null)
RETURNS sql_variant
AS 
BEGIN
    DECLARE @GreatestVal sql_variant
    DECLARE @ColumnVals TABLE (Candidate sql_variant)


    INSERT INTO @ColumnVals
    SELECT @col_1
    UNION ALL
    SELECT @col_2
    UNION ALL
    SELECT @col_3

    SELECT @GreatestVal = MAX(Candidate)
    FROM @ColumnVals

    RETURN @GreatestVal
END

This would require a new UDF for each variant of the number of input parameters OR creating one that could take a greater number but then in the call to the UDF you would have to either specify some value for each unused parameter (null) or specify default.

Alternatives:

This gives you the max value of the three columns from the whole table and would be easier to have a dynamic number of columns:

SELECT MAX([Value]) AS Greatest
FROM figures
UNPIVOT
(
    [Value]
    FOR ColumnName IN ([Col_1], [Col_2], [Col_3])
) AS unpvt 

Assuming you have some rowid or another column that you would want in the output so that you could get the greatest from the specified columns for each row you could do something like this:

SELECT RowID, MAX([Value]) AS Greatest
FROM figures
UNPIVOT
(
    [Value]
    FOR ColumnName IN ([Col_1], [Col_2], [Col_3])
) AS unpvt 
GROUP BY RowID 
like image 171
Chris Townsend Avatar answered Oct 20 '22 01:10

Chris Townsend