Home > Back-end >  SQL query to run on different versions of Oracle DB
SQL query to run on different versions of Oracle DB

Time:03-03

i am trying to modify the the below query to run on all the versions of Oracle Database. The 'LAST_LOGIN' column is not available in Oracle 11g,but the rest of the columns are common in 11g,12c and 19 versions.

select username, profile, account_status,  to_char(ctime,'DD-MON-YYYY HH24:MI:SS') CTIME,
       to_char(ptime,'DD-MON-YYYY HH24:MI:SS') PTIME, DEFAULT_TABLESPACE,
       TEMPORARY_TABLESPACE, EXTERNAL_NAME, AUTHENTICATION_TYPE,
       to_char(LAST_LOGIN,'DD-MON-YYYY HH24:MI:SS') LAST_LOGIN
from dba_users a,
     sys.user$ b
where b.name ( )= a.username

Any help is greatly appreciated.Thanks.

CodePudding user response:

i am trying to modify the the below query to run on all the versions of Oracle Database.

Looking at the underlying source code for the dba_users view, the last_login value is coming from sys.user$, specifially the spare6 column, which has data type DATE. From the way it's being manipulated that is stored as UTC;; last_login converts it to a timestamp (oddly with to_char/to_date rather than cast), uses from_tz to state that's UTC, and converts to the session time zone.

So instead of

to_char(LAST_LOGIN,'DD-MON-YYYY HH24:MI:SS') LAST_LOGIN

you could use

to_char(from_tz(cast(b.spare6 as timestamp), 'UTC')
  at time zone sessiontimezone, 'DD-MON-YYYY HH24:MI:SS') LAST_LOGIN

Or if you're OK with having the value in its original UTC time, rather than your own session time zone, you could just do:

to_char(b.spare6, 'DD-MON-YYYY HH24:MI:SS') LAST_LOGIN_UTC

I don't know when spare6 was added but it was there in 11g; if you really mean "all versions" then there will presumably be some older version where that stops existing and this query won't work either; but I don't know if that's 10g, 9i, or even earlier.


SQL query to run on different versions of Oracle DB

More generally, to have different SQL for different versions you would need to use some form of dynamic SQL, possibly with conditional compilation.

CodePudding user response:

You can use conditional compilation to make a function (or procedure) with different functionality depending on the Oracle version.

For example:

CREATE FUNCTION test_conditional_compilation
RETURN SYS_REFCURSOR
IS
  cur SYS_REFCURSOR;
BEGIN
  OPEN cur FOR
    SELECT 1 AS a,
           $IF DBMS_DB_VERSION.VER_LE_11_2
           $THEN NULL
           $ELSE 2
           $END AS b
    FROM   DUAL;

  RETURN cur;
END;
/

Then:

DECLARE
  cur SYS_REFCURSOR;
  a INT;
  b INT;
BEGIN
  cur := test_conditional_compilation();
  
  LOOP
    FETCH cur INTO a, b;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(a || ', ' || COALESCE(TO_CHAR(b), 'NULL'));
  END LOOP;
END;
/

In Oracle 11g, outputs:

1, NULL

db<>fiddle here

and, in Oracle 21, outputs:

1, 2

db<>fiddle here


For your code, that could be:

CREATE FUNCTION get_details
RETURN SYS_REFCURSOR
IS
  cur SYS_REFCURSOR;
BEGIN
  OPEN cur FOR
    SELECT username,
           profile,
           account_status,
           to_char(ctime,'DD-MON-YYYY HH24:MI:SS') AS CTIME,
           to_char(ptime,'DD-MON-YYYY HH24:MI:SS') AS PTIME,
           DEFAULT_TABLESPACE,
           TEMPORARY_TABLESPACE,
           EXTERNAL_NAME,
           AUTHENTICATION_TYPE,
           $IF DBMS_DB_VERSION.VER_LE_11_2
           $THEN NULL
           $ELSE to_char(LAST_LOGIN,'DD-MON-YYYY HH24:MI:SS')
           $END AS LAST_LOGIN
    FROM   dba_users a
           LEFT OUTER JOIN sys.user$ b
           ON (b.name = a.username);
END;
/

If you just want the query to run and have a NULL value for the missing column in Oracle 11. If you can source the column from somewhere else (as per Alex Poole's answer) then you can use that for Oracle 11 instead and use conditional compilation to change where the column is sourced from.

If you want to use the function in SQL (rather than PL/SQL), you can return a nested table collection type rather than a cursor.

  • Related