Im trying to implement an IF statement to run a query on my Oracle Database.
The expectation:
If the employee is called John, display the limited items(item1 and item2), if it's not John, display the complete list.
DECLARE
employeeName STRING := 'John';
BEGIN
IF (employeeName = 'John') THEN
(SELECT TableA.Table from Table where TableA.Table = 'Item1' OR TableA.Table = 'Item2');
ELSE
(SELECT TableA.Table from Table);
END IF;
END;
Here's the error output:
Error report: ORA-06550: line 6, column 2: PLS-00103: Encountered the symbol " " when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma raise
return select update while with 'an identifier' 'a double-quoted
delimited-identifier' 'a bind variable' ""continue close current
delete fetch lock insert open rollback savepoint set sql execute
commit forall merge pipe purge
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
What am I doing wrong? Forgive my ignorance on IF statements implemented on databases.
In your code,
table is a reserved keyword. Use double quotes or a different identifier
select is missing into.
String is missing length
You can apply your condition in a single SQL and then, use a for loop to loop over the result set.
DECLARE
employeeName varchar2(100) := 'John';
BEGIN
for t in (select "table" from tablea
where "table" in ('item1', 'item2')
or employeename = 'john') loop
dbms_output.put_line(t."table");
end loop;
END;
/
Related
I have created following stored procedure to update recon_dashboard table.
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alvid from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(select count(*) from ' || f.alv || ')
where id='|| f.id;
end loop;
END;
$$;
It works fine unless there is a null value in the alv, then it throws an error.
I tried to check for null like:
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alv,id from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(IF '||f.alv||' is not null
then (select count(*) from ' || f.alv || ')
END IF;)
where id='|| f.id;
end loop;
END;
$$;
But it throws error when I try to call the SP.
Error: SQL Error [42601]: ERROR: syntax error at or near "ca_view"
Where: PL/pgSQL function "sp_count_recon_refresh" line 7 at execute statement
In error "ca_view" is a recod. alv column has values like 'ca_view.table1', 'ca_view.table2' and so on.
This probably fixes your problems:
CREATE OR REPLACE PROCEDURE ca_adhoc_view.sp_count_recon_refresh()
LANGUAGE plpgsql AS
$func$
DECLARE
f record;
BEGIN
FOR f IN
SELECT alv, id FROM ca_adhoc_view.recon_dashboard
LOOP
RAISE NOTICE '% alv', f.alv;
IF f.alv IS NOT NULL THEN
EXECUTE format(
'UPDATE ca_adhoc_view.recon_dashboard
SET alv_count = (SELECT count(*) FROM %I)
WHERE id = $1', f.alv)
USING f.id;
END IF;
END LOOP;
END
$func$;
"Problems" (plural). Besides defending against a NULL value for the table name with proper syntax, this also prevents SQL injection. Your original is wide open there. Related:
PostgreSQL - Writing dynamic sql in stored procedure that returns a result set
Table name as a PostgreSQL function parameter
That said, it would be more efficient to run a single UPDATE instead of updating one row per loop iteration. Consider building and executing a single statement.
I'm trying to find the best solution for this problem. I have a table (I simplified here with only two columns) in which I have to check if the 'customer_id' is the same for each 'service_id' that I can have in the table.
If the customer_id for the same service_id is different, the service_id is raised as warning. I created a loop to pick each single service_id and I tried to save the results of the select query as an array. The problem is the array I created seems not multidimensional (I can't get the 'customer_id' to make comparison).
I don't have a clear picture of how arrays work, so probably I'm doing something wrong. The table services is this one:
The code I have written so far:
CREATE OR REPLACE PROCEDURE process_services()
LANGUAGE plpgsql
AS $$
DECLARE r RECORD;
DECLARE cur_row REFCURSOR;
DECLARE services varchar array;
begin
OPEN cur_row FOR EXECUTE 'select distinct(service_id)
from services';
loop
FETCH NEXT FROM cur_row INTO r;
-- exit when no more row to fetch
EXIT WHEN NOT FOUND;
SELECT array(select row(service_id, customer_id) FROM services WHERE service_id = r.service_id) INTO services;
IF array_length(services, 1) < 2 THEN
raise notice 'ERROR singleton, service_id =% ',r.service_id;
-- check if there are more than 2 endpoints
ELSEIF array_length(services, 1) > 2 THEN
raise notice 'ERROR too many , service_id =%' , r.service_id;
ELSE
raise notice 'Value: %', r.service_id ;
raise notice 'Value: %', services[:1];
raise notice 'Value: %', services[2];
END IF;
end loop;
CLOSE cur_row;
END;
$$;
I'm trying to do an upsert using merge and I'm getting an error message: Oracle - ORA-38101: Invalid column in the INSERT VALUES Clause on Merge: "MY_TABLE"."MYCOL2", even thought the table and column names indicated in the error are correct.
declare
var1 varchar2(50) := 'var1';
var2 varchar2(50) := 'var2';
procedure ins
(mycol1 IN VARCHAR2,
mycol2 IN VARCHAR2)
is
BEGIN
LOOP
BEGIN
MERGE INTO my_table USING dual ON
( MYCOL1 = mycol1
AND MYCOL2 = mycol2
)
WHEN MATCHED THEN UPDATE SET
MYCOL1 = mycol1,
MYCOL2 = mycol2
WHEN NOT MATCHED THEN INSERT
(MYCOL1, MYCOL2)
VALUES ( mycol1, mycol2 );
EXIT; -- success? -> exit loop
EXCEPTION
WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
NULL; -- exception? -> no op, i.e. continue looping
WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
NULL; -- exception? -> no op, i.e. continue looping
END;
END LOOP;
END;
begin
ins (var1, var2);
end;
Dont use the variable names which are same as column name of the table.
Your MATCHED clause is doing nothing.
in USING clause, there must be some query as mentioned in the comment.
Hey guys trying to use a loop to cycle though days so my script doesn't fail the loop fails to execute each time I am not sure what I am doing wrong however i know for a fact the select statement is fine and working its just the loop i have trouble with also is it possible to have the loop check the max date in the table delete only the most recent day in case of incomplete data then add only whats needed to date?
Error line 41, column 4:
PL/SQL: ORA-00933: SQL command not properly ended
ORA-06550: line 7m Column 1:
Pl/sql statement ignored
delete Target_table
commit;
DECLARE
i_date date;
BEGIN
i_date := '01-Jan-2014';
WHILE i_date < sysdate LOOP
insert into Target_table
Select field_1,
field_2
From Data_table_1
LEFT JOIN Data_table_2
ON Data_table_1.ACCOUNT_ID=Data_table_2.account_id
Where Data_table_1.Date >= i_date
and Data_table_1.Date < i_date+1
and Data_table_1.COST_CENTRE In ('1','2','3','4','5','6','7','8','9','10')
And Data_table_1.field_3 In ('C', 'D')
And Data_table_1.field_4 Is Null
And Data_table_2.field_5 in (1,2)
END;
commit;
i_date := i_date +1;
END LOOP;
END
Just want to mention that the most efficent way is write the query in in SQL whenever possible, when PL/SQL can be avoided. (There is an overhead involved in the context switch between SQL and PL/SQL blocks). This is how i would write the query. The new block, using CONNECT by would generate all dates between i_date and sysdate
DECLARE
i_date DATE;
BEGIN
DELETE Target_table;
SELECT field_1,
field_2
FROM Data_table_1
JOIN (SELECT i_date + level -1 as i_date_new
FROM DUAL
CONNECT BY LEVEL<=TRUNC(sysdate)-i_date
)dates_generated
ON Data_table_1.Date_field1 >= dates_generated.i_date_new
AND Data_table_1.Date_field1 < dates_generated.i_date_new+1
LEFT JOIN Data_table_2
ON Data_table_1.ACCOUNT_ID =Data_table_2.account_id
WHERE Data_table_1.COST_CENTRE IN ('1','2','3','4','5','6','7','8','9','10')
AND Data_table_1.field_3 IN ('C', 'D')
AND Data_table_1.field_4 IS NULL
AND Data_table_2.field_5 IN (1,2);
END;
I have made some modifications in your code. I think this should work. I have also mentioned comments for which i have made changes.
DECLARE
i_date DATE;
BEGIN
DELETE Target_table; -- Inserted inside the Anonymous block
i_date := to_date('10-Nov-2015','DD-MON-YYYY'); -- Specified Date format
WHILE i_date < sysdate
LOOP
INSERT INTO Target_table
SELECT field_1,
field_2
FROM Data_table_1
LEFT JOIN Data_table_2
ON Data_table_1.ACCOUNT_ID =Data_table_2.account_id
WHERE Data_table_1.Date_field1 >= i_date
AND Data_table_1.Date_field1 < i_date+1
AND Data_table_1.COST_CENTRE IN ('1','2','3','4','5','6','7','8','9','10')
AND Data_table_1.field_3 IN ('C', 'D')
AND Data_table_1.field_4 IS NULL
AND Data_table_2.field_5 IN (1,2);
dbms_output.put_line(i_date);
i_date := i_date +1;
END LOOP;
COMMIT; -- Added commit at the end
END;
I am trying to craete a procedure where the input to the procedure will be a string like 'a,b,c,d' and output will be the values from a table which match the comma separated value from in IN sting.
For that --
create table test (nm varchar2(10));
insert into test values ('a');
insert into test values ('b');
insert into test values ('c');
insert into test values ('d');
select * from test;
NM
------
a
b
c
d
Now i am trying to make a procedure where the IN parameter will be the values of NM column of TEST table in a comma separated string like 'a,b,c,d,x,w' there could be false values also.
So the procedure will return only the values which are matching with NM column of the TEST table for this i have created This procedure ---
create or replace procedure p_test (p_nm IN varchar2 /*, p_out OUT sys_refcursor*/)
is
l_len number;
l_val varchar2(10);
l_val1 varchar2(10);
begin
l_len := length(p_nm);
-- dbms_output.put_line(l_len);
begin
for i in 1..l_len
loop
select REGEXP_SUBSTR (p_nm, '([^,]*)(,|$)',1, i , NULL, 1) into l_val from dual;
-- dbms_output.put_line(l_val);
-- open p_out for
select * into l_val1 from test where nm = l_val;
dbms_output.put_line(l_val1);
exit when l_len is null;
end loop;
exception
when no_data_found then
null;
end;
exception
when others then
dbms_output.put_line('Error reason :'||sqlerrm||' error code :'||sqlcode);
end;
EXECUTE p_test('a,,b,c,d,q,w');
OUTPUT --
a
b
c
d
This procedure is giving me output as i required but i need to get that result into a variable which should be OUT parameter of this procedure as it will be called by the JAVA of our application.
As i have already tried to use the refcursor (please see the commented part) but it is giving me no output while a call it.
Its a call to this procedure when i use the refcursor (by removing comments ).
declare
l_out sys_refcursor;
l_val varchar2(20);
l_str varchar2(20) := 'a,b,c,d';
begin
p_test (l_str, l_out);
loop
fetch l_out into l_val;
dbms_output.put_line(l_val);
dbms_output.put_line('a');
exit when l_out%notfound;
end loop;
end;
So here i got stuck with that how to get the multiple output or am i missing something here and if there is a better approach to this requirement as i have come with this so i am sharing it here.
I am using ---
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Thanks
1) Create collection in oracle. create type list_of_varchar is table of varchar2(20);
2) Use bulk collect into.
declare
cursor c_obj is select object_name from user_objects where length(object_name) < 20;
arrays list_of_varchar;
begin
open c_obj;
fetch c_obj bulk collect into arrays;
close c_obj;
dbms_output.put_line('Fetched '||arrays.count);
end;
3*) And the same code execute in java.
public static void main(String[] args) throws SQLException, Exception {
Connection con = ConnectionDefinition.getOracleConnection(); //my oracle connection
String str = "declare " +
" cursor c_obj is select object_name from user_objects where length(object_name) < 20;" +
" arrays list_of_varchar;" +
"begin " +
" open c_obj;" +
" fetch c_obj bulk collect into arrays;" +
" close c_obj; " +
" dbms_output.put_line('Fetched '||arrays.count);"+
" ? := arrays;" +
"end;";
CallableStatement cs = con.prepareCall(str);
cs.registerOutParameter(1, OracleTypes.ARRAY, "LIST_OF_VARCHAR");
cs.execute();
String[] strings = (String[])cs.getArray(1).getArray();
for(String tmp : strings) {
System.err.println(tmp);
}
cs.close();
con.close();
}