Comma separated values to IN function in oracle - oracle

I am trying to execute below query, but not getting any result.
Could some one tell what wrong I am doing?.
DECLARE
object_types VARCHAR2(200);
v_object_types VARCHAR2(200);
l_count number;
BEGIN
object_types :='TABLE,VIEW';
select ''''||regexp_replace(object_types, '( )*(,)( )*',''',''')||''''
into v_object_types from dual;
dbms_output.put_line(to_char(v_object_types));
SELECT count(*) into l_count
FROM all_objects o where o.object_type IN ('||v_object_types||');
dbms_output.put_line(l_count);
END;

WHERE variable IN (1,2,3)
is different to what you are sending now
WHERE variable IN ('1,2,3') or
WHERE variable IN ('||v_object_types||')

You are trying to build the SQL dynamically but instead you are using a single string literal '||v_object_types||' in the IN clause.
You can do it using a collection:
Oracle Setup:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(200);
/
PL/SQL:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_object_types stringlist;
BEGIN
SELECT TRIM( BOTH FROM REGEXP_SUBSTR( object_types, '[^,]+', 1, LEVEL ) )
BULK COLLECT INTO v_object_types
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( object_types, '[^,]+' );
FOR i IN 1 .. v_object_types.COUNT LOOP
dbms_output.put_line( v_object_types(i) );
END LOOP;
SELECT count(*)
INTO l_count
FROM all_objects
WHERE object_type MEMBER OF v_object_types;
dbms_output.put_line(l_count);
END;
/
or using dynamic sql:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_sql VARCHAR2(200);
l_count INTEGER;
BEGIN
v_sql := 'SELECT count(*) FROM all_objects WHERE object_type IN ('
|| REGEXP_REPLACE( object_types, ' *(.+?) *(,|$)', '''\1''\2' )
|| ')';
EXECUTE IMMEDIATE v_sql INTO l_count;
dbms_output.put_line(l_count);
END;
/

Related

Oracle creating a dynamic plsql command

I'm trying to put together a procedure, which counts the rows for each PARTITION in a table but I'm getting a syntax error:
Errors: PROCEDURE COUNT_PARTITION
Line/Col: 14/31 PLS-00103: Encountered the symbol "(" when expecting one of the following:
I know this isn't the most efficient way and I can use the num_rows column along with gathering statistics to achieve the same results.
Below is my test CASE. I know the problem is with the construction of the 'cmd' call but I can't seem to get it to work. Any help would be greatly appreciated.
CREATE OR REPLACE PROCEDURE cmd(p_cmd varchar2)
authid current_user
is
BEGIN
dbms_output.put_line(p_cmd);
execute immediate p_cmd;
END;
/
CREATE OR REPLACE PROCEDURE
count_partition(
p_tab varchar2
) authid current_user
is
v_cnt integer;
BEGIN
for cur_rec in (select table_name, partition_name,
partition_position
FROM user_tab_partitions where table_name = p_tab order by partition_position) loop
cmd ('select count(*) /*+ parallel(a,8) */
from ' ||p_tab|| 'PARTITION ' ('||cur_rec.partition_name||')' INTO v_cnt);
DBMS_OUTPUT.PUT_LINE(cur_rec.table_name || ' ' || cur_rec.partition_name || ' ' || v_cnt || ' rows');
end loop;
END;
Created a procedure and wrapper
CREATE OR REPLACE PROCEDURE
count_partition(
p_tab varchar2
) authid current_user
is
sql_stmt varchar2(1024);
row_count number;
cursor get_tab is
select table_name,
partition_name
from user_tab_partitions
where table_name=p_tab;
BEGIN
for get_tab_rec in get_tab loop
BEGIN
sql_stmt := 'select count(*) /*+ parallel(a,4) */ from ' ||get_tab_rec.table_name||' partition ( '||get_tab_rec.partition_name||' )';
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt INTO row_count;
dbms_output.put_line('Table '||rpad(get_tab_rec.table_name
||'('||get_tab_rec.partition_name||')',50)
||' '||TO_CHAR(row_count)||' rows.');
exception when others then
dbms_output.put_line
('Error counting rows for table '||get_tab_rec.table_name);
END;
END LOOP;
END;
/
BEGIN
FOR cur_r IN(
SELECT TABLE_NAME FROM USER_PART_TABLES
)
LOOP
--DBMS_OUTPUT.put_line('Table '|| cur_r.table_name);
count_partition (cur_r.table_name);
END LOOP;
END;
~~

error ORA-00998: must name this expression with a column alias

I tried to create table from all_tab_columns but it throws me an error.
like error ORA-00998: must name this expression with a column alias.
i tried to figure it out but not working.
Declare
CURSOR c1 IS
SELECT COLUMN_NAME FROM (select 'standard_hash(MY_NAME) AS MY_NAME' COLUMN_NAME from DUAL
UNION
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='TABLE1' AND COLUMN_NAME<>'MY_NAME');
cols c1%ROWTYPE;
sqlstmt VARCHAR2(4000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM TABLE1';
DBMS_OUTPUT.PUT_LINE(sqlstmt);
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
You just have to give the name to the expression as follows:
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '
||substr(sqlstmt, 1, length(sqlstmt)-1)
||' as column_name FROM TABLE1'; -- column_name will bre name of the column of new table
As it will become the name of the column of newly created table.
I think this can be simplified somewhat.
You don't need to declare an explicit cursor and a record type and fetch each row into it, as this is already built into the language as the Cursor FOR loop construction:
declare
sqlStmt long := 'create table table2 as select ';
begin
for r in (
select 'standard_hash(my_name) as my_name' column_name
from dual
union
select column_name
from all_tab_columns
where table_name = 'TABLE1'
and column_name <> 'MY_NAME'
)
loop
sqlStmt := sqlStmt || r.column_name || ', ';
end loop;
sqlStmt := sqlStmt || rtrim(sqlStmt,', ') || ' from table1';
dbms_output.put_line(sqlStmt);
execute immediate sqlStmt;
end;
/
But you can do this without any loop, as the listagg function can already build a comma-separated list for you. It also makes it easier to retain the order of columns in the original table.
declare
selectList long;
sqlStmt long;
begin
select listagg(
case column_name
when 'MY_NAME' then 'standard_hash(my_name) as my_name'
else lower(column_name)
end, ', '
) within group (order by column_id) as select_list
into selectList
from user_tab_columns c
where c.table_name = 'TABLE1';
sqlStmt :=
'create table table2 pctfree 0 nologging parallel as '||chr(10)||
'select '||selectList||chr(10)||
'from table1';
dbms_output.put_line(sqlStmt||';');
execute immediate sqlstmt;
end;

Using variable in FROM clause pl/sql

SET SERVEROUTPUT ON
DECLARE
table_name varchar2(80) := 'dual';
BEGIN
SELECT * FROM table_name WHERE dummy = 'X';
END;
The above code throws error. I want to use a variable in the from clause.
For a dynamic query you can use EXECUTE IMMEDIATE.
DECLARE
table_name VARCHAR2 (80) := 'dual';
v_query VARCHAR2 (200);
BEGIN
v_query := 'SELECT *
FROM ' || table_name || '
WHERE dummy = ''X''';
EXECUTE IMMEDIATE v_query;
END;

How to execute the output result for execute immediate statment

From the below query i am getting the output as below. but i am expecting the output result like the output select query to be executed again and provide the result .
Also, i want to remove the UNION ALL from last line of the output like this
select A from Table1 Union all
Select A from table2 Union All
Select A from table3 Union all
Select A from table4;
Query :
SET SERVEROUTPUT ON
Declare
v_sql varchar2(500);
v_sql2 varchar2(500);
v_prj_id varchar2(4000):='ProjectA,ProjectB,ProjectC,ProjectD';
BEGIN
Dbms_Output.Put_Line('v_prj_id='||v_prj_id);
--
FOR i IN (SELECT trim(regexp_substr(v_prj_id, '[^,]+', 1, LEVEL)) l
FROM dual
CONNECT BY LEVEL <= regexp_count(v_prj_id, ',') + 1
) LOOP
v_sql := 'select '''|| i.l ||''' AS "PRJ_ID", EVENT, email,modified_by,modified from ' || i.l ||
'.Table UNION ALL';
Dbms_Output.Put_Line (v_sql);
--
END LOOP;
EXECUTE IMMEDIATE v_sql ;
v_sql2 := RTRIM (v_sql, 'UNION ALL');
EXECUTE IMMEDIATE v_sql2 ;
Dbms_Output.Put_Line (v_sql2);
END;
/
Output :
select 'ProjectA' AS "PRJ_ID", EVENT, email,modified_by,modified from ProjectA.event UNION ALL
select 'ProjectB' AS "PRJ_ID", EVENT, email,modified_by,modified from ProjectB.event UNION ALL
select 'ProjectC' AS "PRJ_ID", EVENT, email,modified_by,modified from ProjectC.event UNION ALL
select 'ProjectD' AS "PRJ_ID", EVENT, email,modified_by,modified from ProjectD.event UNION ALL
EXECUTE IMMEDIATE will not provide you the SELECT statement results.
From Oracle Docs
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then execute_immediate_statement never executes.
You have use SELECT INTO clause or BULK_COLLECT_INTO_CLAUSE and assign the values to some variable if you want to see the results of EXECUTE IMMEDIATE select values.
Something like below for single row
SET SERVEROUTPUT ON
DECLARE
L_RESULT VARCHAR2(20);
BEGIN
VSQL:= --YOUR LOGIC
EXECUTE IMMEDIATE VSQL INTO L_RESULT;
DBMS_OUTPUT.PUT_LINE(L_RESULT);
END;
/
For Multiple rows
DECLARE
TYPE T IS TABLE OF MYTABLE.ID%TYPE INDEX BY PLS_INTEGER;
MYROW T;
BEGIN
VSQL:= --YOUR LOGIC
EXECUTE IMMEDIATE VSQL BULK COLLECT INTO MYROW;
DBMS_OUTPUT.PUT_LINE(MYROW.XXX);
END;
/
You are doing everything right except concatenating the v_sql with original string -
SET SERVEROUTPUT ON
Declare
v_sql varchar2(500);
v_sql2 varchar2(500);
v_prj_id varchar2(4000):='ProjectA,ProjectB,ProjectC,ProjectD';
BEGIN
Dbms_Output.Put_Line('v_prj_id='||v_prj_id);
--
FOR i IN (SELECT trim(regexp_substr(v_prj_id, '[^,]+', 1, LEVEL)) l
FROM dual
CONNECT BY LEVEL <= regexp_count(v_prj_id, ',') + 1
) LOOP
v_sql := v_sql || ' select '''|| i.l ||''' AS "PRJ_ID", EVENT, email, modified_by, modified from ' || i.l ||
'.Table UNION ALL';
Dbms_Output.Put_Line (v_sql);
--
END LOOP;
EXECUTE IMMEDIATE v_sql ;
v_sql2 := RTRIM (v_sql, 'UNION ALL');
EXECUTE IMMEDIATE v_sql2 ;
Dbms_Output.Put_Line (v_sql2);
END;
/

How to insert multiple row result of dynamic sql to another Table?

I write one dynamic SQL which the result of it is a table with 2 columns and multiple rows, I want to insert it to another table with 4 columns that 2 of them will be filled by the result of dynamic SQL, I try to use collection but don't know how to insert result to another table
CREATE OR REPLACE PROCEDURE P_C_SM_Failure_error_Code_P2P AS
v_month VARCHAR2(16); -- to get Month for each table
v_day VARCHAR2(16); -- to get day for each table
v_ERRCODE t_c_rpt_resultmsg.code%TYPE;
v_ERRMSG t_c_rpt_resultmsg.MESSAGE%TYPE;
v_param VARCHAR2(16);
v_sql VARCHAR2(3000);
v_result number;
type t_c_result is record (Err_code varchar2(2000), Err_count number);
type v_t_result is table of t_c_result index by PLS_INTEGER;
v_t1_result v_t_result;
BEGIN
v_sql :='0';
v_param := 'Gateway_G';
v_result := '0';
select to_char(sysdate - 1,'MM') into v_month from dual;
select to_char(sysdate - 1,'DD') into v_day from dual;
-- Get count of P2P
v_sql := '(select count(*), error_code from (
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC01 where
orgaccount = '''||v_param||''' and destaccount = '''||v_param||''' and
sm_status <> 1 union all
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC02 where
orgaccount = '''||v_param||''' and destaccount = '''||v_param||''' and
sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql bulk collect into v_t1_result;
--insert into t_c_rpt_result2 values (trunc(sysdate, 'DD'), v_errcount,
v_err_code,'Failure_error_Code_P2P');
--for indx in 1 .. v_t1_result.COUNT
--loop
--dbms_output.put_line (v_t1_result (indx).Err_code);
--end loop;
You may append the constant values of date and the error message to the subquery and run a dynamic insert. It should also work if you remove the outer parentheses of your dynamic sql since constants can be included in group by. Always remember to pass values as bind variables rather than concatenating them (v_param). Also, specify the column names explicitly in an INSERT statement.
v_sql := '(select count(*) as cnt, error_code
from (
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC01
where orgaccount = :x and destaccount = :x and sm_status <> 1
union all
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC02
where orgaccount = :x and destaccount = :x and sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql bulk collect into v_t1_result using v_param;
EXECUTE IMMEDIATE 'insert into t_c_rpt_result2(err_dt,err_msg,errcount,error_code)
select :dt,:msg,cnt,error_code from '|| v_sql
USING trunc(sysdate, 'DD'),'Failure_error_Code_P2P',v_param;
I think you are looking at an excellent use case for FORALL. The collection you are populating needs to be done with execute immediate since you are dynamically constructing the table name. But the insert into t_c_rpt_result2 looks static to me.
BEGIN
v_sql :=
'(select count(*) as cnt, error_code
from (
select error_code from sm_histable'
|| v_month
|| ''
|| v_day
|| '#ORASMSC01
where orgaccount = :x and destaccount = :x and sm_status <> 1
union all
select error_code from sm_histable'
|| v_month
|| ''
|| v_day
|| '#ORASMSC02
where orgaccount = :x and destaccount = :x and sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO v_t1_result USING v_param;
FORALL indx IN 1 .. v_t1_result.COUNT
INSERT INTO t_c_rpt_result2 (err_dt,
err_msg,
errcount,
ERROR_CODE)
VALUES (TRUNC (SYSDATE, 'DD'),
'Failure_error_Code_P2P',
v_t1_result (indx).cnt,
v_t1_result (indx).ERROR_CODE);
END;
Find more examples of FORALL on LiveSQL here. Of course, even if your insert was dynamic, you can use FORALL - put the execute immediate directly "inside" the FORALL statement. But I don't think that complexity is justified here.
Hope that helps!

Resources