'Least value but not NULL in Oracle SQL

I wanted to use function LEAST in my procedure to find the smallest value. The problem is that some of the values might have been NULLs so if I do

select least(NULL,0,1) from dual

The answer I get is NULL, which is probably correct by is not something I am expecting to return. I would like to get the least real non zero value. Any help greatly appreciated.



Solution 1:[1]

If any argument is NULL, you want to take the least of the other argument(s). If all arguments are NULL, you want to return NULL.

I might use something like this for two arguments:

LEAST(NVL(colA,colB), NVL(colB,colA))

It starts getting ugly for >2 arguments though:

LEAST(COALESCE(colA,colB,colC)
     ,COALESCE(colB,colA,colC)
     ,COALESCE(colC,colA,colB))

At which point I'd start considering magic values; but this can be buggy (e.g. what if one of the values legitimately is the magic value?):

SELECT CASE WHEN r = maxv THEN NULL ELSE r END AS result
FROM   (SELECT LEAST(NVL(:colA,maxv)
                    ,NVL(:colB,maxv)
                    ,NVL(:colC,maxv)) AS r, maxv
        FROM   (SELECT 9.999999999999999999999999999999999999999e125
                       AS maxv FROM DUAL));

Solution 2:[2]

Using the three expressions in three orders in three 'coalesce' calls and putting these in the 'least' call will result the least of three, ignoring null unless all three expressions are null.

select least(coalesce(expr1, expr2, expr3), coalesce(expr2, expr3, expr1), coalesce(expr3, expr1, expr2)) from dual

Solution 3:[3]

Lowest value from column 1 to N:

SELECT
    LEAST(COALESCE(Col1, BINARY_DOUBLE_INFINITY),
          COALESCE(Col2, BINARY_DOUBLE_INFINITY),
          ... ,
          COALESCE(ColN, BINARY_DOUBLE_INFINITY)
    )
FROM MY_TABLE

Greatest value from column 1 to N:

SELECT
    GREATEST(
        COALESCE(Col1, -BINARY_DOUBLE_INFINITY),
        COALESCE(Col2, -BINARY_DOUBLE_INFINITY),
        ..., 
        COALESCE(ColN, -BINARY_DOUBLE_INFINITY)
    )
FROM MY_TABLE

To remove the infinity results from the query, just add the where clause checking if all the values are null. You can do that using the coalesce function as well:

WHERE COALESCE(Col1, Col2, ..., ColN) IS NOT NULL

Solution 4:[4]

One way is to define own version of LEAST/GREATEST func:

-- here version for two params
WITH  FUNCTION least_improved(p1 IN NUMBER, p2 IN NUMBER) RETURN NUMBER IS
BEGIN
    IF p1 IS NULL THEN
       RETURN p2;
    ELSIF p2 IS NULL THEN
       RETURN p1;
    ELSE
       RETURN LEAST(p1, p2);
    END IF; 
END;
SELECT least_improved(col1, col2)
FROM table_name;
/

Solution 5:[5]

If you have a lot of columns from which you want the min & max, you can UNPIVOT them into a single column, which then makes it simple; exclude NULLS from your single column & use a MIN() or MAX() to get what you want.

I needed to do this with about 50 date columns (not a great db design) and UNPIVOT seemed to be the most sensible / practical way of doing it

Solution 6:[6]

SELECT least(1, 2, 3, nvl(NULL,4)) FROM dual

Return: 1

SELECT GREATEST(1, 2, 3, nvl(NULL,0)) FROM dual

Return: 3

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Harry
Solution 3 Jon Heller
Solution 4 Lukasz Szozda
Solution 5 user3569339
Solution 6 Caconde