4

Most databases have something like a GREATEST function, which can be useful some times. At least these databases don't have such a function:

  • Derby
  • SQL Server
  • Sybase ASE
  • Sybase SQL Anywhere

For SQL Server and Sybase SQL Anywhere, the function can be simulated using subqueries and UNION ALL, as can be seen in this question here. An example:

-- SELECT GREATEST(field1, field2, field3) FROM my_table
SELECT (SELECT MAX(c) FROM 
                     (SELECT my_table.field1 AS c UNION ALL 
                      SELECT my_table.field2      UNION ALL
                      SELECT my_table.field3) T) AS greatest
FROM my_table

But this doesn't work in Sybase ASE. Apparently, the subqueries don't have access to the outer query's my_table reference. The error I get is

The column prefix 'my_table' does not match with a table name or alias name used in the query. Either the table is not specified in the FROM clause or it has a correlation name which must be used instead

Note, this problem does not appear with Sybase SQL Anywhere. Any idea what's wrong here and how I could re-write the query?

I'd like to avoid

  • Stored functions, as I may not have the necessary grants to create them
  • Lengthy CASE expressions, as the expression length of the combined permutation of all comparisons needed with nested CASE expressions is at least O(n^2) when n is the number of parameters for GREATEST
Community
  • 1
  • 1
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509

3 Answers3

2

As I understand it, the logic (ignoring nulls) is

SELECT CASE 
          WHEN field1 >= field2 
               AND field1 >= field3
             THEN field1
          WHEN field2 >= field3
             THEN field2
          ELSE field3
       END AS greatest
  FROM my_table;

...but should only return null when all values are null.


I think this is more how I'd like to be able do things (although, Sybase ASE does not support common table expressions):

WITH my_table
     AS 
     (
      SELECT * 
        FROM (
              VALUES ('A', 1, 2, 3), 
                     ('B', 2, 3, 1), 
                     ('C', 3, 1, 2),
                     ('D', NULL, 2, 3), 
                     ('E', NULL, NULL, 3), 
                     ('F', NULL, 3, NULL), 
                     ('G', 1, NULL, 3), 
                     ('H', 1, 3, NULL), 
                     ('X', NULL, NULL, NULL)
             ) AS T (ID, field1, field2, field3)
     ), 
     T1
     AS
     (
      SELECT ID, field1 AS field_n
        FROM my_table
      UNION
      SELECT ID, field2 AS field_n
        FROM my_table
      UNION
      SELECT ID, field3 AS field_n
        FROM my_table
     )        
SELECT ID, MAX(field_n) AS greatest
  FROM T1
 GROUP 
    BY ID;
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
onedaywhen
  • 55,269
  • 12
  • 100
  • 138
  • Good point mentioning `NULL`. Actually `GREATEST(1, 2, NULL)` should return `NULL`, not 2. Anyway, for small numbers of fields, this probably works best. But for arbitrary numbers, the `CASE` expression might explode... – Lukas Eder Jan 12 '12 at 12:30
  • @LukasEder do you mean arbitrary numbers or arbitrary number of fields? – aF. Jan 12 '12 at 14:35
  • @aF. yes, `GREATEST(1, 2, NULL)` is just an example. – Lukas Eder Jan 12 '12 at 14:46
  • @LukasEder: I was using the postgreSQL document [here](http://www.postgresql.org/docs/8.1/interactive/functions-conditional.html): "NULL values in the list are ignored. The result will be NULL only if all the expressions evaluate to NULL." This is why having SQL Standards is so important ;) – onedaywhen Jan 12 '12 at 15:03
  • Oops! I checked with Oracle on my side. I didn't know there was doubt about the correct behaviour when `NULL` is involved... Well, I'll wait a bit to see if any solution comes up without a `CASE` expression. If not, this is as good as it gets! Thanks! – Lukas Eder Jan 12 '12 at 15:06
  • I came up with an equivalent solution to yours, which is slightly more concise: http://stackoverflow.com/questions/8825866/how-to-simulate-greatest-in-sybase-ase/9045290#9045290. Cheers, Lukas – Lukas Eder Jan 28 '12 at 12:41
  • ++++++++++++1 for SELECT * FROM VALUES(). Only now I know this trick! – Viet May 10 '13 at 03:24
0

In that case, I'll add one extra CASE.

SELECT 
    CASE WHEN field1 IS NOT NULL
          AND field2 IS NOT NULL
          AND field3 IS NOT NULL THEN
       CASE WHEN field1 >= field2 
               AND field1 >= field3
             THEN field1
          WHEN field2 >= field3
             THEN field2
          ELSE field3
       END
    ELSE
        NULL
    END AS greatest
  FROM my_table
aF.
  • 64,980
  • 43
  • 135
  • 198
  • I'd like to simulate the function `GREATEST()` itself for arbitrary use cases. I doubt I should create temporary tables for that... Besides, your examples will select the overall max value from the whole table, i.e. `MAX(GREATEST(field1, field2, field3))`. I'm only interested in `GREATEST(field1, field2, field3)` – Lukas Eder Jan 12 '12 at 12:37
  • @LukasEder you want the greatest from each row? – aF. Jan 12 '12 at 13:32
  • I don't have a particular problem with a particular solution. I want to simulate the `GREATEST()` function generically. I want to be able to do `GROUP BY GREATEST(a, b, c)`, or `ORDER BY GREATEST(a, b, c)`, or `WHERE GREATEST(a, b, c) = LEAST(d, e, f)` etc. Just as I proposed in my question. The proposed expression works in many databases including SQL Server. But not in Sybase ASE (or Derby, or MySQL)... Having said this, no I don't necessarily want the greatest from each row. I want any `GREATEST(a, b, c)` whatever `a, b, c` is. That's what I mean by "simulate" – Lukas Eder Jan 12 '12 at 13:39
  • @LukasEder how do you want to simulate the `greatest function` without making a `function`? The tables that I've created you don't have to do it, although max everything is incorrect. I'll edit my answer after unserstanding what you really want. – aF. Jan 12 '12 at 14:16
  • 1
    I want a `SQL CLAUSE` that I can formally substitute for `GREATEST(a, b, c)` whereever I can put `GREATEST(a, b, c)`. I have posted one option in my question. User **onedaywhen** has posted another... – Lukas Eder Jan 12 '12 at 14:19
0

The following SQL clause is even more concise than onedaywhen's answer, although they're semantically equivalent:

SELECT CASE WHEN field1 >= ALL(field2, field3, field4) THEN field1
            WHEN field2 >= ALL(        field3, field4) THEN field2
            WHEN field3 >= ALL(                field4) THEN field3
                                                       ELSE field4 
       END AS greatest
FROM my_table;

This is finally a rare and yet nice use-case for those quantifiers that hardly anyone uses in SQL. See also this question:

Are SQL ANY and SOME keywords synonyms in all SQL dialects?

Unfortunately, this syntax is not supported in all SQL dialects, as the ALL quantifier ususally expects a <table subquery>

Community
  • 1
  • 1
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • The SQL Standard requires the `ALL` quantifier to have a `` so this must be an extension. Do you know which SQL implementations support this? (SQL Server doesn't support it.)
    – onedaywhen Jan 30 '12 at 13:00
  • @onedaywhen: At least [Postgres](http://www.postgresql.org/docs/9.1/static/functions-comparisons.html#AEN17416) and [Oracle](http://docs.oracle.com/cd/B19306_01/server.102/b14200/conditions002.htm#sthref2733) have some syntax extensions – Lukas Eder Jan 30 '12 at 20:47