sqloracleora-00904

How to reuse dynamic columns in an Oracle SQL statement?


I try to reuse some columns that I calculate dynamically in Oracle SQL, something like

SELECT
    A*2 AS P,
    P+5 AS Q
FROM tablename

Where 'tablename' has a column called 'A', but no other colums. This gives me an

ORA-00904: "P": invalid identifier

I know how to work around this by using a subquery like

SELECT P, P+5 AS Q
FROM ( SELECT A*2 AS P FROM tablename )

but I think this is kinda ugly. Furthermore I want to make the query somewhat more complex, e.g. reusing 'Q' as well, and I do not want to create yet another subquery.

Update: The reason I want to store the calculation of 'P' is that I want to make it more complex, and reuse 'P' multiple times. So I do not want to explicitly say 'A*2+5 AS Q', because that would quickly become to cumbersome as 'P' gets more complex.

There must be a good way to do this, any ideas?

Update: I should note that I'm not a DB-admin :(.


Update: A real world example, with a more concrete query. What I would like to do is:

SELECT 
    SL/SQRT(AB) AS ALPHA,
    5*LOG(10,ALPHA) AS B,
    2.5*LOG(10,1-EXP(-5/ALPHA)*(5/ALPHA+1)) AS D
    BS -2.74 + B + D AS BSA
FROM tablename

for now, I've written it out, which works, but is ugly:

SELECT
    SL/SQRT(AB) AS ALPHA,
    5*LOG(10,SL/SQRT(AB)) AS B,
    2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*(5/(SL/SQRT(AB))+1)) AS D
    BS -2.74 + 5*LOG(10,SL/SQRT(AB)) + 2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*((5/(SL/SQRT(AB)))+1)) AS BSA
FROM tablename

I could do all of this after receiving the data, but I thought, let's see how much I can let the database do. Also, I would like to select on 'BSA' as well (which I can do now with this query as a subquery/with-clause).


Update: OK, I think for now I finished with Cade Roux' and Dave Costa's solution. Albeit Pax' and Jens Schauder's solution would look better, but I can't use them since I'm not a DBA. Now I don't know who to mark as the best answer :).

WITH 
  A1 AS ( 
    SELECT A0.*, 
    SL/SQRT(AB) AS ALPHA
    FROM tablename A0
  ),
  A2 AS (
    SELECT A1.*, 
    5*LOG(10,ALPHA) AS B,
    2.5*LOG(10,1-EXP(-5/ALPHA)*((5/ALPHA)+1)) AS D
    FROM A1
  )
SELECT
  ALPHA, B, D, BS,
  BS -2.74 + B + D AS BSA
FROM A2

BTW, in case anyone is interested, SB is the 'surface brightness' of galaxies, for which B and D are correction terms.


Solution

  • We have this same problem in SQL Server (it's an ANSI problem). I believe it is intended to avoid confusing aliasing effects:

    SELECT A * 2 AS A
        ,A * 3 AS B -- This is the original A, not the new A
    FROM whatever
    

    We work around it by stacking up common table expressions:

    WITH A1 AS (
        SELECT A * 2 AS A
        FROM whatever
    )
    ,A2 AS (
        SELECT A1.*
            ,A * 3 AS B
        FROM A1
    )
    ,A3 AS (
        SELECT A2.*
            ,A + B AS X
        FROM A2
    )
    SELECT *
    FROM A3
    

    This is the most readable and maintable and followable version.

    For UPDATEs, there is a deprecated SQL Server workaround using the column_name = notation, where you can reference a column which has been updated previously in the list. But this cannot be used in SELECTs.

    I would hope that some ability to stack expressions (without using a scalar UDF) is added to ANSI SQL at some point in the future.