Making "CREATE TABLE" statements from data of table - sql

I have table TABLE_INFO with columns TNAME, CNAME, DTYPE, COLCOMMENT
I need to create a SQL script in Oracle SQL Developer, which creates new table with table name from column TNAME, columns from CNAME, datatype of columns from DTYPE and column comment from COLCOMMENT
How can I do this?
For example table TABLE_INFO has following data:
|TNAME | CNAME | DTYPE | Comment |
1|Employee | Emp_ID | NUMBER | Employee ID |
2|Employee | Emp_Nm| VARCHAR | Employee Name |
3|Job | Job_ID | NUMBER | Job ID | etc.
I need to write many CREATE TABLE statements like :
CREATE TABLE EMLOYEE
(EMP ID NUMBER,
Emp_Nm VARCHAR)
COMMENT ON COLUMN EMP_ID is ''Employee ID''
COMMENT ON COLUMN EMP_NM is ''Employee NAME''
for each TNAME

Oh, I confused.
This is a code for schema:
CREATE TABLE TABLE_INFO
(
TNAME VARCHAR2 (30)
,CNAME VARCHAR2 (30)
,DTYPE VARCHAR2 (30)
,COLCOMMENT VARCHAR2 (100)
);
INSERT INTO TABLE_INFO VALUES ('Employee','Emp_ID','NUMBER','Employee ID');
INSERT INTO TABLE_INFO VALUES ('Employee','Emp_Nm','VARCHAR','Employee Name');
INSERT INTO TABLE_INFO VALUES ('Employee','Emp_Sal','NUMBER','Employee Salary');
INSERT INTO TABLE_INFO VALUES ('Job','Job_ID','NUMBER','Job ID');
INSERT INTO TABLE_INFO VALUES ('Job','Job_Nm','VARCHAR','Job Name');
INSERT INTO TABLE_INFO VALUES ('Department','Dept_ID','VARCHAR','Department ID');
INSERT INTO TABLE_INFO VALUES ('Department','Dept_Nm','VARCHAR','Department Name');
And, this is a code for execution:
-- FOR TABLE CREATION
SELECT
CASE WHEN RNO = 1 THEN 'CREATE TABLE '||T1.TNAME||' ( ' ELSE ' ' END ||
T2.CNAME || ' ' || T2.DTYPE ||
CASE WHEN CNT = RNO THEN ');' ELSE ',' END AS SQLTEXT
FROM ( SELECT TNAME
,COUNT (1) CNT
FROM TABLE_INFO
GROUP BY TNAME) T1
,(SELECT ROW_NUMBER () OVER (PARTITION BY TNAME ORDER BY CNAME) RNO
,X.*
FROM TABLE_INFO X) T2
WHERE T1.TNAME = T2.TNAME
ORDER BY T1.TNAME, T2.RNO;
-- FOR COMMENT CREATION
SELECT 'COMMENT ON TABLE ' || TNAME || '.' || CNAME || ' IS ''' || COLCOMMENT || ''';' AS SQLTEXT
FROM TABLE_INFO;
You could find the result at here
NOTE: I used ROW_NUMBER () function at Oracle only. please be aware of that.

Try this.
SELECT T1.TABLE_NAME
,T1.COLUMN_NAME
,T1.DATA_TYPE
,T2.COMMENTS
FROM ALL_TAB_COLS T1
,ALL_COL_COMMENTS T2
WHERE T1.OWNER = T2.OWNER
AND T1.TABLE_NAME = T2.TABLE_NAME
AND T1.COLUMN_NAME = T2.COLUMN_NAME
It will retrieve all accessible tables information.
To include DBA tables, use DBA_TAB_COLS and DBA_COL_COMMENTS instead of ALL_TAB_COLS and ALL_COL_COMMENTS respectively.
You can also give more conditions to that query.
To insert this,
INSERT INTO TABLE_INFO (
TNAME
,CNAME
,DTYPE
,COLCOMMENT)
SELECT T1.TABLE_NAME
,T1.COLUMN_NAME
,T1.DATA_TYPE
,T2.COMMENTS
FROM ALL_TAB_COLS T1
,ALL_COL_COMMENTS T2
WHERE T1.OWNER = T2.OWNER
AND T1.TABLE_NAME = T2.TABLE_NAME
AND T1.COLUMN_NAME = T2.COLUMN_NAME

I've found anouther solution
CREATE OR REPLACE
PROCEDURE CREATEFROMTABLE AS
BEGIN
for j in (select DISTINCT tname from t_info) loop `
dbms_output.put_line('create table '|| j.tname || ' as (');`
for col in (select CNAME, dtype from T_INFO where tname = j.tname)
loop
dbms_output.put_line(col.cname|| ' ' || col.dtype);
end loop;
dbms_output.put_line(')');`
dbms_output.put_line('');`
for col in (select cname, dcmt from T_INFO where tname = j.tname) `
loop`
dbms_output.put_line('COMMENT ON COLUMN '|| col.cname || ' is ' || col.dcmt || ' ');`
end loop; `
`dbms_output.put_line(' ');`
`end loop;`
END CREATEFROMTABLE;
t_info is main table with columns tname, cname, dcmt
tname is new table name
cname is column name
dcmt is comment on the column

Related

How to perform a dynamic select query for multiple tables in oracle? using table name and column names as values from other table?

i have a table having columns such schema name,table name,column name.
schema name
table name
column name
dbo
product
colour
dbo
sales
quantity
dbo
customer
order
i want to perform an action such that to get a count of records based on column as column name and table as table name
select count(colour) as count from dbo.product
select count(quantity) as count from dbo.sales
select count(order) as count from dbo.customer
can u suggest me correct steps to achieve this using Oracle database. thanks in advance
expected output
count
5
50
150
If you need to get count per column per table, you can do this with plain SQL using DBMS_XMLGEN package, which essentially executes new cursors dynamically.
I think you can adapt the example query below to suit your needs (aggregate counts or convert them to another format).
with a as (
select
'all_tables' as table_name, 'table_name' as column_name
from dual
union all
select 'all_tables', 'tablespace_name' from dual union all
select 'all_tab_cols', 'column_name' from dual union all
select 'all_indexes', 'index_name' from dual union all
select 'all_indexes', 'tablespace_name' from dual
)
select
table_name,
column_name,
cast(extractvalue(
dbms_xmlgen.getxmltype(
'select count(' || column_name || ') as cnt' || chr(10) ||
'from ' || table_name
),
'/ROWSET/ROW/CNT'
) as int) as cnt
from a
TABLE_NAME | COLUMN_NAME | CNT
:----------- | :-------------- | ----:
all_tables | table_name | 71
all_tables | tablespace_name | 43
all_tab_cols | column_name | 20983
all_indexes | index_name | 81
all_indexes | tablespace_name | 73
db<>fiddle here
You cannot do this in pure sql, but you can pretty easily do this in pl/sql. In the code below I'm storing the results in a table called rowcounts and it assumes your table with tables and column names is called
CREATE table mytables (schema_name VARCHAR2(100), table_name, VARCHAR2(100), column_name VARCHAR2(100));
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','product','colour');
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','sales','quantity');
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','customer','order');
CREATE TABLE rowcounts (table_name VARCHAR2(500), rowcount NUMBER);
DECLARE
l_rowcount INT;
BEGIN
FOR r IN (SELECT * FROM mytables) LOOP
EXECUTE IMMEDIATE 'SELECT COUNT('||r.column_name||') FROM '||r.table_name INTO l_rowcount;
--dbms_output.put_line('SELECT COUNT('||r.column_name||') FROM '||r.table_name);
INSERT INTO rowcounts(table_name, rowcount) VALUES (r.table_name, l_rowcount);
END LOOP;
COMMIT;
END;
/
--DROP TABLE rowcounts;

Oracle insert data from another table without column names

I have two tables. They differ only columns order.
First
Table1
(
name,
surname,
age
)
Second
Table2
(
age
surname,
name
)
I want insert data to Table2 from Table1.
If tables column order is the same I can use
insert into Table2
select * from Table1
I know that I can solve this problem with
insert into table2
select age,surname,name from table1
But I don't use it because there are many column in my real table.
is there good idea for it?
The only way is with some dynamic SQL, by relying on column names; for example, say you have the tables
CREATE TABLE Table1
(
name VARCHAR2(100),
surname VARCHAR2(100),
age NUMBER
);
CREATE TABLE Table2
(
name VARCHAR2(100),
age NUMBER,
oneMoreColumn NUMBER,
surname VARCHAR2(100)
);
you can do:
declare
vSQL varchar2(1000);
vCols varchar2(1000);
begin
select listagg(tc1.column_name, ', ') within group (order by tc1.column_name)
into vCols
from user_tab_columns tc1
inner join user_tab_columns tc2
on(tc1.column_name = tc2.column_name)
where tc1.table_name = 'TABLE1'
and tc2.table_name = 'TABLE2';
--
vSQL := 'insert into table2( ' || vCols || ') select ' || vCols || ' from table1';
--
dbms_output.put_line(vSQL);
--
execute immediate vSQL;
end;
this will build and execute the statement:
insert into table2( AGE, NAME, SURNAME) select AGE, NAME, SURNAME from table1
you can do like this
create table EX_EMPLOYEE
(
NAME VARCHAR2(100),
PATH VARCHAR2(1000)
)
SET serveroutput ON size 2000
/
declare T_COL varchar2(50);
CURSOR c1 IS SELECT column_name name FROM user_tab_cols where table_name='EX_EMPLOYEE';
BEGIN
FOR rec IN c1 LOOP
if T_COL is null then
T_COL := T_COL || rec.name;
else
T_COL := T_COL ||' ,' || rec.name;
end if;
END LOOP;
dbms_output.put_line('select '|| T_COL ||' FROM EX_EMPLOYEE');
END;
/
select NAME ,PATH FROM EX_EMPLOYEE
PL/SQL procedure successfully completed
you could do something like this:
create table new_table as
select * from old_table

CREATE AS SELECT * but with one column obtained from another table

I need to 'recreate' over 50 tables (in Oracle) with CREATE AS SELECT statements. However, all this tables will have one column modified using data from another table. Is there a way to achieve this without declaring each column in the SELECT statement?
Something like:
CREATE TABLE table_name_copy AS SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) AS col_name FROM table_name`
Basically on all tables I have a column which needs to be replaced with the data in the other_table column.
Generate the SQL string as such:
SELECT 'CREATE TABLE table_name_copy AS SELECT '
|| LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
|| ', (SELECT col_name FROM other_table
WHERE other_table.col_id = table_name.col_id) AS col_name'
|| ' FROM table_name'
FROM all_tab_cols
WHERE owner = 'OWNER'
AND table_name = 'TABLE_NAME'
AND column_name != 'COL_NAME'
If you want to run the above statement, you could use EXECUTE IMMEDIATE:
DECLARE
v_sql VARCHAR2(10000);
BEGIN
SELECT 'CREATE TABLE table_name_copy AS SELECT '
|| LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
|| ', (SELECT col_name FROM other_table
WHERE other_table.col_id = table_name.col_id) AS col_name'
|| ' FROM table_name'
INTO v_sql
FROM all_tab_cols
WHERE owner = 'OWNER'
AND table_name = 'TABLE_NAME'
AND column_name != 'COL_NAME';
EXECUTE IMMEDIATE v_sql;
END;
/
If col_id column is fixed for both of the joined tables,
you may use user_tab_columns and user_tables dictionary views through the schema to produce new tables named as "table_name_copy" by using the following mechanism :
declare
v_ddl varchar2(4000);
v_cln varchar2(400);
begin
for c in ( select *
from user_tables t
where t.table_name in
( select c.table_name
from user_tab_columns c
where c.column_name = 'COL_ID' )
order by t.table_name )
loop
v_ddl := 'create table '||c.table_name||'_copy as
select ';
for d in ( select listagg('t1.'||column_name, ',') within group ( order by column_name ) cln
from user_tab_columns
where table_name = c.table_name
and column_name != 'COL_ID' )
loop
v_cln := v_cln||d.cln;
end loop;
v_ddl := v_ddl||v_cln;
v_ddl := v_ddl||', t2.col_id t2_id
from '||c.table_name||' t1
left outer join other_table t2 on ( t1.col_id = t2.col_id )';
execute immediate v_ddl;
v_ddl := null;
v_cln := null;
end loop;
end;
Maybe you can use a simple join and an asterisk to return all columns from the first table, like that:
CREATE TABLE table_name_copy AS
SELECT * FROM (
SELECT tab1.*, tab2.column_name
FROM table_name tab1 LEFT JOIN other_table tab2 ON tab1.col_id = tab2.col_id
);
I would try this (but I don't have Oracle SQL to test on so please leave me the benefit of the doubt)
CREATE TABLE table_name_copy AS
SELECT * FROM (
SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) as col_name
FROM table_name`
)
edit:
then run
ALTER TABLE table_name_copy DROP COLUMN <old column>
to remove the column you don't need any more

Oracle get table names based on column value

I have table like this:
Table-1
Table-2
Table-3
Table-4
Table-5
each table is having many columns and one of the column name is employee_id.
Now, I want to write a query which will
1) return all the tables which is having this columns and
2) results should show the tables if the column is having values or empty values by passing employee_id.
e.g. show table name, column name from Table-1, Table-2,Table-3,... where employee_id='1234'.
If one of the table doesn't have this column, then it is not required to show.
I have verified with link, but it shows only table name and column name and not by passing some column values to it.
Also verified this, but here verifies from entire schema which I dont want to do it.
UPDATE:
Found a solution, but by using xmlsequence which is deprecated,
1)how do I make this code as xmltable?
2) If there are no values in the table, then output should have empty/null. or default as "YES" value
WITH char_cols AS
(SELECT /*+materialize */ table_name, column_name
FROM cols
WHERE data_type IN ('CHAR', 'VARCHAR2') and table_name in ('Table-1','Table-2','Table-3','Table-4','Table-5'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Employee_ID",
SUBSTR (table_name, 1, 14) "Table",
SUBSTR (column_name, 1, 14) "Column"
FROM char_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select "'
|| column_name
|| '" from "'
|| table_name
|| '" where upper("'
|| column_name
|| '") like upper(''%'
|| :val
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) t ORDER BY "Table"
/
This query can be done in one step using the (non-deprecated) XMLTABLE.
Sample Schema
--Table-1 and Table-2 match the criteria.
--Table-3 has the right column but not the right value.
--Table-4 does not have the right column.
create table "Table-1" as select '1234' employee_id from dual;
create table "Table-2" as select '1234' employee_id from dual;
create table "Table-3" as select '4321' employee_id from dual;
create table "Table-4" as select 1 id from dual;
Query
--All tables with the column EMPLOYEE_ID, and the number of rows where EMPLOYEE_ID = '1234'.
select table_name, total
from
(
--Get XML results of dynamic query on relevant tables and columns.
select
dbms_xmlgen.getXMLType(
(
--Create a SELECT statement on each table, UNION ALL'ed together.
select listagg(
'select '''||table_name||''' table_name, count(*) total
from "'||table_name||'" where employee_id = ''1234'''
,' union all'||chr(10)) within group (order by table_name) v_sql
from user_tab_columns
where column_name = 'EMPLOYEE_ID'
)
) xml
from dual
) x
cross join
--Convert the XML data to relational.
xmltable('/ROWSET/ROW'
passing x.xml
columns
table_name varchar2(128) path 'TABLE_NAME',
total number path 'TOTAL'
);
Results
TABLE_NAME TOTAL
---------- -----
Table-1 1
Table-2 1
Table-3 0
Just try to use code below.
Pay your attention that may be nessecery clarify scheme name in loop.
This code works for my local db.
set serveroutput on;
DECLARE
ex_query VARCHAR(300);
num NUMBER;
emp_id number;
BEGIN
emp_id := <put your value>;
FOR rec IN
(SELECT table_name
FROM all_tab_columns
WHERE column_name LIKE upper('employee_id')
)
LOOP
num :=0;
ex_query := 'select count(*) from ' || rec.table_name || ' where employee_id = ' || emp_id;
EXECUTE IMMEDIATE ex_query into num;
if (num>0) then
DBMS_OUTPUT.PUT_LINE(rec.table_name);
end if;
END LOOP;
END;
I tried with the xml thing, but I get an error I cannot solve. Something about a zero size result. How difficult is it to solve this instead of raising exception?! Ask Oracle.
Anyway.
What you can do is use the COLS table to know what table has the employee_id column.
1) what table from table TABLE_LIKE_THIS (I assume column with table names is C) has this column?
select *
from COLS, TABLE_LIKE_THIS t
where cols.table_name = t
and cols.column_name = 'EMPLOYEE_ID'
-- think Oracle metadata/ think upper case
2) Which one has the value you are looking for: write a little chunk of Dynamic PL/SQL with EXECUTE IMMEDIATE to count the tables matching above condition
declare
v_id varchar2(10) := 'JP1829'; -- value you are looking for
v_col varchar2(20) := 'EMPLOYEE_ID'; -- column
n_c number := 0;
begin
for x in (
select table_name
from all_tab_columns cols
, TABLE_LIKE_THIS t
where cols.table_name = t.c
and cols.column_name = v_col
) loop
EXECUTE IMMEDIATE
'select count(1) from '||x.table_name
||' where Nvl('||v_col||', ''##'') = ''' ||v_id||'''' -- adding quotes around string is a little specific
INTO n_c;
if n_c > 0 then
dbms_output.put_line(n_C|| ' in ' ||x.table_name||' has '||v_col||'='||v_id);
end if;
-- idem for null values
-- ... ||' where '||v_col||' is null '
-- or
-- ... ||' where Nvl('||v_col||', ''##'') = ''##'' '
end loop;
dbms_output.put_line('done.');
end;
/
Hope this helps

A more efficient way to do a select insert that involves over 300 columns?

I am trying to find a more efficient way to write PL/SQL Query to
to select insert from a table with 300+ columns, to the back up version of that table (same column names + 2 extra columns).
I could simply type out all the column names in the script (example below), but with that many names, it will bother me... :(
INSERT INTO
TABLE_TEMP
(column1, column2, column3, etc)
(SELECT column1, column2, column3, etc FROM TABLE WHERE id = USER_ID);
Thanks in advance
Specify literals/null for those two extra columns.
INSERT INTO
TABLE_TEMP
SELECT t1.*, null, null FROM TABLE t1 WHERE id = USER_ID
You can pretty easily build a column list for any given table:
select table_catalog
,table_schema
,table_name
,string_agg(column_name, ', ' order by ordinal_position)
from information_schema.columns
where table_catalog = 'catalog_name'
and table_schema = 'schema_name'
and table_name = 'table_name'
group by table_catalog
,table_schema
,table_name
That should get you nearly where you need to be.
The question tag says plsql, which is Oracle or one of its variants. Here is an example of doing it in Oracle:
drop table brianl.deleteme1;
drop table brianl.deleteme2;
CREATE TABLE brianl.deleteme1
(
a INTEGER
, b INTEGER
, c INTEGER
, efg INTEGER
);
CREATE TABLE brianl.deleteme2
(
b INTEGER
, c INTEGER
, d INTEGER
, efg INTEGER
);
DECLARE
l_ownerfrom VARCHAR2 (30) := 'BRIANL';
l_tablefrom VARCHAR2 (30) := 'DELETEME1';
l_ownerto VARCHAR2 (30) := 'BRIANL';
l_tableto VARCHAR2 (30) := 'DELETEME2';
l_comma VARCHAR2 (1) := NULL;
BEGIN
DBMS_OUTPUT.put_line ('insert into ' || l_ownerto || '.' || l_tableto || '(');
FOR eachrec IN ( SELECT f.column_name
FROM all_tab_cols f INNER JOIN all_tab_cols t ON (f.column_name = t.column_name)
WHERE f.owner = l_ownerfrom
AND f.table_name = l_tablefrom
AND t.owner = l_ownerto
AND t.table_name = l_tableto
ORDER BY f.column_name)
LOOP
DBMS_OUTPUT.put_line (l_comma || eachrec.column_name);
l_comma := ',';
END LOOP;
DBMS_OUTPUT.put_line (') select ');
l_comma := NULL;
FOR eachrec IN ( SELECT f.column_name
FROM all_tab_cols f INNER JOIN all_tab_cols t ON (f.column_name = t.column_name)
WHERE f.owner = l_ownerfrom
AND f.table_name = l_tablefrom
AND t.owner = l_ownerto
AND t.table_name = l_tableto
ORDER BY f.column_name)
LOOP
DBMS_OUTPUT.put_line (l_comma || eachrec.column_name);
l_comma := ',';
END LOOP;
DBMS_OUTPUT.put_line (' from ' || l_ownerfrom || '.' || l_tablefrom || ';');
END;
This results in this output:
insert into BRIANL.DELETEME2(
B
,C
,EFG
) select
B
,C
,EFG
from BRIANL.DELETEME1;
Nicely formatted:
INSERT INTO brianl.deleteme2 (b, c, efg)
SELECT b, c, efg
FROM brianl.deleteme1;