Неопределенное количество аргументов для пользовательских функций SQL? - PullRequest
6 голосов
/ 14 ноября 2011

В Oracle и MySQL как я могу создать функцию, которая принимает неопределенное количество параметров, чтобы ее можно было вызывать как GREATEST (значение1, значение2, ...)?

Сравнение двух значений по определенному стандарту довольно просто, но передача "большего" значения в еще одно сравнение - это то, что мне не кажется работой в SQL.

Спасибо!

Изменить (после комментария Майка ниже): Я ищу решение для сравнения нескольких столбцов. Конкретно, мой вопрос заключается в том, как реализовать GREATEST () в качестве UDF. Следующий код сравнивает три столбца.

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;

По-видимому, это не так хорошо масштабируется. Будет гораздо полезнее иметь общую функцию, которая снова и снова применяет один и тот же метод сравнения к списку значений.

Под SQL я имею в виду любой продукт базы данных SQL, но я предпочитаю решение, которое работает в Oracle или MySQL

Ответы [ 2 ]

2 голосов
/ 17 ноября 2011

В Oracle (экземпляры, поддерживающие отключение)

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

Запрос Oracle не проверен, потому что у меня нет удобного экземпляра Более подробная информация о unpivot находится здесь , и он делает то же самое, что и univivot SQL Server.

MySQL Я не уверен, как это сделать в MySQL, но могу исследовать, когда у меня есть возможность (если кто-то не побьет меня. ;-))

Ниже приведены ответы SQL Server:

Делать это в UDF нехорошо, потому что все входные параметры ТРЕБУЕТСЯ в каждом случае. Если бы вам не удавалось реализовать ее как хранимую процедуру, вы могли бы указать значения по умолчанию для входных параметров и вызывать ее с динамическим числом столбцов. В любом случае вам придется выбрать максимальное количество входных параметров. Вот пример в UDF на 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

Это потребует нового UDF для каждого варианта числа входных параметров ИЛИ создания такого, который может занять большее число, но затем при вызове UDF вам придется либо указать какое-то значение для каждого неиспользуемого параметра (ноль) или укажите по умолчанию.

Альтернативы:

Это дает вам максимальное значение трех столбцов из всей таблицы, и было бы проще иметь динамическое число столбцов:

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

Предполагая, что у вас есть какой-нибудь rowid или другой столбец, который вы хотели бы получить в выводе, чтобы вы могли получить наибольшее значение из указанных столбцов для каждой строки, вы могли бы сделать что-то вроде этого:

SELECT RowID, MAX([Value]) AS Greatest
FROM figures
UNPIVOT
(
    [Value]
    FOR ColumnName IN ([Col_1], [Col_2], [Col_3])
) AS unpvt 
GROUP BY RowID 
0 голосов
/ 18 ноября 2011

Еще один вариант SQL Server (не уверен, насколько хорошо он будет переводиться в MySQL / Oracle).

Я должен был сделать это со списком целочисленных идентификаторов, прежде чем поместить его в список через запятую и передать функции, чтобы получить наибольшее:

CREATE Function [dbo].[GreatestFromList]
(@ListOfValues VARCHAR(8000))

RETURNS INT

AS

BEGIN

DECLARE @ListOfValuesTable TABLE (ValueColumn INT) 

    DECLARE @spot1 SMALLINT, @str1 VARCHAR(8000) 

    WHILE @ListOfValues <> ''  
    BEGIN  
        SET @spot1 = CHARINDEX(',', @ListOfValues)  
        IF @spot1>0  
            BEGIN  
                SET @str1 = LEFT(@ListOfValues, @spot1-1) 
                SET @ListOfValues = RIGHT(@ListOfValues, LEN(@ListOfValues)-@spot1)  
            END  
        ELSE  
            BEGIN  
                SET @str1 = @ListOfValues 
                SET @ListOfValues = ''  
            END  
        INSERT INTO @ListOfValuesTable (ValueColumn) VALUES(convert(int, @str1)) 
    END  

DECLARE @GreatestValue INT

SELECT @GreatestValue = SELECT MAX(ValueColumn) FROM @ListOfValuesTable

RETURN @GreatestValue 

END
...