I have two tables
temp_data_holder
temp_data_holder1
These two tables will have some common columns but they can also have some extra columns which might be present in only one table. I need a query which will select the common columns from each table.
I can get the common column names using the following query
Select column_name
from all_tab_columns
where table_name like 'temp_data_holder'
intersect
Select column_name
from all_tab_columns
where table_name like 'temp_data_holder1';
Is there a way to use this query to get the resultant columns from each table?
I'm asking for something like this
Select columns=(Select column_name from all_tab_columns where table_name like 'temp_data_holder' intersect Select column_name from all_tab_columns where table_name like 'temp_data_holder1') from temp_data_holder;
I.e. the tables will be inside a for loop and the structure will change after each iteration so i cannot simply hard-code the column names
In general you can get the common columns (evaluated by name) from two tables using the statement below.
select listagg(column_name,',') within group (order by column_name)
from (
select column_name
from user_tab_columns
where table_name in (t1,t2)
group by column_name having count(*) = 2
);
the resulting string could be used to generate useful statements for comparing the two tables t1 and t2
SQL and PL/SQL are strongly-typed and rigorous languages. Doing things on the fly is hard. That makes applications reliable but developers schooled in more reflexive languages butt up against such inflexibility.
Which is to say there is no easy way to achieve what you want. You need to assemble the query as a string and use Dynamic SQL to execute the finished statement. You also will have problems reading the result set, because SQL doesn't handle ad hoc projections. Usually this means code has to be called from say Java using a Ref Cursor to map to a JDBC ResultSet.
create or replace function get_data_for_common_cols return sys_refcursor is
stmt varchar2(32767) := 'select ';
rc sys_refcursor;
begin
for recs in ( select t.column_name
, row_number() over (partition by t.table_name order by t.column_id) as rn
from user_tab_columns t
join user_tab_columns t1 on t1.column_name = t.column_name
where t.table_name = 'TEMP_DATA_HOLDER'
and t1.table_name = 'TEMP_DATA_HOLDER1'
order by t.column_id)
loop
if recs.rn != 1 then
stmt := stmt || ',';
end if;
stmt := stmt || recs.column_name;
end loop;
stmt := stmt || ' from TEMP_DATA_HOLDER';
open rc for stmt;
return rc;
end;
/
This function can be parameterised to take a table name - or two table names - for use in the loop query and the assembled statement. Doing so is left as an exercise for the reader :)
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME IN ('articles', 'articles')
GROUP BY column_name HAVING COUNT(*) = 2;
Related
I need to search in a large DB a table that matches with a column name, but this table must have more than 0 rows.
Here is the query by the way:
SELECT * FROM all_tab_columns WHERE column_name LIKE '%ID_SUPPORT%';
You could use single query to filter names and get actual number of rows:
SELECT owner, table_name, cnt
FROM all_tab_columns, XMLTABLE('/ROWSET/ROW' passing
(dbms_xmlgen.getxmltype(REPLACE(REPLACE(
'select COUNT(*) AS cnt from <owner>.<table_name>', '<owner>', owner)
, '<table_name>', table_name))) COLUMNS cnt INT)
WHERE column_name LIKE '%ID_SUPPORT%' AND cnt > 0;
DBFiddle Demo
Any chance this can be expanded/tweaked to yield the values of the first few rows for all tables?
Yes, by flattening row using JSON_ARRAYAGG(JSON_OBJECT(*)) Oracle 19c:
-- generic approach Oracle 19c
SELECT owner, table_name, cnt, example
FROM all_tab_columns, XMLTABLE('/ROWSET/ROW' passing
(dbms_xmlgen.getxmltype(REPLACE(REPLACE(
'select COUNT(*) AS cnt,
MAX((SELECT JSON_ARRAYAGG(JSON_OBJECT(*))
FROM <owner>.<table_name>
WHERE rownum < 10) -- taking up to 10 rows as example
) as example
from <owner>.<table_name>', '<owner>', owner)
, '<table_name>', table_name)))
COLUMNS cnt INT
, example VARCHAR2(1000))
WHERE column_name LIKE '%ID_SUPPORT%'
AND cnt > 0;
Demo contains hardcoded column list inside JSON_OBJECT. Oracle 19c and JSON_OBJECT(*) would allow any column list per table.
db<>fiddle demo
How it works:
find all tables that have column named '%ID_SUPPORT'
run query per table using dbms_xml_gen.getxmltype
in sub query count the rows, flatten few rows an example to JSON
return rows that have at least one record table
One way:
SELECT * FROM all_tables WHERE num_rows > 0
AND table_name in (SELECT table_name FROM all_tab_columns WHERE column_name LIKE '%ID_SUPPORT%')
If your DB is periodically analyzed the direct way is to use the following SQL :
SELECT *
FROM all_tables t
WHERE t.table_name LIKE '%ID_SUPPORT%'
and t.num_rows > 0;
More precise way to determine is using the following :
declare
v_val pls_integer := 0;
begin
for c in (
SELECT *
FROM all_tables t
WHERE t.table_name LIKE '%ID_SUPPORT%'
)
loop
execute immediate 'select count(1) from '||c.owner||'.'||c.table_name into v_val;
if v_val > 0 then
dbms_output.put_line('Table Name : '||c.table_name||' with '||v_val||' rows ');
end if;
end loop;
end;
I'm confused with the word matches. If you mean column, but not table, you may use the following routine to get the desired tables with columns whose names are like ID_SUPPORT :
declare
v_val pls_integer := 0;
begin
for c in (
SELECT t.*
FROM all_tab_columns c
JOIN all_tables t on ( c.table_name = t.table_name )
WHERE c.column_name LIKE '%ID_SUPPORT%'
)
loop
execute immediate 'select count(1) from '||c.owner||'.'||c.table_name into v_val;
if v_val > 0 then
dbms_output.put_line('Table Name : '||c.table_name||' with '||v_val||' rows ');
end if;
end loop;
end;
I want to select/display the columns of a table that are of a certain format. I wrote the following query:
SELECT
(SELECT
COLUMN_NAME
FROM SYS.ALL_TAB_COLS
WHERE TABLE_NAME='SOME_TABLE' AND DATA_TYPE IN ('DATE'))
FROM SOME_TABLE;
After the query runs for some time, I get the following error:
ORA-01427: single-row subquery returns more than one row
I would want a result that is something like:
DATE1 DATE2
2017-01-01 2017-01-01
2017-01-01 2018-01-02
... ...
Does someone know how to achieve this?
You could make use of a refcursor bind variable and use the PRINT command to display the output from a dynamic query. This works in SQL* Plus and in Toad and SQL developer when run as script.
VARIABLE x refcursor;
DECLARE
v_query CLOB;
BEGIN
SELECT 'SELECT '
|| LISTAGG(column_name, ',')
within GROUP ( ORDER BY column_name )
|| ' FROM '
|| table_name
INTO v_query
FROM sys.all_tab_cols
WHERE table_name = 'EMPLOYEES'
AND data_type IN ( 'DATE' )
GROUP BY table_name;
OPEN :x FOR v_query;
END;
/
PRINT x;
for 12c and above, you could use DBMS_SQL.RETURN_RESULT on a PL/SQL cursor on the same query.
DECLARE
v_query CLOB;
x SYS_REFCURSOR;
BEGIN
SELECT..
..
OPEN x FOR v_query;
DBMS_SQL.RETURN_RESULT(x);
END;
/
Note: If there are multiple tables in different schemas with the same name, you would need to add owner = <schema> as well.
Use this query to return the rows in columns and then you can use and format the columns that dynamically return with execute of EXECUTE IMMEDIATE
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP(ORDER BY COLUMN_NAME) AS COLUMNA FROM (
SELECT COLUMN_NAME
FROM SYS.ALL_TAB_COLS
WHERE TABLE_NAME = 'SOME_TABLE' AND DATA_TYPE IN ('DATE')
) SOME_TABLE
RESULT:
DATE1, DATE2
This question already has answers here:
Can you SELECT everything, but 1 or 2 fields, without writer's cramp?
(12 answers)
Closed 6 years ago.
I want to write a query to fetch data from a table except some columns which start name like given in the wildcard criteria as bellow example pseudo code. Is it possible on oracle?
(this can be done as adding column names for the select clause. but assuming there will be new columns will add in future , i want to write a more generic code)
example
Employee(id , name , age, gender)
select *
from table_name
where column_name not like a%
after query it should display a table with
Employee(id , name . gender)
the age column is not there because we are not include in the result
You can try with some dynamic SQL:
declare
vSQL varchar2(32767);
vClob clob;
begin
/* build the query */
select distinct 'select ' || listagg(column_name, ',') within group (order by column_name) over (partition by table_name)|| ' from ' || table_name
into vSQL
from user_tab_columns
where table_name = 'EMPLOYEE'
and column_name not like 'A%';
/* print the query */
dbms_output.put_line(vSQL);
/* build an XML */
select DBMS_XMLGEN.getXML(vSQL)
into vClob
from dual;
dbms_output.put_line(vClob);
/* build a CLOB with all the columns */
vSQL := replace (vSQL, ',', ' || '' | '' || ' );
execute immediate vSQL into vClob;
dbms_output.put_line(vClob);
end;
In this way you can dynamically build a query that extracts all the columns exept those matching a pattern.
After building the query, the question is how to fetch it, given that you don't know in advance what columns you are fetching.
In the example I make an XML and a single row; you can use the query in different ways, depending on your needs.
Duplicate, but I like writing PL, so here is how you could, creating a temp table, then select * from it:
declare
your_table varchar2(40) := 'CHEMIN';
select_to_tmp varchar2(4000) := 'create table ttmp as select ';
begin
-- drop temporary table if exists
begin
execute immediate 'drop table ttmp';
Exception
When others Then
dbms_output.put_line(SQLERRM);
end;
for x in (
select column_name from all_tab_columns
where table_name=your_table
and column_name not in (
-- list columns you want to exclude
'COL_A'
, 'COL_B'
)
)
loop
select_to_tmp := select_to_tmp|| x.column_name ||',';
dbms_output.put_line(x.column_name);
end loop;
-- remove last ','
select_to_tmp := substr(select_to_tmp, 1, length(select_to_tmp) -1);
-- from your table
select_to_tmp := select_to_tmp||' from '||your_table;
-- add conditions if necessary
-- select_to_tmp := select_to_tmp|| ' where rownum < 1 '
dbms_output.put_line(select_to_tmp);
-- then create the temporary table using the query you generated:
execute immediate select_to_tmp;
end;
/
SELECT * FROM ttmp;
I want to display column based on certain condition, is it possible for that as i do this getting an error.
select (
select column_name
from all_tab_cols
where table_name='BED_2016_MAR_CIT4114A_FYP1_G_'
and column_name like '%na%'
)
from BED_2016_MAR_CIT4114A_FYP1_G_;
A SQL query must list the columns it is using explicitly. You can do what you want using dynamic SQL (execute immediate). For example:
declare
sql varchar2(4000);
cols varchar2(4000);
begin
select listagg(column_name, ',') within group (order by column_name)
into cols
from all_tab_cols
where table_name = 'BED_2016_MAR_CIT4114A_FYP1_G_' and column_name like '%na%' ;
sql := '
create table newtab as
select #cols
from BED_2016_MAR_CIT4114A_FYP1_G_';
sql := replace(sql, '#cols', cols);
execute immediate sql;
end;
select *
from newtab;
Looks like the problem is in the where condition:
where table_name='BED_2016_MAR_CIT4114A_FYP1_G_'
and column_name like '%na%'
Remove the condition for table_name. You are already selecting from BED_2016_MAR_CIT4114A_FYP1_G_ so only columns in that table will be shown.
Here is a more simplified version of the query:
select column_name
from BED_2016_MAR_CIT4114A_FYP1_G_
where column_name like '%na%'
Hope that helped
I would love to be able to use the system tables (Oracle in this case) to drive which fields are used in a SELECT statement. Something like:
SELECT
(
select column_name
from all_tab_cols
where table_Name='CLARITY_SER'
AND OWNER='CLARITY'
AND data_type='DATE'
)
FROM CLARITY_SER
This syntax doesn't work, as the subquery returns multiple rows, instead of one row with multiple columns.
Is it possible to generate a SQL statement dynamically by querying the table schema information in order to select only certain columns?
** edit **
Do this without using a function or procedure, if possible.
You can do this:
declare
l_sql varchar2(32767);
rc sys_refcursor;
begin
l_sql := 'select ';
for r in
( select column_name
from all_tab_cols
where table_Name='CLARITY_SER'
AND OWNER='CLARITY'
AND data_type='DATE'
)
loop
l_sql := l_sql || r.column_name || ',';
end loop;
l_sql := rtrim(l_sql,',') || ' from clarity_ser';
open rc for l_sql;
...
end;
No, it's not possible to specify a column list dynamically in SQL. You'll need to use a procedural language to run the first query, use that to construct a second query, then run the second query.
You could use dynamic SQL. Create a function that takes the table name, owner, data type, executes the inner query and returns a comma-separated list of column names, or an array table if you prefer. Then construct the outer query and execute it with execute immediate.
CREATE FUNCTION get_column_list(
table_name IN varchar2,
owner_name IN varchar2,
data_type IN varchar2)
RETURN varchar2
IS
BEGIN
...... (get columns and return comma-separated list)
END;
/
If your function returns a comma-separated list you can inline it:
execute immediate 'select ' || get_column_list(table_name, owner_name, datatype) || ' from ' || table_name
Admittedly it's a long time since I played with oracle so I may be a bit off but I'm pretty sure this is quite doable.
In SQLPlus you could do this:
COLUMN cols NEW_VALUE cols
SELECT max( ltrim( sys_connect_by_path( column_name, ',' ), ',' ) ) cols
FROM
(
select rownum rn, column_name
from all_tab_cols
where table_Name='CLARITY_SER'
and OWNER='CLARITY'
AND data_type='DATE'
)
start with rn = 1 connect by rn = prior rn +1
;
select &cols from clarity.clarity_ser;