DB2 Dynamic View CREATE statement from SYS tables - sql

Uncertain if this is even possible, but here goes...
In DB2 v9.7, I'm trying to create a view for every table in a schema.
The view should contain each column, but appends one more timestamp column.
i.e.
TABLE1 (COL1, COL2, COL3)
V_TABLE1 (COL1, COL2, COL3, INS_DTTM)
TABLE2 (COL1, COL4)
V_TABLE2 (COL1, COL4, INS_DTTM)
Here's what I've got so far
SELECT 'CREATE VIEW V_' || NAME || ' (' ||
(SELECT LISTAGG(NAME, ', ') FROM SYSIBM.SYSCOLUMNS WHERE TBCREATOR=CREATOR) || ')
AS SELECT ' || (SELECT LISTAGG(NAME, ', ')
FROM SYSIBM.SYSCOLUMNS WHERE TBCREATOR='SCHEMA') || ', CAST(NULL AS TIMESTAMP)
AS INS_DTTM FROM ' || CREATOR || '.' || NAME
FROM SYSIBM.SYSTABLES
WHERE CREATOR = 'SCHEMA' AND NAME LIKE 'T%' ORDER BY NAME;
It's my subselect that is causing me grief...
I get the following:
CREATE VIEW V_TABLE1 (COL1, COL2, COL3) AS SELECT COL1, COL2, COL3, CAST(NULL AS TIMESTAMP) AS INS_DTTM FROM SCHEMA.TABLE1
CREATE VIEW V_TABLE2 (COL1, COL2, COL3) AS SELECT COL1, COL2, COL3, CAST(NULL AS TIMESTAMP) AS INS_DTTM FROM SCHEMA.TABLE2
I'll apologize in advance if my syntax is off...I'm retyping across development environs and the internet...so I'm certain I've made mistakes...
I want the columns listed to relate to the table name of that row...but the LISTAGG appears for just the first row...
Hope this makes sense...and if anyone has any suggestions, please let me know.
Thanks

Related

oracle sql dynamically select columns using a parameter table

I have a table in oracle with 10 columns, say Table A with col_1, col_2, col_3 etc. I have another table, Table B with rows that has column names from table A co1_1, col_2, col_3. The rows in Table B can vary.
TABLE A
COL1 COL2 COL3 COL4 COL5 COL6 COL7 COL8 COL9 COL10
TABLE B
COL1
COL2
COL3
I want to write an oracle sql query which dynamically gets the select columns names based on the column names(rows) in table B.
If table B has 3 rows with corresponding column names then my query should look like this
select col_1, col_2, col_3 from A
If table B has 4 rows then my query should dynamically change to below
select col_1, col_2, col_3, col_4 from A
You need to use the dynamic query.
'SELECT '
|| (SELECT LISTAGG(COLNAME, ',') WITHIN GROUP (ORDER BY COLNAME) FROM TABLEB)
|| ' FROM TABLEA'
I think we should go to ALL_TAB_COLUMNS table for finding column names.
Like this.
SELECT
'SELECT '
||
(SELECT LISTAGG( y.COLNAME, ',') WITHIN GROUP (ORDER BY Y.COLNAME)
FROM TABLE_B x,ALL_TAB_COLUMNS y
where x.COLNAME=Y.COLUMN_NAME )
||
' FROM Table_A' script
FROM DUAL;
A ref cursor can be used to created dynamic columns. Many languages and applications support ref cursors, and if you add some details about your system someone may know exactly how to integrate them in your environment.
Below is a simple example of how to create a function that returns a ref cursor. How to call it depends on your system.
Sample Schema
--drop table a;
--drop table b;
create table a as
select 1 col1, 2 col2, 3 col3, 4 col4, 5 col5, 6 col6, 7 col7, 8 col8, 9 col9, 10 col10
from dual;
create table b as
select 'COL1' column_name from dual union all
select 'COL2' column_name from dual union all
select 'COL3' column_name from dual;
Function
create or replace function get_dynamic_results return sys_refcursor is
v_cursor sys_refcursor;
v_column_list varchar2(4000);
begin
--Split this into multiple SELECTS if you get an error like:
-- ORA-01489: result of string concatenation is too long error
select listagg(column_name, ',') within group (order by column_name) columns
into v_column_list
from b;
open v_cursor for 'select '||v_column_list||' from a';
return v_cursor;
end;
/

SQL: Problems translating recursive connect by Statement for Postgres

I try to translate the following oracle sql, which inserts 1000 rows with incremental values into a table:
insert into tableName (col1, col2, col3)
select 'AN' || (1000000 + ROWNUM), 'EXT' || (9000000 + ROWNUM), ROWNUM
from dual
Connect By ROWNUM <= 1000 ;
For Postgres support, i know i can substitute ROWNUM with ROW_NUMBER() OVER (), but i'm really getting a headache about translating the connect by statement. I have read about CTEs but i don't get how i can use this with an insert statement.
Does anyone know how to write this statement for postgresql? Thanks.
You can generate a series and just use that:
insert into tableName (col1, col2, col3)
select 'AN' || (1000000 + g.n), 'EXT' || (9000000 + g.n), g.n
from generate_series(1, 1000) g(n);
Try generate_series.
select 'AN' || (1000000 + ROWNUM), 'EXT' || (9000000 + ROWNUM),
ROWNUM from generate_series(1,10000) as rownum ;

Count distinct values for every column individually

Can I count distinct values of every column without enumerating them ?
Say I have a table with col1, col2, col3, and no other column. Without mentioning these columns explicitly, I would like to have the same result as:
SELECT
count(distinct col1) as col1,
count(distinct col2) as col2,
count(distinct col3) as col3
FROM mytable;
How can I do this ?
I think the best you could easily do with plain SQL is to run a query like this to generate the query you want, and then run that.
select 'select count(distinct '
|| listagg(column_name || ') as ' || column_name, ', count(distinct ') within group (order by column_id)
|| ' from ' || max(table_name) || ';' as script
from all_tab_cols
where table_name = 'MYTABLE';

How to find the 2nd highest value in a row with Oracle SQL?

I have a row of the database like this:
1|abc|10|30|12
The biggest value is 30 and the second highest is 12. How can I get the second value for each row in my table?
This will work with any number of columns, just make sure you add them to the concatenated list where labelled merge_col in my query below:
select col1, col2, col3, col4, col5, second_highest
from (select x.*,
regexp_substr(merge_col, '[^|]+', 1, levels.column_value) as second_highest,
row_number() over(partition by x.col1 order by to_number(regexp_substr(merge_col, '[^|]+', 1, levels.column_value)) desc) as rn
from (select t.*, col3 || '|' || col4 || '|' || col5 as merge_col
from tbl t) x,
table(cast(multiset
(select level
from dual
connect by level <=
length(regexp_replace(merge_col,
'[^|]+')) + 1) as
sys.OdciNumberList)) levels)
where rn = 2
Fiddle test: http://sqlfiddle.com/#!4/b446f/2/0
In other words, for additional columns, change:
col3 || '|' || col4 || '|' || col5 as merge_col
to:
col3 || '|' || col4 || '|' || col5 || '|' || col6 ......... as merge_col
with however many columns there are in place of the ......
Assuming the values are all different:
select t.*,
(case when col1 <> greatest(col1, col2, col3) and
col1 <> least(col1, col2, col3)
then col1
when col2 <> greatest(col1, col2, col3) and
col2 <> least(col1, col2, col3)
then col2
else col3
end) as secondgreatest
from table t;
There are different ways to achieve that. try this one for instance:
SELECT MAX('column_name') FROM 'table_name'
WHERE 'column_name' NOT IN (SELECT MAX('column_name') FROM 'table_name' )
You basically exclude the highest number from your query and then you select the highest out of the rest .
If you can concatenate all the columns as a comma-delimited list, then you can do the following (assuming you're looking for the second highest numerical value):
WITH d1 AS (
SELECT keycolumn1, keycolumn2, col1 || ',' || col2 || ',' || ... || ',' || colx AS other_columns
FROM mytable
), d2 AS (
SELECT keycolumn1, keycolumn2, LEVEL AS pos
, TO_NUMBER(REGEXP_SUBSTR(other_columns, '[^,]+', 1, LEVEL)) AS cvalue
FROM d1
CONNECT BY REGEXP_SUBSTR(other_columns, '[^,]+', 1, LEVEL) IS NOT NULL
AND PRIOR keycolumn1 = keycolumn1
AND PRIOR keycolumn2 = keycolumn2
AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
)
SELECT keycolumn1, keycolumn2, pos, cvalue FROM (
SELECT keycolumn1, keycolumn2, pos, cvalue
, ROW_NUMBER() OVER ( PARTITION BY keycolumn1, keycolumn2 ORDER BY cvalue DESC ) AS rn
FROM d2
) WHERE rn = 2;
The above will return the second-highest value for a given set of "key" column values along with the position of the column in which that value is found. I use concatenation and CONNECT BY to unpivot the table.

Oracle select selected columns from a table whose column names are available in another table

Let me give an example :
Suppose I have a table TableA(Col1, Col2, Col3, Col4, Col5)
I have another table TableB where their are entries of the names of the columns of TableA that required to be fetched, for example Col2 and Col5
Now I want to write an SQL query that will only fetch the columns of TableA as defined in TableB .
Here is a start.
The idea is to build a concatenated list of column_names as a varchar
'col1, col2, col3, col4'
and to use it in a dynamic sql query.
declare
column_list xmltype;
column_names varchar(10000);
begin
SELECT
XMLAGG (XMLELEMENT (e, t1.column_name || ',')).EXTRACT ('//text()')
column_name
into column_list
FROM all_tab_cols t1
where t1.table_name = 'TABLEA'
and exists (select null
from TableB
where t1.column_name = <the field for the column_name in tableB>);
column_names := RTRIM(column_list.getClobVal(), ',');
--this will just display the sql query, you'll need to execute it to get your results with EXECUTE IMMEDIATE
dbms_output.put_line( 'SELECT '||column_names||' from TableA');
end;