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;
Related
I have a requirement to translate it to an SQL script.
I am using the information schema to get all the columns of a table and print their distinct count.
I was able to get the count, but not able to print the column name properly,
PFA the below code.
I have to pass the value of the "colum_lbl" to my select clause, if I do so it is giving me an group by error.
So I passed the "colum_lbl" within quotes. now all the values of the result has hardcoded 'colum_lbl' as value, I have to replace it with the original value I read from the for Loop
Any other efficient method for this requirement will be very much appreciated. Thanks in advance
do $$
DECLARE
colum_lbl text;
BEGIN
DROP TABLE IF EXISTS tmp_table;
CREATE TABLE tmp_table
(
colnm varchar(50),
cnt integer
);
FOR colum_lbl IN
SELECT distinct column_name
FROM information_schema.columns
WHERE table_schema = 'cva_aggr'
AND table_name = 'employee' AND column_name in ('empid','empnm')
LOOP
EXECUTE
'Insert into tmp_table
SELECT '' || colum_lbl || '',count(distinct ' || colum_lbl || ')
FROM employee ';
END LOOP;
END; $$
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;
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
I have engine_id column in various tables in the schema. So I want to count the number of rows based on engine_id column in the whole schema where this column exists.
Select count(*)
from table_name
where table_name.engine_id = 8;
Without user-defined PL/SQL, using only built-in Oracle functionality...
Oracle 11g (and maybe even lower) query:
select TC.table_name, X.*
from user_tab_columns TC
cross join xmltable(
'/ROWSET/ROW/CNT'
passing
dbms_xmlgen.getXMLType('
select count(1) as cnt
from '||TC.table_name||'
where &columnName = &columnValueAsLiteral
')
columns
cnt integer
) X
where TC.column_name = '&columnName'
;
Oracle 12c+ query:
select TC.table_name, X.*
from user_tab_columns TC
cross apply xmltable(
'/ROWSET/ROW/CNT'
passing
dbms_xmlgen.getXMLType('
select count(1) as cnt
from '||TC.table_name||'
where &columnName = &columnValueAsLiteral
')
columns
cnt integer
) X
where TC.column_name = '&columnName'
;
In your case supply
&columnName as ENGINE_ID,
&columnValueAsLiteral as 8.
Note: It could also be possible with the with-PLSQL clause of 12c's but I somehow can't make it work, hence I'm not posting that solution here.
These are the steps which you should be following
Get all the tables which have the column.
select table_name from all_tab_columns
where column_name = 'ENGINE_ID';
Create the above as a cursor and run a loop on it
for records in above_cursor
loop
execute immediate 'select count(*) from ' || records.table_name || 'where engine_id = 8' into some_temp_number_var;
some_total_number_var := some_temp_number_var + some_total_number_var;
end loop;
You will need PL code that would query the data dictionary view USER_TABLES to find out which tables have the column you seek and then build a dynamic SQL query as per your filter requirements.
Your function would be something like this:
create or replace function find_count (p_column_name varchar2,
p_column_value number)
return number is
v_sql clob; HERE
v_count number;
begin
for i in (select table_name
from user_tab_cols
where column_name = upper(v_column_name)) loop
v_sql :=
v_sql
|| 'select count(*) as cnt from '
|| i.table_name
|| ' where '
|| p_column_name
|| ' = '
|| p_column_value;
v_sql := v_sql || chr (10) || 'union all ';
end loop;
v_sql := substr (v_sql, 1, length (v_sql) - 11);
v_sql := 'select sum(cnt) from (' || v_sql || ')';
execute immediate v_sql into v_count;
return v_count;
end find_count;
/
You can trigger this function using a query like this:
select find_count('ENTITY_ID', 1012) as engine_count from dual;
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;