SQL table macros 8: Print table

I like this one!

Tom Kyte once wrote a procedure to output table contents one column at a time. For some history and a newer solution, see “Print a table with one column name + value per row”.

Using SQL macros, we can get a solution that is even better in my view. My previous solution was a SELECT statement, which I like, but as such it does not hide the code inside a function or procedure, and it is not really configurable.

To stick different column values into one column, we generally have to convert non-string values to VARCHAR2. For fun, I’ll create a little table with several datatypes:

create table datatypes (
C_NUMBER NUMBER,
C_VARCHAR2 VARCHAR2(16),
C_DATE DATE,
C_RAW RAW(16),
C_BINARY_FLOAT BINARY_FLOAT,
C_BINARY_DOUBLE BINARY_DOUBLE,
C_CLOB CLOB,
C_BFILE BFILE,
C_TIMESTAMP TIMESTAMP,
C_TIMESTAMP_WITH_TIME_ZONE TIMESTAMP WITH TIME ZONE,
C_INTERVAL_YEAR_TO_MONTH INTERVAL YEAR TO MONTH,
C_INTERVAL_DAY_TO_SECOND INTERVAL DAY TO SECOND
);

insert into datatypes 
select 3-level,'varchar2',sysdate,hextoraw('FF0102030405'),
  1.1,2.2,'clob',bfilename('DOWNLOAD_DIR', 'x.txt'),
  localtimestamp, localtimestamp,
  interval '1' year, interval '2' day
from dual
connect by level <= 2;

All these datatypes should work from version 19.6 on. If we have access to the Autonomous Cloud 19c version, or to version 21c, we can add some extra datatypes:


create or replace view v_datatypes as
select d.*,
  sys.odcinumberlist(1) c_varray,
  sys.FI_CATEGORICALS('A') c_nested_table,
  SYS.AWRRPT_TEXT_TYPE('a') c_object,
  JSON_object('sysdate':sysdate) j_object
from datatypes d;

Before showing the code of my PRINT_TABLE_MACRO, I’ll show what it does to the view V_DATATYPES. Again, if you try this on a non-cloud 19c version, use the table DATATYPES instead.

select * from print_table_macro(
  p_table => v_datatypes
);
RN COLUMN_NAME COLUMN_VALUE
1 C_NUMBER 2
1 C_VARCHAR2 varchar2
1 C_DATE 2021-12-16T13:40:08
1 C_RAW FF0102030405
1 C_BINARY_FLOAT 1,10000002
1 C_BINARY_DOUBLE 2,2000000000000002
1 C_CLOB clob
1 C_BFILE bfilename(‘DOWNLOAD_DIR’, ‘x.txt’)
1 C_TIMESTAMP 2021-12-16T14:40:8.003345
1 C_TIMESTAMP_WITH_TIME_ZONE 2021-12-16T14:40:8.003345 Europe/Paris
1 C_INTERVAL_YEAR_TO_MONTH +01-00
1 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
1 C_VARRAY [1]
1 C_NESTED_TABLE [“A”]
1 C_OBJECT {“OUTPUT”:”a”}
1 J_OBJECT {“sysdate”:”2021-12-16T13:40:14″}
2 C_NUMBER 1
2 C_VARCHAR2 varchar2
2 C_DATE 2021-12-16T13:40:08
2 C_RAW FF0102030405
2 C_BINARY_FLOAT 1,10000002
2 C_BINARY_DOUBLE 2,2000000000000002
2 C_CLOB clob
2 C_BFILE bfilename(‘DOWNLOAD_DIR’, ‘x.txt’)
2 C_TIMESTAMP 2021-12-16T14:40:8.003345
2 C_TIMESTAMP_WITH_TIME_ZONE 2021-12-16T14:40:8.003345 Europe/Paris
2 C_INTERVAL_YEAR_TO_MONTH +01-00
2 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
2 C_VARRAY [1]
2 C_NESTED_TABLE [“A”]
2 C_OBJECT {“OUTPUT”:”a”}
2 J_OBJECT {“sysdate”:”2021-12-16T13:40:14″}
 

Notice that the input rows are not sorted: I just use ROWNUM to identify each row. Also, each schema-level “User Defined Type” structure is converted to a string in JSON format. Now suppose I want to identify each row by one more columns, and sort by those columns.

select * from print_table_macro(
  p_table => v_datatypes, 
  p_key_cols => columns(c_number)
);
C_NUMBER COLUMN_NAME COLUMN_VALUE
1 C_VARCHAR2 varchar2
1 C_DATE 2021-12-16T13:40:08
1 C_RAW FF0102030405
1 C_BINARY_FLOAT 1,10000002
1 C_BINARY_DOUBLE 2,2000000000000002
1 C_CLOB clob
1 C_BFILE bfilename(‘DOWNLOAD_DIR’, ‘x.txt’)
1 C_TIMESTAMP 2021-12-16T14:40:8.003345
1 C_TIMESTAMP_WITH_TIME_ZONE 2021-12-16T14:40:8.003345 Europe/Paris
1 C_INTERVAL_YEAR_TO_MONTH +01-00
1 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
1 C_VARRAY [1]
1 C_NESTED_TABLE [“A”]
1 C_OBJECT {“OUTPUT”:”a”}
1 J_OBJECT {“sysdate”:”2021-12-16T13:45:50″}
2 C_VARCHAR2 varchar2
2 C_DATE 2021-12-16T13:40:08
2 C_RAW FF0102030405
2 C_BINARY_FLOAT 1,10000002
2 C_BINARY_DOUBLE 2,2000000000000002
2 C_CLOB clob
2 C_BFILE bfilename(‘DOWNLOAD_DIR’, ‘x.txt’)
2 C_TIMESTAMP 2021-12-16T14:40:8.003345
2 C_TIMESTAMP_WITH_TIME_ZONE 2021-12-16T14:40:8.003345 Europe/Paris
2 C_INTERVAL_YEAR_TO_MONTH +01-00
2 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
2 C_VARRAY [1]
2 C_NESTED_TABLE [“A”]
2 C_OBJECT {“OUTPUT”:”a”}
2 J_OBJECT {“sysdate”:”2021-12-16T13:45:50″}
 

Finally, I can exclude columns if I want.

select * from print_table_macro(
  p_table => v_datatypes, 
  p_key_cols => columns(c_number), 
  p_exclude_cols => columns(C_BFILE,C_TIMESTAMP,C_TIMESTAMP_WITH_TIME_ZONE)
);
C_NUMBER COLUMN_NAME COLUMN_VALUE
1 C_VARCHAR2 varchar2
1 C_DATE 2021-12-16T13:40:08
1 C_RAW FF0102030405
1 C_BINARY_FLOAT 1,10000002
1 C_BINARY_DOUBLE 2,2000000000000002
1 C_CLOB clob
1 C_INTERVAL_YEAR_TO_MONTH +01-00
1 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
1 C_VARRAY [1]
1 C_NESTED_TABLE [“A”]
1 C_OBJECT {“OUTPUT”:”a”}
1 J_OBJECT {“sysdate”:”2021-12-16T13:47:36″}
2 C_VARCHAR2 varchar2
2 C_DATE 2021-12-16T13:40:08
2 C_RAW FF0102030405
2 C_BINARY_FLOAT 1,10000002
2 C_BINARY_DOUBLE 2,2000000000000002
2 C_CLOB clob
2 C_INTERVAL_YEAR_TO_MONTH +01-00
2 C_INTERVAL_DAY_TO_SECOND +02 00:00:00.000000
2 C_VARRAY [1]
2 C_NESTED_TABLE [“A”]
2 C_OBJECT {“OUTPUT”:”a”}
2 J_OBJECT {“sysdate”:”2021-12-16T13:47:36″}
 

The code (depends on package SQM_UTIL)

create or replace function print_table_macro(
  p_table in dbms_tf.table_t,
  p_key_cols in dbms_tf.columns_t default null,
  p_exclude_cols in dbms_tf.columns_t default null
) return clob sql_macro is
  l_col_to_strings long;
  l_col_column_names long;
  l_sql long;
  l_key_list varchar2(4000);
  l_order_by varchar2(4000);
  l_exclude_cols dbms_tf.columns_t;
begin
  l_exclude_cols := p_key_cols;
  if l_exclude_cols is null then
    l_exclude_cols := p_exclude_cols;
  else
    if p_exclude_cols is not null then
      l_exclude_cols := l_exclude_cols multiset union p_exclude_cols;
    end if;
  end if;
  sqm_util.col_to_strings(p_table, l_col_to_strings, l_exclude_cols);
  sqm_util.col_column_names(p_table, l_col_column_names, l_exclude_cols);
  if p_key_cols is null then
    l_key_list := 'rownum rn';
    l_order_by := null;
  else
    sqm_util.list_columns(p_key_cols, l_key_list);
    l_order_by := 'order by ' || l_key_list;
  end if;
  l_sql := '
select * from (
  select ' || l_key_list || ',' || l_col_to_strings || ' from p_table
  ' || l_order_by || '
)
unpivot include nulls (column_value for column_name in (' || l_col_column_names || '))
';
  dbms_output.put_line(l_sql);
  return l_sql;
end print_table_macro;
/
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s