How to dynamically construct table name - sql

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;
/

Related

Convert string from a table to be used as a column for selection for another table

I have a table where I store records to be used as a column name for my queries where the record is an actual column on another table.
TBL_1
COL_1
==========
SAMPLE_COL
TBL_2
SAMPLE_COL_1 SAMPLE_COL2
============ ===========
ABC DEF
I'm having a problem using the record that I fetched to use as an actual column. I already tried a bunch of things like casting and using case (using case works but it's a bit of a brute force and I'm looking for a more elegant way of doing this).
This is a sample query that I have tried:
SELECT (SELECT column_1 FROM tbl_1)
FROM tbl_2
Expected output
SAMPLE_COL_1
============
ABC
Actual output
(SELECT column_1 FROM tbl_1)
============================
SAMPLE_COL_1
This is what I've tried that worked so far but a brute force technique
SELECT (
CASE
WHEN (SELECT column_1 FROM tbl_2) = 'SAMPLE_COL_1' THEN SAMPLE_COL_1
ELSE SAMPLE_COL_2
END
)
FROM tbl_2
Appreciate the help! Keep safe from COVID-19 everyone :)
It's not that easy as you'd want it to be - you'll have to use dynamic SQL. Here's an example, based on Scott's table(s).
Create a function that accepts table and column names and returns ref cursor.
SQL> create or replace function f_test
2 (par_table_name in varchar2, par_column_name in varchar2)
3 return sys_refcursor
4 is
5 rc sys_refcursor;
6 begin
7 open rc for 'select ' || dbms_assert.simple_sql_name(par_column_name) ||
8 ' from ' || dbms_assert.sql_object_name(par_table_name);
9 return rc;
10 end;
11 /
Function created.
Testing:
SQL> select f_test('dept', 'dname') from dual;
F_TEST('DEPT','DNAME
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME
--------------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
SQL> select f_test('dual', 'dummy') from dual;
F_TEST('DUAL','DUMMY
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
D
-
X
SQL>
Another example, with column (and table) names stored in a table (something like you posted).
Table that contains those info and the function:
SQL> select * from tbl_1;
TNAM CNAME
---- -----
dept dname
dual dummy
SQL> create or replace function f_test
2 (par_table_name in varchar2)
3 return sys_refcursor
4 is
5 l_str varchar2(1000);
6 rc sys_refcursor;
7 begin
8 select 'select ' || dbms_assert.simple_sql_name(cname) ||
9 ' from ' || dbms_assert.sql_object_name(tname)
10 into l_str
11 from tbl_1
12 where tname = dbms_assert.sql_object_name(par_table_name);
13 open rc for l_str;
14 return rc;
15 end;
16 /
Function created.
Testing:
SQL> select f_test('dept') from dual;
F_TEST('DEPT')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME
--------------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
SQL>

Oracle- creating dynamic function for deleting tables based on cursor

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;
/

How to use 'sysdate' if its a string constant

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>

How to execute DDLs conditionally in oracle?

I have a script that creates a lot of tables, indexes, triggers etc. And I want to run all those DDLs conditionally. i tried to wrap the script with 'if then' but it didn't work
IF exists (select 1 from xxx where yyy) THEN
create table...
create table...
CREATE UNIQUE INDEX ...
CREATE TRIGGER ...
END IF;
how can i achieve that?
One of the ways:
begin
for cur in (select 1 from xxx where yyy and rownum <= 1) loop
execute immediate 'create table...';
execute immediate 'create table...';
execute immediate 'create unique index...';
end loop;
end;
/
P.S. One more way is to generate exception and proceed in SQL*Plus.
file example.sql:
SET ECHO OFF
SET VERIFY OFF
WHENEVER SQLERROR EXIT;
VAR x NUMBER
EXEC :x := &1
BEGIN
FOR cur IN (SELECT 1 FROM dual WHERE 1=:x) LOOP
RETURN;
END LOOP;
RAISE NO_DATA_FOUND;
END;
/
PROMPT Here we are
Result:
SQL> #example
Enter value for 1 1: 1
PL/SQL procedure completed.
PL/SQL procedure completed.
Here we are
SQL> #example
Enter value for 1: 2
PL/SQL procedure completed.
BEGIN
*
error in line 1:
ORA-01403: no data found
ORA-06512: in line 5
When I use value 2 the block raises exception and the script exists SQL*Plus.
P.S. One more example in response - hope this cleas questions below. I create table only when it does not exist. Table containts 1024 partitions and ' characters in DEFAULT statement. Text size > 32K.
SQL> set serveroutput on
SQL> DECLARE
2 sql_code clob;
3 delim varchar2(1) := '';
4 amount int;
5 sql_text varchar2(32767);
6 BEGIN
7
8 dbms_lob.createtemporary(sql_code,cache => true);
9 sql_text := q'[CREATE TABLE TEST_TAB (X INT PRIMARY KEY, Y VARCHAR2(10) DEFAULT 'DEF', Z INTEGER) PARTITION BY RANGE(Z) ( ]';
10 amount := length(sql_text);
11 dbms_lob.writeappend(sql_code,amount,sql_text);
12
13 for i in 1..1024 loop
14 sql_text := delim||'PARTITION P_'||i||' VALUES LESS THAN ('||i||')';
15 amount := length(sql_text);
16 dbms_lob.writeappend(sql_code,amount,sql_text);
17 delim := ',';
18 end loop;
19
20 dbms_lob.writeappend(sql_code,1,')');
21
22 FOR cur IN (
23 SELECT * FROM dual WHERE NOT EXISTS (
24 SELECT * FROM user_tables WHERE table_name = 'TEST_TAB')
25 ) LOOP
26 EXECUTE IMMEDIATE sql_code;
27 END LOOP;
28
29 dbms_output.put_line(dbms_lob.getlength(lob_loc => sql_code));
30
31 END;
32 /
39877
PL/SQL procedure completed.
SQL> desc test_tab
Имя Пусто? Тип
----------------------------------------- -------- ----------------------------
X NOT NULL NUMBER(38)
Y VARCHAR2(10)
Z NUMBER(38)
SQL> select count(*) from user_tab_partitions where table_name = 'TEST_TAB';
COUNT(*)
----------
1024

If I turn a collection of Number into a table, what's the name of the column? 10gR2

If I wanted to replace the * with a column name, what would it be?
create type mytable$t as table of number;
/
declare
mytmou mytable$t := myTable$T();
cnt pls_integer ;
begin
mytmou := myTable$T(1,2,3,4,5,6);
SELECT count(*) into cnt From Table (mytmou);
dbms_output.put_line(cnt);
end;
6
COLUMN_VALUE is the name of the column
SQL> ed
Wrote file afiedt.buf
1 declare
2 mytmou mytable$t := myTable$T();
3 cnt pls_integer ;
4 begin
5 mytmou := myTable$T(1,2,3,4,5,6);
6 SELECT count(column_value) into cnt From Table (mytmou);
7 dbms_output.put_line(cnt);
8* end;
SQL> /
6
PL/SQL procedure successfully completed.