I extract data from a table, the field is mostly null, but sometimes it's sysdate. However since its from a table, after getting the field its 'sysdate', between single quotes. How can I use it?
I have tried to_date, to_date(to_char()).
I need something that works within
select to_date('sysdate') from dual;
You can use a case expression:
select case
when the_column = 'sysdate' then sysdate
else to_date(the_column)
end as date_value
from the_table;
The only way I know is dynamic SQL. Here's an example:
SQL> create table test (id number, col varchar2(20));
Table created.
SQL> insert into test
2 select 1, '''sysdate''' from dual union all
3 select 2, null from dual;
2 rows created.
SQL> declare
2 l_res test%rowtype;
3 l_str varchar2(200);
4 begin
5 for cur_r in (select id, col from test) loop
6 l_str := 'select ' || cur_r.id ||', '||
7 nvl(replace(cur_r.col, chr(39), null), 'null') || ' from dual';
8 execute immediate l_str into l_res;
9 dbms_output.put_line(l_res.id ||': '|| l_res.col);
10 end loop;
11 end;
12 /
1: 24.06.2019 12:18:39
2:
PL/SQL procedure successfully completed.
SQL>
Related
I would like to construct a query where a table name is based off of another table's column mod 12. For example:
SELECT *
FROM table_b_XX
where XX here is determined by table_a.column_a % 12.
Presuming you have such a tables:
SQL> create table table_a as
2 select 1212 as column_a from dual;
Table created.
As the following result returns 0, we need table_b_00 so I'll create it:
SQL> select mod(1212, 12) from dual;
MOD(1212,12)
------------
0
SQL> create table table_b_00 as select 'table 00' name from dual;
Table created.
SQL> create table table_b_01 as select 'table 01' name from dual;
Table created.
Now, create a function which returns ref cursor; it selects rows from a table whose name is designed by the help of the table_a contents:
SQL> create or replace function f_test return sys_refcursor
2 is
3 l_str varchar2(200);
4 rc sys_refcursor;
5 begin
6 select 'select * from table_b_' || lpad(mod(a.column_a, 12), 2, '0')
7 into l_str
8 from table_a a;
9
10 open rc for l_str;
11 return rc;
12 end f_test;
13 /
Function created.
Let's try it:
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
NAME
--------
table 00
Right; that's contents of table_b_00.
Consider the following meta code:
DECLARE
n VARCHAR2(32767);
r VARCHAR2(32767);
BEGIN
SELECT column_a INTO name FROM table_a;
EXECUTE IMMEDIATE 'SELECT r FROM table_b_'||n INTO r;
END;
/
I'm trying to move rows between two tables which have many columns.
The table columns are identical other than the destination table (tab#db2) has a few more columns which causes a simple INSERT to fail.
I'd like to use a simple PL/SQL statement to build a list of the columns in tab#db2 dynamically instead of typing out the names of col1, col2, etc in the INSERT and SELECT clause. Example
declare a variable as var_col_list
set col_list = output of select * from tab (omitting rows)
INSERT INTO TAB *var_col_list* SELECT *var_cols_list* FROM TABLE TAB#db2
I've researched using %rowtype but cannot find a suitable example that would take less time than simply writing out the names of the columns!
Any advice is greatly appreciated
If you use e.g. TOAD, you can right-click the table and let it Generate statement - in your case, that would be INSERT. You'd slightly modify it (remove columns you don't need) and that's all.
Otherwise, this is how you might do it semi-manually.
This is my source table:
SQL> SELECT * FROM dept;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Target table doesn't contain all columns:
SQL> CREATE TABLE target
2 (
3 deptno NUMBER,
4 dname VARCHAR2 (20)
5 );
Table created.
Code which loops through all TARGET table columns (i.e. a table which has less columns) and composes the INSERT INTO statement:
SQL> DECLARE
2 l_str VARCHAR2 (1000);
3 BEGIN
4 FOR cur_r IN (SELECT column_name
5 FROM user_tab_columns
6 WHERE table_name = 'TARGET')
7 LOOP
8 l_str := l_str || ', ' || cur_r.column_name;
9 END LOOP;
10
11 l_str :=
12 'insert into target select ' || LTRIM (l_str, ', ') || ' from dept';
13 DBMS_OUTPUT.put_line (l_str);
14
15 EXECUTE IMMEDIATE l_str;
16 END;
17 /
insert into target select DEPTNO, DNAME from dept --> this is the L_STR contents
PL/SQL procedure successfully completed.
SQL> SELECT * FROM target;
DEPTNO DNAME
---------- --------------------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
Seems to be OK.
Using the solution provided by Littefoot, I made some minor tweaks to fit my requirement perfectly:
SQL> create table taba (col1 number,col2 number);
SQL> insert into taba values (1,2);
SQL> select * from taba;
COL1 COL2
---------- ----------
1 2
SQL> create table tabb (col1 number,col2 number, col3 number);
SQL> DECLARE
l_str VARCHAR2 (32767);
BEGIN
FOR cur_r IN (SELECT column_name
FROM user_tab_columns
WHERE table_name = 'TABA'
order by column_id asc)
LOOP
l_str := l_str || ', ' || cur_r.column_name;
END LOOP;
l_str :=
'insert into tabb (' || LTRIM (l_str, ', ') || ') ' ||' select ' || LTRIM (l_str, ', ') || ' from taba';
DBMS_OUTPUT.put_line (l_str);
EXECUTE IMMEDIATE l_str;
END;
/
Output of l_str (SQL INSERT):
insert into tabb (COL1, COL2) select COL1, COL2 from taba
Result:
SQL> select * from tabb;
COL1 COL2 COL3
---------- ---------- ----------
1 2
I'm trying to build a dynamic function in Oracle using a cursor for all the tables that need to be dropped and re-created again. For example, I have the following example table structure:
CREATE TABLE All_tmp_DATA AS
(SELECT 'T_tmp_test1' As Table_NM, 'TEST1' As Process_name FROM DUAL UNION ALL
SELECT 'T_tmp_test2' As Table_NM, 'TEST1' As Process_name FROM DUAL UNION ALL
SELECT 'T_tmp_test3' As Table_NM, 'TEST1' As Process_name FROM DUAL)
The above tables starting with "T_tmp" represent all the tables in the database which needs to be dropped if their counts are >1 when starting the TEST1 process. I really need a function to pass in the parameter Process_name where I can input "TEST1", and build a loop using a cursor by binding it to the Table_NM from All_tmp_DATA and inserting it into table_name in the following code:
BEGIN
SELECT count(*)
INTO l_cnt
FROM user_tables
WHERE table_name = 'MY_TABLE';
IF l_cnt = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE my_table';
END IF;
END;
In the beginning, I'd suggest you not to use mixed case when naming Oracle objects.
Test case:
SQL> select * From all_tmp_data;
TABLE_NM PROCE
----------- -----
T_tmp_test1 TEST1
T_tmp_test2 TEST1
T_tmp_test3 TEST1
SQL> create table "T_tmp_test1" as select * From dept;
Table created.
SQL> -- I don't have "T_tmp_test2"
SQL> create table "T_tmp_test3" as select * From emp;
Table created.
SQL>
SQL> select table_name From user_Tables where upper(table_name) like 'T_TMP%';
TABLE_NAME
------------------------------
T_tmp_test3
T_tmp_test1
Procedure which drops tables contained in ALL_TMP_DATA:
as opposed to your code, I concatenated table name with DROP
as you use table names with mixed case, you have to enclose their names into double quotes, always (did I say not do use that?)
As the final select shows, those tables don't exist any more.
SQL> declare
2 l_cnt number;
3 begin
4 for cur_r in (select table_nm from all_tmp_data) loop
5 select count(*) into l_cnt
6 from user_tables
7 where table_name = cur_r.table_nm;
8
9 if l_cnt > 0 then
10 execute immediate ('drop table "' || cur_r.table_nm || '"');
11 end if;
12 end loop;
13 end;
14 /
PL/SQL procedure successfully completed.
SQL> select table_name From user_Tables where upper(table_name) like 'T_TMP%';
no rows selected
SQL>
As of the process column: I have no idea what is it used for so I did exactly that - didn't use it.
You can use the exception handling to handle such scenario directly as follows:
DECLARE
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT ( TABLE_DOES_NOT_EXIST, -00942 );
BEGIN
FOR CUR_R IN (
SELECT TABLE_NM
FROM ALL_TMP_DATA
) LOOP
BEGIN
EXECUTE IMMEDIATE 'drop table "' || cur_r.table_nm || '"';
DBMS_OUTPUT.PUT_LINE('"' || cur_r.table_nm || '" table dropped.');
EXCEPTION
WHEN TABLE_DOES_NOT_EXIST THEN
DBMS_OUTPUT.PUT_LINE('"' || cur_r.table_nm || '" table does not exists');
END;
END LOOP;
END;
/
When I run this code I didn't get the right data from the standard table
create table COUNTRY_XML(
C_X sys.xmltype);
begin
for cursor in
(select country, continental, population2019 from countries)
loop
insert into country_xml values(
sys.xmltype.createXML(
'<ac_x createdby="Guangzhe">
<country_info>
<Country>mycursor.country</Country>
<Continental>mycursor.continental</Continental>
<Population2019>mycursor.population2019</Population2019>
</country_info>
</ac_x>'));
end loop;
end;
select c.c_x.extract('/').getstringval() from country_xml c
There results are as follow and they are all the same things for each row.
"<ac_x createdby="Guangzhe">
<country_info>
<Country>mycursor.country</Country>
<Continental>mycursor.continental</Continental>
<Population2019>mycursor.population2019</Population2019>
</country_info>
</ac_x>"
You are not doing concatnation properly.
You need to use || (concatanation operator) as following
Table creation
SQL> CREATE TABLE COUNTRY_XML (
2 C_X SYS.XMLTYPE
3 );
Table created.
Solution you need:
SQL>
SQL> begin
2 for mycursor in -- changed the name
3 (select 'INDIA' as country, 'ASIA' as continental, '130B' as population2019 from dual)
4 --(select country, continental, population2019 from countries)
5 loop
6 insert into country_xml values(
7 sys.xmltype.createXML(
8 '<ac_x createdby="Guangzhe">
9 <country_info>
10 <Country>' || mycursor.country || '</Country>
11 <Continental>' || mycursor.continental|| '</Continental>
12 <Population2019>' || mycursor.population2019|| '</Population2019>
13 </country_info>
14 </ac_x>'));
15 end loop;
16 end;
17 /
PL/SQL procedure successfully completed.
Output
SQL> SELECT C.C_X.EXTRACT('/').GETSTRINGVAL()
2 FROM COUNTRY_XML C
3 ;
C.C_X.EXTRACT('/').GETSTRINGVAL()
--------------------------------------------------------------------------------
<ac_x createdby="Guangzhe">
<country_info>
<Country>INDIA</Country>
<Continental>ASIA</Continental>
<Population2019>130B</Population2019>
</country_info>
</ac_x>
SQL>
Cheers!!
I'm try to loop some tables and run select as below:
set serveroutput on
declare
type tables_names is table of varchar2(30);
type selectTable is table of varchar2(30);
tName tables_names;
sTableName selectTable;
begin;
tName := tables_names('PERIOD','SETTING','RAP','LOG');
sTableName := selectTable('m_table1','m_table2','m_table3','m_table4','m_table5');
for i in 1..tName.count loop
for j in 1..sTableName.count loop
select col10, count(*) from user.sTableName(j)
where table_name = tName(i) group by col10;
end loop;
end loop;
end;
I got error:PL/SQL: ORA-00933.
Can you please tell me how can I correctly run PL/SQL procedure to have displayed result from my select?
UPDATE: looking result
Normally, to get this I need to run below select's:
select column_name,
count(*) as countColumn
from user.m_table1 where table_name = 'PERIOD' group by column_name;
select column_name,
count(*) as countColumn
from user.m_table2 where table_name = 'PERIOD' group by column_name;
Oracle complains (ORA-00933) that command isn't properly ended. That's probably because of a semi-colon behind the BEGIN; also, you lack the INTO clause.
I'm not sure what PERIOD, SETTING, ... are opposed to m_table1, m_table2, ... Which ones of those are table names? What are those other values, then?
Anyway: here's an example which shows how to do something like that - counting rows from tables. Try to adjust it to your situation, or - possibly - add some more info so that we'd know what you are doing.
SQL> set serveroutput on
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 tname := sys.odcivarchar2list('EMP', 'DEPT');
7
8 for i in 1 .. tname.count loop
9 l_str := 'select count(*) from ' || tname(i);
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(tname(i) ||': '|| l_cnt);
12 end loop;
13 end;
14 /
EMP: 14
DEPT: 4
PL/SQL procedure successfully completed.
SQL>
[EDIT: added GROUP BY option]
Here you go; as EMP and DEPT share the DEPTNO column, I chose it for a GROUP BY column.
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 type t_job is record (deptno varchar2(20), cnt number);
4 type t_tjob is table of t_job;
5 l_tjob t_tjob := t_tjob();
6 l_str varchar2(200);
7 begin
8 tname := sys.odcivarchar2list('EMP', 'DEPT');
9
10 for i in 1 .. tname.count loop
11 l_str := 'select deptno, count(*) from ' || tname(i) ||' group by deptno';
12 execute immediate l_str bulk collect into l_tjob;
13
14 for j in l_tjob.first .. l_tjob.last loop
15 dbms_output.put_Line('Table ' || tname(i) || ': Deptno ' || l_tjob(j).deptno||
16 ': number of rows = '|| l_tjob(j).cnt);
17 end loop;
18
19 end loop;
20 end;
21 /
Table EMP: Deptno 30: number of rows = 6
Table EMP: Deptno 20: number of rows = 5
Table EMP: Deptno 10: number of rows = 3
Table DEPT: Deptno 10: number of rows = 1
Table DEPT: Deptno 20: number of rows = 1
Table DEPT: Deptno 30: number of rows = 1
Table DEPT: Deptno 40: number of rows = 1
PL/SQL procedure successfully completed.
SQL>
You are probably looking for something like this. Note that you can't run a simple select statement inside a PL/SQL without INTO clause. use a refcursor and DBMS_SQL.RETURN_RESULT
DECLARE
TYPE tables_names IS TABLE OF VARCHAR2 (30);
TYPE selectTable IS TABLE OF VARCHAR2 (30);
tName tables_names;
sTableName selectTable;
rc SYS_REFCURSOR;
BEGIN
tName :=
tables_names ('PERIOD',
'SETTING',
'RAP',
'LOG');
sTableName :=
selectTable ('m_table1',
'm_table2',
'm_table3',
'm_table4',
'm_table5');
FOR i IN 1 .. tName.COUNT
LOOP
FOR j IN 1 .. sTableName.COUNT
LOOP
OPEN rc FOR
'select col10, count(*) from '||USER||'.'
|| sTableName (j)
|| ' where table_name = '''
|| tName (i)
|| ''' group by col10';
DBMS_SQL.RETURN_RESULT (rc);
END LOOP;
END LOOP;
END;
/