How to Unpivot with dynamic columns Oracle - oracle

I need to unpiviot a table that I don't have control over the columns, so i need to dynamically get the column names: This is what I have
CREATE TABLE test
(
PK VARCHAR2(255 CHAR),
COL1 VARCHAR2(255 CHAR),
COL2 VARCHAR2(255 CHAR),
COL3 VARCHAR2(255 CHAR),
COL4 VARCHAR2(255 CHAR),
COL5 VARCHAR2(255 CHAR),
COL6 NUMBER,
)
declare
sql_stmt clob;
pivot_clause clob;
begin
select listagg('''' || column_name || ''' as "' || column_name || '"', ',') within group (order by column_name)
into pivot_clause
FROM USER_TAB_COLUMNS
WHERE table_name = 'test');
sql_stmt := 'SELECT PK,
VarName,
Valuer,
Max(timestamp) over (Partition by PK) as timestamp,
FROM test
UNPIVOT(Valuer FOR VarName IN (' || pivot_clause || '))';
execute immediate sql_stmt;
end;
Which returns:
Fehlerbericht -
ORA-00904: : invalid identifier
ORA-06512: at line 23
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Expected out put would be something like:
PK|VARNAME|Valuer
1 |Col1 | value1
1 |Col2 | value2
1 |Col3 | value3
1 |Col4 | value4
1 |Col5 | value5
1 |Col6 | 12345
2 |Col1 | value1
2 |Col2 | value2
2 |Col3 | value3
2 |Col4 | value4
2 |Col5 | value5
2 |Col6 | 12345
Which is the same error i get if i just toss the sub select right into the IN()
Thanks

You can use dbms_output.put_line(sql_stmt) to see the actual dynamic SQL being generated, which in this case is:
SELECT PK,
VarName,
Valuer,
Max(timestamp) over (Partition by PK) as timestamp,
FROM test
UNPIVOT(Valuer FOR VarName IN ('COL1' as "COL1",'COL2' as "COL2",'COL3' as "COL3",'COL4' as "COL4",'COL5' as "COL5",'COL6' as "COL6",'PK' as "PK",'TIMESTAMP' as "TIMESTAMP"))
Which has a number of issues. Generally for this sort of thing it's sensible to start from a static statement you know works and then figure out how to make it dynamic, but you don't seem to have done that here.
This version gets ORA-00936: missing expression because of the trailing comma after the timestamp in the dynamic statement. Which I imagine is another error from modifying your code for posting. Without that it gets the ORA-00904: : invalid identifier you have in your question. The immediate cause of that error you're getting is the parts in the parentheses, such as:
'COL1' as "COL1"
The quotes are wrong; that should be:
COL1 as 'COL1'
Fixing that then gives ORA-00904: "PK": invalid identifier because your column look-up isn't excluding the columns you don't want to pivot on, so really you want:
select listagg(column_name || ' as ''' || column_name || '''', ',')
within group (order by column_name)
into pivot_clause
from user_tab_columns
where table_name = 'TEST'
and column_name not in ('PK', 'TIMESTAMP');
Your next problem is that the columns you're unpivoting are different data types, so you get ORA-01790: expression must have same datatype as corresponding expression. That's a bit trickier - essentially you'll have to convert everything to strings, using suitable formats, particularly if there are dates involved. You need to do that conversion in a subquery, and you unpivot the result of that. An example that just explicitly handles the number column, generating the subquery columns in the same way as the unpivot clause:
declare
sql_stmt clob;
subquery clob;
pivot_clause clob;
begin
select listagg(column_name || ' as ''' || column_name || '''', ',')
within group (order by column_name),
listagg(
case when data_type = 'NUMBER' then 'to_char(' || column_name || ')'
else column_name
end || ' as ' || column_name, ',')
within group (order by column_name)
into pivot_clause, subquery
from user_tab_columns
where table_name = 'TEST'
and column_name not in ('PK', 'TIMESTAMP');
sql_stmt := 'select pk,
varname,
valuer,
max(timestamp) over (partition by pk) as timestamp
from (select pk, timestamp, ' || subquery || ' from test)
unpivot(valuer for varname in (' || pivot_clause || '))';
dbms_output.put_line(sql_stmt);
execute immediate sql_stmt;
end;
/
You could instead always cast every column to say varchar2(255) in the second listagg(), but then yuo have no direct control over formatting and are reliant on NLS settints.
When run the block above now generates:
select pk,
varname,
valuer,
max(timestamp) over (partition by pk) as timestamp
from (select pk, timestamp, COL1 as COL1,COL2 as COL2,COL3 as COL3,COL4 as COL4,COL5 as COL5,to_char(COL6) as COL6 from test)
unpivot(valuer for varname in (COL1 as 'COL1',COL2 as 'COL2',COL3 as 'COL3',COL4 as 'COL4',COL5 as 'COL5',COL6 as 'COL6'))
and when run manually that gets output like:
PK VARNAME VALUER TIMESTAMP
1 COL1 value1 11-AUG-17 15.49.42.239283000
1 COL2 value2 11-AUG-17 15.49.42.239283000
1 COL3 value3 11-AUG-17 15.49.42.239283000
1 COL4 value4 11-AUG-17 15.49.42.239283000
1 COL5 value5 11-AUG-17 15.49.42.239283000
1 COL6 12345 11-AUG-17 15.49.42.239283000
2 COL1 value6 11-AUG-17 15.49.42.340387000
2 COL2 value7 11-AUG-17 15.49.42.340387000
2 COL3 value8 11-AUG-17 15.49.42.340387000
2 COL4 value9 11-AUG-17 15.49.42.340387000
2 COL5 value10 11-AUG-17 15.49.42.340387000
2 COL6 23456 11-AUG-17 15.49.42.340387000
(I set up dummy data with different values in the unpivoted columns, and just used systimestamp to get the timestamp column value).
When you run it dynamically it doesn't really do anything - it's parsed but not fully executed because you aren't executing immediate into anything.
My plan was to use this in a view
You can make your anonymous block generate the view dynamically, as a one-off event:
sql_stmt := 'create or replace view your_view_name as
select pk,
varname,
valuer,
max(timestamp) over (partition by pk) as timestamp
from (select pk, timestamp, ' || subquery || ' from test)
unpivot(valuer for varname in (' || pivot_clause || '))';
execute immediate sql_stmt;
and you can then just select from your_view_name.
Whenever a new column is added to the table (which is hopefully rare and under change control - but then you wouldn't really need to do this dynamically) you can just re-run the block to recreate the view.

Related

Dynamic SQL in Informatica

I have a mapping that is executing dynamic sql via a SQL transformation. The dynamic sql is stored in an Oracle table and I need to add a string parameter to this query.
In this example query, the values compared to COL4 and COL5 are output correctly in the log, but the value compared to COL6 is output as '$$STRING_PARA' in the query -
'SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = ''Y''
AND COL6 = ''||$$STRING_PARA||'' '
$$STRING_PARA is being output correctly at the beginning of the log. I have tried omitting the pipes and increasing the number of quotes but nothing seems to work.
Has anyone done something similar?
I guess you want to run the query by replacing the Informatica input variables with the values of the mapping. It might work like this
' SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = ''Y''
AND COL6 = ''$$STRING_PARA'' '
In PL/SQL
SQL> set serveroutput on size unlimited echo on lines 200
SQL> #test.sql
SQL> declare
2 v_query varchar2(4000) := ' SELECT COL1, COL2, COL3 FROM TABLE
3 WHERE COL4 = $$NUMERIC_PARA
4 AND COL5 = ''Y''
5 AND COL6 = ''$$STRING_PARA'' ' ;
6 begin
7 dbms_output.put_line(v_query);
8 end;
9 /
SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = 'Y'
AND COL6 = '$$STRING_PARA'
PL/SQL procedure successfully completed.
Runtime replacement and execution by replacing input variables
SQL> select object_id, object_name from dba_objects where object_name = 'MY_TEST' ;
OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
8897475 MY_TEST
SQL> #test.sql 8897475 MY_TEST
SQL> declare
2 v_query varchar2(4000) := ' SELECT count(*) from dba_objects
3 WHERE object_id = &1
4 AND object_name = ''&2'' ' ;
5 v_counter pls_integer;
6 begin
7 dbms_output.put_line(v_query);
8 execute immediate v_query into v_counter;
9 dbms_output.put_line('Counter is '||v_counter||' ');
10 end;
11 /
old 3: WHERE object_id = &1
new 3: WHERE object_id = 8897475
old 4: AND object_name = ''&2'' ' ;
new 4: AND object_name = ''MY_TEST'' ' ;
SELECT count(*) from dba_objects
WHERE object_id = 8897475
AND object_name = 'MY_TEST'
Counter is 1
PL/SQL procedure successfully completed.
SQL>
It should work the same way, as the escaping for the single quote should be the same.
Thanks for the suggestions - ''$$STRING_PARA'' worked.
I couldn't see that at the time as there was another issue in the mapping that prevented it from working correctly.

Alternative to evaluating a string using a function

I'm trying to output a list of records but some may not have a value in the subject column.
I have an altSubject column that would specify what to output instead.
simplified for example
insert all
into myTable
(id, subject, altSubject, partNumber, serialNumber, startDate, endDate)
values
(1, 'test',null,'xyz','123','1/1/2019', '1/5/2019')
into myTable
(id, subject, altSubject, partNumber, serialNumber, startDate, endDate)
values
(2, null, '''SN: '' || serialNumber','abc','789','1/1/2019', '1/5/2019')
output should look like:
subject | Part Number | Start Date | End Date
test | xyz | 1/1/2019 | 1/5/2019
SN: 789 | abc | 1/1/2019 | 1/5/2019
I've been able to do this using a case with a function below but the problem I'm having is it takes 5 minutes to run on a 40k row table.
select
...
...
case when altSubject is not null then
fAltSubject(id,altSubject)
else
subject
end subject
from
myTable
where
status = 'closed'
the function:
create or replace function fAltSubject
(pID in number
, pAltSubject in varchar2)
return varchar2
as
vNewSubject varchar2(400) := '';
begin
vSql := 'select ' ||
pAltSubject ||
' from
myTable
where
id = ' || pID;
execute immediate vSql
into
vNewSubject;
return vNewSubject;
end faltsubject;
Is there a better way to do this that doesn't take 5 minutes?
Thanks in advance.
"how to use a user defined mask in a column when the mask could be a combination of fields and text".
This is the best I can do and get good performance.
The table defines Subject, AltSubject and DisplaySubject.
A trigger sets the DisplaySubject based on the other two fields.
The trigger must reference the specific column names, so it needs to be regenerated every time columns are added. Maybe a nightly job?
create table myTable (
id number,
subject varchar2(64),
altSubject varchar2(128),
displaySubject varchar2(128),
partNumber varchar2(16),
serialNumber varchar2(16),
startDate date,
endDate date
);
create or replace procedure generate_mytable_trigger is
l_newline constant varchar2(1) := chr(10);
l_text clob := to_clob(
'create or replace trigger mytable_displaysubject
before insert or update on mytable
for each row
declare
lt_column_names sys.odcivarchar2list;
begin
if :new.subject is not null then
:new.altsubject := null;
:new.displaysubject := :new.subject;
return;
end if;
:new.displaysubject := :new.altsubject;
-- start lines to be generated');
l_end_text constant varchar2(4000) :=
'-- end lines to be generated
return;
end mytable_displaysubject;';
begin
for rec in (
select l_newline ||
':new.displaysubject := replace(:new.displaysubject, ''#'||column_name||'#'', :new.'||column_name||');'
as text
from user_tab_columns where table_name = 'MYTABLE'
and column_name not in ('SUBJECT','ALTSUBJECT','DISPLAYSUBJECT')
) loop
l_text := l_text || rec.text;
end loop;
l_text := l_text || l_newline || l_end_text;
execute immediate l_text;
end;
/
exec generate_mytable_trigger;
Now a little test:
insert into mytable(id, subject, altsubject, partnumber, serialnumber, startdate, enddate)
select 1, 'test',null,'xyz','123',sysdate, sysdate+1 from dual
union all
select 2, null,'PN: #PARTNUMBER#','abc','789',sysdate, sysdate+1 from dual
union all
select 3, null,'PN: #PARTNUMBER#, SN: #SERIALNUMBER#','qsdf','789',sysdate, sysdate+1 from dual
union all
select 3, null,'PN: #PARTNUMBER#, ??: #BADCOLUMN#','qsdf','789',sysdate, sysdate+1 from dual;
commit;
select subject, altsubject, displaysubject from mytable;
SUBJECT ALTSUBJECT DISPLAYSUBJECT
test test
PN: #PARTNUMBER# PN: abc
PN: #PARTNUMBER#, SN: #SERIALNUMBER# PN: qsdf, SN: 789
PN: #PARTNUMBER#, ??: #BADCOLUMN# PN: qsdf, ??: #BADCOLUMN#
You have a recent version of Oracle: congratulations. So use it: virtual columns!
create table myTable (
id number,
subject varchar2(32),
altSubject varchar2(64)
generated always as (case when subject is null then 'SN: '||serialnumber end),
partNumber varchar2(16),
serialNumber varchar2(16),
startDate date,
endDate date
);
insert into mytable(id, subject, partnumber, serialnumber, startdate, enddate)
select 1, 'test','xyz','123',sysdate, sysdate+1 from dual
union all
select 2, null,'abc','789',sysdate, sysdate+1 from dual;
select ID, coalesce(SUBJECT, ALTSUBJECT) subject,
PARTNUMBER, SERIALNUMBER, STARTDATE, ENDDATE
from mytable;
ID SUBJECT PARTNUMBER SERIALNUMBER STARTDATE ENDDATE
-- -------- ----------- ------------- ------------------- -------------------
1 test xyz 123 2019-12-18 13:09:10 2019-12-19 13:09:10
2 SN: 789 abc 789 2019-12-18 13:09:10 2019-12-19 13:09:10
That may very well be overkill. You could always get rid of the extra column and say:
select ID, coalesce(SUBJECT, 'SN: '||serialnumber) subject,
PARTNUMBER, SERIALNUMBER, STARTDATE, ENDDATE
from mytable;
Best regards,
Stew Ashton

How to include column names along with rows dynamically in Oracle

I am trying to insert the column header and corresponding field into another table .
Table1 :
col1 col2 col3 col4
1 2 3 4
output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Table1 will be created dynamically , so structure of Table 1 might vary ,for eg .
sometime ,It might be
Case1:
col1 col2 col3 col4
1 2 3 4
For Case1 ,output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Or it might be
Case2:
col1 col2
1 2
For Case2 ,output should look like this:
COL_A COL_B COL_C COL_D
col1 1 col2 2
I got help with the static query for similar requirement in another question in Stack Overflow,but I was trying to convert the static query to dynamic query.
Static Query :
Static Table structure :
CREATE TABLE Table1
(col1 int, col2 int, col3 int, col4 int)
;
INSERT ALL
INTO Table1 (col1, col2, col3, col4)
VALUES (1, 2, 3, 4)
SELECT * FROM dual
;
Static Query :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
COL1
,COL2
,COL3
,COL4
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
'COL1' as VAL1
,'COL2' as VAL2
,'COL3' as VAL3
,'COL4' as VAL4
));
Output :
VAL1_C VAL1_V VAL2_C VAL2_V VAL3_C VAL3_V VAL4_C VAL4_V
COL1 1 COL2 2 COL3 3 COL4 4
As I tried to make the static query dynamic ,I tried this :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
select regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
select regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
));
But this throws error :
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 6 Column: 5
I tried using dynamic sql .Below is the dynamic sql I used :
declare
sql_stmt varchar2(4000);
pivot_clause1 varchar2(4000);
pivot_clause2 varchar2(4000);
begin
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
into pivot_clause1
FROM (select * from all_tab_columns where table_name = 'TEST11') ;
select * into pivot_clause2 from
(
select AA ||'''' aa
from (SELECT ''''||LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TEST11'))
);
dbms_output.put_line(pivot_clause1);
dbms_output.put_line(pivot_clause2);
sql_stmt := 'create table test13 as ('|| 'SELECT * FROM
(
SELECT *
FROM TEST11
UNPIVOT(val FOR col IN ( '
|| pivot_clause1 || '
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (' ||
pivot_clause2 || '
))
)';
execute immediate sql_stmt ;
end;
/
It is working fine for a table having description as below :
Name Null Type
---- ---- ----------
COL1 NUMBER(38)
COL2 NUMBER(38)
COL3 NUMBER(38)
COL4 NUMBER(38)
But it is not working for a table having description :
COL1 DATE
COL1 VARCHAR2(100)
COL1 VARCHAR2(100)
COL1 NUMBER
COL1 NUMBER
I am getting below error :
Error report -
ORA-01790: expression must have same datatype as corresponding expression
ORA-06512: at line 38
01790. 00000 - "expression must have same datatype as corresponding expression"
*Cause:
*Action:
I just learned unpivot cannot be used with columns having different datatype,Is there any way around ?
Can someone please help !!

If statement inside create table query -Oracle-

I would like to create table which asks user input first. Then based on the input, it select which columns are added.
for example, if the response is 'N', then table is created including columns col1, col2, col3.
If the response is 'Y', table is created including columns col1, col2, col3, col4, col5.
Is this possible?
If yes, please provide me simple and primitive query so that I can apply it to my case.
Thanks,
Using SQL*Plus it's simple:
ACCEPT table_option -
PROMPT 'Create more columns? '
SET TERM OFF
COLUMN extra_columns NEW_VALUE extra_columns
SELECT
CASE '&table_option'
WHEN 'Y' THEN ', C4 NUMBER, C5 VARCHAR2(255), C6 DATE'
END extra_columns FROM DUAL;
CREATE TABLE tmp (
C1 NUMBER,
C2 VARCHAR2(255),
C3 DATE &extra_columns
);
SET TERM ON
You can store the script as a file and invoke it from SQL*Plus using #filename.
CREATE OR REPLACE FUNCTION tmp_custom_DDL( p_input VARCHAR2 IN, p_resp CHAR IN OUT) RETURN CHAR
AS
v_str VARCHAR2(4000);
IF p_resp = 'Y' THEN
v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10)';
ELSE v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10), col4 varchar2(10), col4 varchar2(10) ' ;
EXECUTE IMMEDIATE v_comm_1 || v_str || v_comm2;
--v_comm_1 is the first half of create table command till the specified cols
--v_comm_2 is the rest of the create table command
RETURN p_resp;
END;
this is only a quick draft, fix the few lexical bug and the missing definitions :) (this is the first step)

How do I get column datatype in Oracle with PL-SQL with low privileges?

I have "read only" access to a few tables in an Oracle database. I need to get schema information on some of the columns. I'd like to use something analogous to MS SQL's sp_help.
I see the table I'm interested in listed in this query:
SELECT * FROM ALL_TABLES
When I run this query, Oracle tells me "table not found in schema", and yes the parameters are correct.
SELECT
DBMS_METADATA.GET_DDL('TABLE', 'ITEM_COMMIT_AGG', 'INTAMPS') AS DDL
FROM DUAL;
After using my Oracle universal translator 9000 I've surmised this doesn't work because I don't have sufficient privileges. Given my constraints how can I get the datatype and data length of a column on a table I have read access to with a PL-SQL statement?
ALL_TAB_COLUMNS should be queryable from PL/SQL. DESC is a SQL*Plus command.
SQL> desc all_tab_columns;
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(30)
TABLE_NAME NOT NULL VARCHAR2(30)
COLUMN_NAME NOT NULL VARCHAR2(30)
DATA_TYPE VARCHAR2(106)
DATA_TYPE_MOD VARCHAR2(3)
DATA_TYPE_OWNER VARCHAR2(30)
DATA_LENGTH NOT NULL NUMBER
DATA_PRECISION NUMBER
DATA_SCALE NUMBER
NULLABLE VARCHAR2(1)
COLUMN_ID NUMBER
DEFAULT_LENGTH NUMBER
DATA_DEFAULT LONG
NUM_DISTINCT NUMBER
LOW_VALUE RAW(32)
HIGH_VALUE RAW(32)
DENSITY NUMBER
NUM_NULLS NUMBER
NUM_BUCKETS NUMBER
LAST_ANALYZED DATE
SAMPLE_SIZE NUMBER
CHARACTER_SET_NAME VARCHAR2(44)
CHAR_COL_DECL_LENGTH NUMBER
GLOBAL_STATS VARCHAR2(3)
USER_STATS VARCHAR2(3)
AVG_COL_LEN NUMBER
CHAR_LENGTH NUMBER
CHAR_USED VARCHAR2(1)
V80_FMT_IMAGE VARCHAR2(3)
DATA_UPGRADED VARCHAR2(3)
HISTOGRAM VARCHAR2(15)
You can use the desc command.
desc MY_TABLE
This will give you the column names, whether null is valid, and the datatype (and length if applicable)
The best solution that I've found for such case is
select column_name, data_type||
case
when data_precision is not null and nvl(data_scale,0)>0 then '('||data_precision||','||data_scale||')'
when data_precision is not null and nvl(data_scale,0)=0 then '('||data_precision||')'
when data_precision is null and data_scale is not null then '(*,'||data_scale||')'
when char_length>0 then '('||char_length|| case char_used
when 'B' then ' Byte'
when 'C' then ' Char'
else null
end||')'
end||decode(nullable, 'N', ' NOT NULL')
from user_tab_columns
where table_name = 'TABLE_NAME'
and column_name = 'COLUMN_NAME';
#Aaron Stainback, thank you for correction!
Note: if you are trying to get this information for tables that are in a different SCHEMA use the all_tab_columns view, we have this problem as our Applications use a different SCHEMA for security purposes.
use the following:
EG:
SELECT
data_length
FROM
all_tab_columns
WHERE
upper(table_name) = 'MY_TABLE_NAME' AND upper(column_name) = 'MY_COL_NAME'
select t.data_type
from user_tab_columns t
where t.TABLE_NAME = 'xxx'
and t.COLUMN_NAME='aaa'
Oracle 11.2: Get a list of the full datatype in your table:
create table SOMETABLE (foo integer, bar varchar(300));
select data_type || '(' || data_length || ')' thetype
from user_tab_columns where TABLE_NAME = 'SOMETABLE';
Prints:
NUMBER(22)
VARCHAR(300)
Screenshot:
Documentation:
https://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_4462.htm#REFRN26277
select column_name, data_type || '(' || data_length || ')' as datatype
from all_tab_columns
where TABLE_NAME = upper('myTableName')
Quick and dirty way (e.g. to see how data is stored in oracle)
SQL> select dump(dummy) dump_dummy, dummy
, dump(10) dump_ten
from dual
DUMP_DUMMY DUMMY DUMP_TEN
---------------- ----- --------------------
Typ=1 Len=1: 88 X Typ=2 Len=2: 193,11
1 row selected.
will show that dummy column in table sys.dual has typ=1 (varchar2), while 10 is Typ=2 (number).
You can try this.
SELECT *
FROM (SELECT column_name,
data_type,
data_type
|| CASE
WHEN data_precision IS NOT NULL
AND NVL (data_scale, 0) > 0
THEN
'(' || data_precision || ',' || data_scale || ')'
WHEN data_precision IS NOT NULL
AND NVL (data_scale, 0) = 0
THEN
'(' || data_precision || ')'
WHEN data_precision IS NULL AND data_scale IS NOT NULL
THEN
'(*,' || data_scale || ')'
WHEN char_length > 0
THEN
'(' || char_length
|| CASE char_used
WHEN 'B' THEN ' Byte'
WHEN 'C' THEN ' Char'
ELSE NULL
END
|| ')'
END
|| DECODE (nullable, 'N', ' NOT NULL')
DataTypeWithLength
FROM user_tab_columns
WHERE table_name = 'CONTRACT')
WHERE DataTypeWithLength = 'CHAR(1 Byte)';
To see the internal representation size in bytes you can use:
REGEXP_SUBSTR(DUMP(your_column_name), 'Len=(\d+)\:', 1, 1, 'c', 1 )
DECLARE
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
-- YOUR SELECT HERE
DBMS_SQL.PARSE(c, '
SELECT *
FROM table1 a
bable2 b
table3 c
where a.id = b.id
and b.id2 = c.id
', DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/

Resources