0

How to get the column definitions of a CTE in Oracle?

DESCRIBE DUAL yields the column definition of DUAL. But:

WITH MyTable AS (SELECT * FROM DUAL)
DESCRIBE MyTable;

gives an error: missing SELECT keyword

Stephan Stamm
  • 1,063
  • 10
  • 13
  • 3
    `DESCRIBE` is not a SQL statement - it is a SQL\*Plus command. You can't combine fragments of SQL code with SQL\*Plus commands, like you are trying to do. Now - the example you give is very unnatural. What real-life situation were you trying to address with something along these lines? Let's talk about the problem itself, not the impossible solution you tried. –  Mar 02 '19 at 20:40
  • I want to see the column definitions of of my complex CTEs. Column names and types. I use them to document my CTEs. – Stephan Stamm Mar 02 '19 at 21:04
  • 1
    You can use `DBMS_SQL.DESCRIBE_COLUMNS` as @Tomasz suggested. But then I would not be surprised if it turns out that you don't have rights for `DBMS_SQL`. Maybe this [answer](https://stackoverflow.com/a/46523696/1168212) solves your problem? – Alex Yu Mar 02 '19 at 21:24
  • You are going about it the wrong way. In a Reply to an Answer you state that you don't have permissions to create views. I would understand not having permissions to create tables in a production environment; not being allowed to create views, in a development environment, makes ZERO sense. You are not allowed to do your job properly. Instead of dancing around the issue, you should discuss it with your manager: if your job includes developing code (including CTE's) and writing documentation, you SHOULD be given enough permissions so that you could do the job properly. –  Mar 03 '19 at 00:03
  • Documenting the projection of CTEs? I suppose we have to admire your organisation's thoroughness. But I'm afraid this strikes me as a documentation set which will never be used. Anyway, we can't use DESC on the projection of a WITH clause for exactly the same reason we can't use it on the projection of a regular SELECT clause. – APC Mar 03 '19 at 09:41

2 Answers2

2

code error says, that there are missing select clause after the with block. describe command shows the structure of the TABLE, not the structure of QUERY RESULT. To use describe you should "materialize" your output first.

create table tab1 as (
  with q1 as (select * from tab)
  select * from q1
);
describe tab1;

Or you can use dump() function, but it should we explicitly added for each column.

select dump(col1), dump(col2)
from query;

Some information here:

Get data type of field in select statement in ORACLE

Tomasz
  • 610
  • 4
  • 22
  • 1
    Thank you. But your answer does not help me. I don't have the privileges to create a table (or view) in the database in question. – Stephan Stamm Mar 02 '19 at 20:28
  • 1
    Why would you like to know the query result data types? What they will be used for? As i read there is a possibility to use DBMS_SQL package, but if you can't create new tables, than you probably can't create and run procedures. – Tomasz Mar 02 '19 at 20:28
  • Do you have rights to create table/view in your user namespace? Oracle creates a separated namespace for each user and by default you have privileges to create tables/views there. – Tomasz Mar 02 '19 at 20:33
  • It's a nice idea, to create views in my namespace. But even this isn't allowed. If I could create views, I would not need CTEs for my usages. – Stephan Stamm Mar 02 '19 at 20:43
  • Is there no test or development environment you can work in? – William Robertson Mar 02 '19 at 22:54
  • I marked your answer as accepted, because I like your straightforward approach. My restrictions were not part of the question. Thus it's fine to accept your answer. – Stephan Stamm Oct 01 '19 at 14:29
2

You can find the column names and types of a SQL statement using DBMS_SQL (as Alex Yu suggested).

You cannot only use a CTE. To make the SQL statement valid you must select from that CTE, but that's a trivial change.

declare
    v_sql           varchar2(4000) := 'WITH MyTable AS (SELECT * FROM DUAL) select * from mytable';
    v_cursor_number integer;
    v_column_count  number;
    v_columns       dbms_sql.desc_tab3;
begin
    --Parse statement, get columns.
    v_cursor_number := dbms_sql.open_cursor;
    dbms_sql.parse(v_cursor_number, v_sql, dbms_sql.native);
    dbms_sql.describe_columns3(v_cursor_number, v_column_count, v_columns);

    --Print metadata.
    dbms_output.put_line('Column Name,Column Type');
    for i in 1 .. v_column_count loop
        dbms_output.put_line(v_columns(i).col_name || ',' ||
            --Thanks to APC for providing this list in
            --https://stackoverflow.com/a/12041206/409172:
            case v_columns(i).col_type
                when dbms_types.TYPECODE_DATE then 'DATE'             
                when dbms_types.TYPECODE_NUMBER then 'NUMBER'           
                when dbms_types.TYPECODE_RAW then 'RAW'              
                when dbms_types.TYPECODE_CHAR then 'CHAR'             
                when dbms_types.TYPECODE_VARCHAR2 then 'VARCHAR2'         
                when dbms_types.TYPECODE_VARCHAR then 'VARCHAR'          
                when dbms_types.TYPECODE_MLSLABEL then 'MLSLABEL'         
                when dbms_types.TYPECODE_BLOB then 'BLOB'             
                when dbms_types.TYPECODE_BFILE then 'BFILE'            
                when dbms_types.TYPECODE_CLOB then 'CLOB'              
                when dbms_types.TYPECODE_CFILE then 'CFILE'            
                when dbms_types.TYPECODE_TIMESTAMP then 'TIMESTAMP'        
                when dbms_types.TYPECODE_TIMESTAMP_TZ then 'TIMESTAMP_TZ'     
                when dbms_types.TYPECODE_TIMESTAMP_LTZ then 'TIMESTAMP_LTZ'    
                when dbms_types.TYPECODE_INTERVAL_YM then 'INTERVAL_YM'      
                when dbms_types.TYPECODE_INTERVAL_DS then 'INTERVAL_DS'      
                when dbms_types.TYPECODE_REF then 'REF'              
                when dbms_types.TYPECODE_OBJECT then 'OBJECT'           
                when dbms_types.TYPECODE_VARRAY then 'VARRAY'                       
                when dbms_types.TYPECODE_TABLE then 'TABLE'                        
                when dbms_types.TYPECODE_NAMEDCOLLECTION then 'NAMEDCOLLECTION'  
                when dbms_types.TYPECODE_OPAQUE then 'OPAQUE'                            
                when dbms_types.TYPECODE_NCHAR then 'NCHAR'            
                when dbms_types.TYPECODE_NVARCHAR2 then 'NVARCHAR2'       
                when dbms_types.TYPECODE_NCLOB then 'NCLOB'                  
                when dbms_types.TYPECODE_BFLOAT then 'BFLOAT'           
                when dbms_types.TYPECODE_BDOUBLE then 'BDOUBLE'          
                when dbms_types.TYPECODE_UROWID then 'UROWID'              
            end
        );
    end loop;

    --Close the cursor.
    dbms_sql.close_cursor(v_cursor_number);
end;
/

Results:

Column Name,Column Type
DUMMY,VARCHAR
Jon Heller
  • 34,999
  • 6
  • 74
  • 132
  • As @Tomasz already guessed, I can not use `dbms_sql`. But thank you anyway. – Stephan Stamm Mar 05 '19 at 05:52
  • @StephanStamm You should talk to your DBAs. It's not reasonable for them to expect you to work on this system if they don't give you any privileges and even revoked default public privileges. Someone should be able to run "grant create view to your_user" or "grant execute on dbms_sql to your_user". – Jon Heller Mar 05 '19 at 21:33