Stored procedure in Oracle with three OUT parameters from different queries - oracle

I'm having trouble creating a stored procedure with one IN and three OUT parameters. Basically I have three queries which I want to put in a stored procedure.
How can I relate the OUT paramters with each of the queries?
CREATE OR REPLACE PROCEDURE 'SP_NAME'(
#phone_number IN VARCHAR2
REG OUT INTEGER
EMAIL OUT VARCHAR2
VALID OUT INTEGER )
AS
BEGIN
--I just need 1 or 0 to valid if exists on the table
SELECT COUNT (phone_number) FROM users
WHERE phone_number = #phone_number
-- this should bring me the email a different table
SELECT email FROM details_users
WHERE phone_number = #phone_number
-- and this bring if exists in oooother table
SELECT count(phone_number) FROM suscribers
WHERE phone_number = #phone_number
END;

Your revised business logic features three tables. In the absence of any indication that the tables contents are related it's better to keep them as separate queries.
Here is my solution (featuring valid Oracle syntax).
CREATE OR REPLACE PROCEDURE SP_NAME (
p_phone_number IN VARCHAR2
, p_REG OUT INTEGER
, p_EMAIL OUT VARCHAR2
, p_VALID OUT INTEGER )
AS
l_usr_cnt pls_integer;
l_email details_users.email%type;
l_sub_cnt pls_integer;
BEGIN
SELECT COUNT (*) into l_usr_cnt
FROM users u
WHERE u.phone_number = p_phone_number;
begin
select du.email into l_email
from details_users du
where du.phone_number = p_phone_number;
exception
when others then
l_email := null;
end;
SELECT COUNT (*) into l_sub_cnt
FROM suscribers s -- probably a typo :-/
WHERE s.phone_number = p_phone_number;
if l_usr_cnt != 0 then
p_reg := 1; -- "true"
else
p_reg := 0; -- "false"
end if;
p_email := l_email;
if l_sub_cnt != 0 then
p_valid := 1;
else
p_valid := 0;
end if;
END;
Note it is permitted to populate an OUT parameter directly from a query, like this:
SELECT COUNT (*)
into p_reg
FROM users u
WHERE u.phone_number = p_phone_number;
However, it is accepted good practice to work with local variables and only assign OUT parameters at the end of the procedure. This is to ensure consistent state is passed to the calling program, (which matters particularly if the called procedure throws an exception).
Also, I prefixed all the parameter names with p_. This is also optional but good practice. Having distinct namespaces is always safer, but it especially matters when the parameter names would otherwise match table column names (i.e. phone_number).

You may use below queries then assign the result to each of the out parameters. I notice that 1st and 3rd query are the same. Is it copy/paste error? Or you mean phone number is valid or not?
SELECT COUNT (phone_number) INTO REG FROM users
WHERE phone_number = #phone_number;
SELECT email INTO EMAIL FROM users
WHERE phone_number = #phone_number;
REG := VALID;

Related

PL/SQL : Need to compare data for every field in a table in plsql

I need to create a procedure which will take collection as an input and compare the data with staging table data row by row for every field (approx 50 columns).
Business logic :
whenever a staging table column value will mismatch with the corresponding collection variable value then i need to update 'FAIL' into staging table STATUS column and reason into REASON column for that row.
If matched then need to update 'SUCCESS' in STATUS column.
Payload will be approx 500 rows in each call.
I have created below sample script:
PKG Specification :
CREATE OR REPLACE
PACKAGE process_data
IS
TYPE pass_data_rec
IS
record
(
p_eid employee.eid%type,
p_ename employee.ename%type,
p_salary employee.salary%type,
p_dept employee.dept%type
);
type p_data_tab IS TABLE OF pass_data_rec INDEX BY binary_integer;
PROCEDURE comp_data(inpt_data IN p_data_tab);
END;
PKG Body:
CREATE OR REPLACE
PACKAGE body process_data
IS
PROCEDURE comp_data (inpt_data IN p_data_tab)
IS
status VARCHAR2(10);
reason VARCHAR2(1000);
cnt1 NUMBER;
v_eid employee_copy.eid%type;
v_ename employee_copy.ename%type;
BEGIN
FOR i IN 1..inpt_data.count
LOOP
SELECT ec1.eid,ec1.ename,COUNT(*) over () INTO v_eid,v_ename,cnt1
FROM employee_copy ec1
WHERE ec1.eid = inpt_data(i).p_eid;
IF cnt1 > 0 THEN
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
UPDATE employee_copy SET status = 'SUCCESS' WHERE eid = inpt_data(i).p_eid;
ELSE
UPDATE employee_copy SET status = 'FAIL' WHERE eid = inpt_data(i).p_eid;
END IF;
ELSE
NULL;
END IF;
END LOOP;
COMMIT;
status :='success';
EXCEPTION
WHEN OTHERS THEN
status:= 'fail';
--reason:=sqlerrm;
END;
END;
But in this approach i have below mentioned issues.
Need to declare all local variables for each column value.
Need to compare all variable data using 'and' operator. Not sure whether it is correct way or not because if there are 50 columns then if condition will become very heavy.
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
Need to update REASON column when any column data mismatched (first mismatched column name) for that row, in this approach i am not able to achieve.
Please suggest any other good way to achieve this requirement.
Edit :
There is only one table at my end i.e target table. Input will come from any other source as collection object.
REVISED Answer
You could load the the records into t temp table, but unless you want additional processing it's not necessary. AFAIK there is no way to identify the offending column (first one only) without slugging through column-by-column. However, your other concern having to declare a variable is not necessary. You can declare a single variable defined as %rowtype which gives you access to each column by name.
Looping through an array of data to find the occasional error is just bad (imho) with SQL available to eliminate the good ones in one fell swoop. And it's available here. Even though your input is a array we can use as a table by using the TABLE operator, which allows an array (collection) as though it were a database table. So the MINUS operator can till be employed. The following routine will set the appropriate status and identify the first miss matched column for each entry in the input array. It reverts to your original definition in package spec, but replaces the comp_data procedure.
create or replace package body process_data
is
procedure comp_data (inpt_data in p_data_tab)
is
-- define local array to hold status and reason for ecah.
type status_reason_r is record
( eid employee_copy.eid%type
, status employee_copy.status%type
, reason employee_copy.reason%type
);
type status_reason_t is
table of status_reason_r
index by pls_integer;
status_reason status_reason_t := status_reason_t();
-- define error array to contain the eid for each that have a mismatched column
type error_eids_t is table of employee_copy.eid%type ;
error_eids error_eids_t;
current_matched_indx pls_integer;
/*
Helper function to identify 1st mismatched column in error row.
Here is where we slug our way through each column to find the first column
value mismatch. Note: There is actually validate the column sequence, but
for purpose here we'll proceed in the input data type definition.
*/
function identify_mismatch_column(matched_indx_in pls_integer)
return varchar2
is
employee_copy_row employee_copy%rowtype;
mismatched_column employee_copy.reason%type;
begin
select *
into employee_copy_row
from employee_copy
where employee_copy.eid = inpt_data(matched_indx_in).p_eid;
-- now begins the task of finding the mismatched column.
if employee_copy_row.ename != inpt_data(matched_indx_in).p_ename
then
mismatched_column := 'employee_copy.ename';
elsif employee_copy_row.salary != inpt_data(matched_indx_in).p_salary
then
mismatched_column := 'employee_copy.salary';
elsif employee_copy_row.dept != inpt_data(matched_indx_in).p_dept
then
mismatched_column := 'employee_copy.dept';
-- elsif continue until ALL columns tested
end if;
return mismatched_column;
exception
-- NO_DATA_FOUND is the one error that cannot actually be reported in the customer_copy table.
-- It occurs when an eid exista in the input data but does not exist in customer_copy.
when NO_DATA_FOUND
then
dbms_output.put_line( 'Employee (eid)='
|| inpt_data(matched_indx_in).p_eid
|| ' does not exist in employee_copy table.'
);
return 'employee_copy.eid ID is NOT in table';
end identify_mismatch_column;
/*
Helper function to find specified eid in the initial inpt_data array
Since the resulting array of mismatching eid derive from a select without sort
there is no guarantee the index values actually match. Nor can we sort to build
the error array, as there is no way to know the order of eid in the initial array.
The following helper identifies the index value in the input array for the specified
eid in error.
*/
function match_indx(eid_in employee_copy.eid%type)
return pls_integer
is
l_at pls_integer := 1;
l_searching boolean := true;
begin
while l_at <= inpt_data.count
loop
exit when eid_in = inpt_data(l_at).p_eid;
l_at := l_at + 1;
end loop;
if l_at > inpt_data.count
then
raise_application_error( -20199, 'Internal error: Find index for ' || eid_in ||' not found');
end if;
return l_at;
end match_indx;
-- Main
begin
-- initialize status table for each input enter
-- additionally this results is a status_reason table in a 1:1 with the input array.
for i in 1..inpt_data.count
loop
status_reason(i).eid := inpt_data(i).p_eid;
status_reason(i).status :='SUCCESS';
end loop;
/*
We can assume the majority of data in the input array is valid meaning the columns match.
We'll eliminate all value rows by selecting each and then MINUSing those that do match on
each column. To accomplish this cast the input with TABLE function allowing it's use in SQL.
Following produces an array of eids that have at least 1 column mismatch.
*/
select p_eid
bulk collect into error_eids
from (select p_eid, p_ename, p_salary, p_dept from TABLE(inpt_data)
minus
select eid, ename, salary, dept from employee_copy
) exs;
/*
The error_eids array now contains the eid for each miss matched data item.
Mark the status as failed, then begin the long hard process of identifying
the first column causing the mismatch.
The following loop used the nested functions to slug the way through.
This keeps the main line logic clear.
*/
for i in 1 .. error_eids.count -- if all inpt_data rows match then count is 0, we bypass the enttire loop
loop
current_matched_indx := match_indx(error_eids(i));
status_reason(current_matched_indx).status := 'FAIL';
status_reason(current_matched_indx).reason := identify_mismatch_column(current_matched_indx);
end loop;
-- update employee_copy with appropriate status for each row in the input data.
-- Except for any cid that is in the error eid table but doesn't exist in the customer_copy table.
forall i in inpt_data.first .. inpt_data.last
update employee_copy
set status = status_reason(i).status
, reason = status_reason(i).reason
where eid = inpt_data(i).p_eid;
end comp_data;
end process_data;
There are a couple other techniques used you may want to look into if you are not familiar with them:
Nested Functions. There are 2 functions defined and used in the procedure.
Bulk Processing. That is Bulk Collect and Forall.
Good Luck.
ORIGINAL Answer
It is NOT necessary to compare each column nor build a string by concatenating. As you indicated comparing 50 columns becomes pretty heavy. So let the DBMS do most of the lifting. Using the MINUS operator does exactly what you need.
... the MINUS operator, which returns only unique rows returned by the
first query but not by the second.
Using that this task needs only 2 Updates: 1 to mark "fail", and 1 to mark "success". So try:
create table e( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
);
create table stage ( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
, status varchar2(20)
, reason varchar2(20)
);
-- create package spec and body
create or replace package process_data
is
procedure comp_data;
end process_data;
create or replace package body process_data
is
package body process_data
procedure comp_data
is
begin
update stage
set status='failed'
, reason='No matching e row'
where e_id in ( select e_id
from (select e_id, col1, col2 from stage
except
select e_id, col1, col2 from e
) exs
);
update stage
set status='success'
where status is null;
end comp_data;
end process_data;
-- test
-- populate tables
insert into e(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any') from dual union all
select (3,'ok', 'best ever') from dual union all
select (4,'xx','zzzzzz') from dual;
insert into stage(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any more') from dual union all
select (4,'yy', 'zzzzzz') from dual union all
select (5,'no e','nnnnn') from dual;
-- run procedure
begin
process_data.comp_date;
end;
-- check results
select * from stage;
Don't ask. Yes, you to must list every column you wish compared in each of the queries involved in the MINUS operation.
I know the documentation link is old (10gR2), but actually finding Oracle documentation is a royal pain. But the MINUS operator still functions the same in 19c;

Search string to match Data inside of all existing columns and rows from Views in Oracle SQL

The code below is looking for a string to match a column name. However I would like to search for a string to match Data (meaning, search on each existing column and row from all views - not column names). I want the results to show me all Views Names that contain that string on their Data. Hope this makes sense.
begin
dbms_output.put_line('Owner View name');
dbms_output.put_line('------------------------------ -------------------------------');
for r in (
select v.owner, v.view_name, v.text
from all_views v
where v.owner <> 'SYS'
)
loop
if lower(r.text) like '%my_String%' then
dbms_output.put_line(rpad(r.owner,31) || r.view_name);
end if;
end loop;
end;
I suggest you at least review some of the Oracle documentation. Your statement "Oracle SQL... i am using Oracle SQL Developer" indicates a lack of understanding. So lets clear some of that up. You are actually using 3 separate software tools:
Oracle SQL - all access to data being stored in your Oracle database.
Oracle Pl/SQL - the programming language, which tightly integrates with but is still separate from SQL. This is the language in which your script is written.
Oracle SQL Developer - an IDE within which you develop, run, etc run your Pl/SQL scripts and/or SQL statements.
Now as for your script. The syntax is fine and it will run, successfully retrieving all non sys owned views and printing the names of those containing the specified search string. However, as it currently stands it'll never find a match. Your match statement attempts to match a lower case string to a string that contains the character 'S' (upper case). There will never be a match. Also, keep in mind that within LIKE the underscore (_) is a single character wild card. You may need to escape it as "like '%my_ ...'". With these in mind we come to:
REVISED
The requirement to actually find the string in view columns completely changes things from your original query. Although the title does suggest that was actually the initial desire. What you want to accomplish is much more complex and cannot be done in SQL alone; it requires PL/SQL or an external language code. The reason is that you need run select but you don't know against what nor what columns nor even how many columns area needed. For that you need to identify the view, isolate the viable columns and generate the appropriate SQL dynamically then actually execute that sql and check the results.
Two approaches come to mind: Parse the view to extract the columns (something I would even consider doing in PL/SQL) or join ALL_VIEWS with ALL_TAB_COLUMNS (All table columns). That we'll do. The majority of the code will be constructing the SQL and the necessary error handling dynamic SQL essentially forces on you. I've created this a a procedure with 2 parameters: the target string, and the schema.
create or replace procedure find_string_in_view(
schema_name_in varchar2
, target_string_in varchar2
)
is
--- set up components for eventual execution
k_new_line constant varchar2(2) := chr(10);
k_base_statement constant varchar2(50) :=
q'!select count(*) from <view_name> where 1=1 and !' || k_new_line;
k_where_statement constant varchar2(50) :=
q'! <column_name> like '%<target>%' !' || k_new_line;
k_limit_statement constant varchar2(20) :=
' ) and rownum < 2';
k_max_allowed_errors constant integer := 3;
--- cursor for views and column names
cursor c_view_columns is
(select av.owner,av.view_name , atc.column_name
, lead(atc.column_name) over (partition by av.view_name order by atc.column_id) next_col
, lag(atc.column_name) over (partition by av.view_name order by atc.column_id) prev_col
from all_views av
join all_tab_columns atc
on ( atc.owner = av.owner
and atc.table_name = av.view_name
)
where av.owner = upper(schema_name_in)
and atc.data_type in
('CHAR', 'NCHAR', 'VARCHAR2','NVARCHAR2','VARCHAR','NVARCHAR')
) ;
--- local variables
m_current_view varchar2(61);
m_sql_errors integer := 0;
m_where_connector varchar(2);
m_sql_statement varchar2(4000);
-- local helper function
function view_has_string
return boolean
is
l_item_count integer := 0;
begin
execute immediate m_sql_statement into l_item_count;
return (l_item_count > 0);
exception
when others then
dbms_output.put_line(rpad('-',61,'-') || k_new_line);
dbms_output.put_line('Error processing:: ' || m_current_view);
dbms_output.put_line('Statement::' || k_new_line || m_sql_statement);
dbms_output.put_line(sqlerrm);
m_sql_errors := m_sql_errors + 1;
if m_sql_errors >= k_max_allowed_errors
then
raise_application_error(-20199,'Maximun errors allowed reach. Terminating');
end if;
return false;
end view_has_string;
begin -- MAIN --
dbms_output.put_line('Owner View name');
dbms_output.put_line('------------------------------ -------------------------------');
for rec in c_view_columns
loop
if rec.prev_col is null
then
m_current_view := rec.owner || '.' || rec.view_name;
m_sql_statement := replace(k_base_statement,'<view_name>',m_current_view);
m_where_connector := ' (';
end if;
m_sql_statement := m_sql_statement || m_where_connector
|| replace(k_where_statement,'<column_name>',rec.column_name);
m_where_connector := 'or' ;
if rec.next_col is null
then
m_sql_statement := replace(m_sql_statement,'<target>',target_string_in);
m_sql_statement := m_sql_statement || k_limit_statement;
if view_has_string
then
dbms_output.put_line(rpad(rec.owner,31) || rec.view_name);
end if;
end if;
end loop;
end find_string_in_view;
select v.owner, v.view_name, v.text
from all_views v
where v.owner <> 'SYS' and lower(v.text) like '%my_String%'
one SQL do this?

Oracle - change columns in WHERE clause based on input

I want use separate columns in WHERE clause based on the INPUT received in the stored procedure.
If TYPE_DEFINITION = 'SUP' then use SUPPLIER column
If TYPE_DEFINITION = 'CAT' then use CATEGORY column
I know I can write two separate SELECT's using a CASE statement, but that will be very dumb and redundant. Any cleaner way of doing it?
CREATE OR REPLACE PROCEDURE SG.STORED_PROCEDURE (
TYPE_DEFINITION IN VARCHAR2,
VALUE IN VARCHAR2,
STORELIST IN VARCHAR2)
AS
BEGIN
SELECT O.ORGNUMBER,
S.SKU,
FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID
WHERE
AND O.ORGNUMBER IN (STORELIST)
AND (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER = VALUE
ELSE S.CATEGORY = VALUE
END);
END;
/
Your code is very close. The CASE THEN must return an expression, not a condition. But the CASE can be used as part of a condition, just move the = VALUE
to the outside.
Change this:
AND (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER = VALUE
ELSE S.CATEGORY = VALUE
END);
To This:
AND VALUE = (CASE TYPE_DEFINITION
WHEN 'SUP' THEN S.SUPPLIER
ELSE S.CATEGORY
END);
Your code makes sense. This limitation is probably a result of Oracle not fully supporting Booleans.
UPDATE
If you run into performance problems you may want to use dynamic SQL or ensure that the static SQL is correctly using FILTER operations. When Oracle builds an execution plan it is able to use bind variables like constants, and choose a different plan based on the input. As Ben pointed out, these FILTER operations don't always work perfectly, sometimes it may help if you use simplified conditions like this:
(TYPE_DEFINITION = 'SUP' AND S.SUPPLIER = VALUE)
OR
((TYPE_DEFINITION <> 'SUP' OR TYPE_DEFINITION IS NULL) AND S.CATEGORY = VALUE)
You need to use dynamic sql in your procedure.
Something like this:
CREATE OR REPLACE PROCEDURE SG.STORE_PROC (
TYPE_DEFINITION IN VARCHAR2,
VALUE IN VARCHAR2,
STORELIST IN VARCHAR2)
AS
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
v_stmt_str VARCHAR2(200);
v_orgnumber VARCHAR2(200);
v_sku VARCHAR2(200);
BEGIN
v_stmt_str := 'SELECT O.ORGNUMBER, S.SKU,FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID ';
if type_definition = 'SUP' then
v_stmt_str := v_stmt_str || 'WHERE s.supplier = :v';
else
v_stmt_str := v_stmt_str || 'WHERE s.category = :v';
end if;
-- Open cursor & specify bind variable in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING value;
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO v_orgnumber, v_sku;
-- you can do something here with your values
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
/

Find specific varchar in Oracle Nested Table

I'm new to PL-SQL, and struggling to find clear documentation of operations are nested tables. Please correct any misused terminology etc.
I have a nested table type that I use as a parameters for a stored procedure.
CREATE OR REPLACE TYPE "STRARRAY" AS TABLE OF VARCHAR2 (255)
In my stored procedure, the table is initialized and populated. Say I have a VARCHAR2 variable, and I want to know true or false if that varchar exists in the nested table.
I tried
strarray.exists('somevarchar')
but I get an ORA-6502
Is there an easier way to do that other than iterating?
FOR i IN strarray.FIRST..strarray.LAST
LOOP
IF strarray(i) = value THEN
return 1;--found
END IF;
END LOOP;
For single value check I prefer the "member" operator.
zep#dev> declare
2 enames strarray;
3 wordToFind varchar2(255) := 'King';
4 begin
5 select emp.last_name bulk collect
6 into enames
7 from employees emp;
8 if wordToFind member of enames then
9 dbms_output.put_line('Found King');
10 end if;
11 end;
12 /
Found King
PL/SQL procedure successfully completed
zep#dev>
You can use the MULTISET INTERSECT operator to determine whether the string you're interested in exists in the collection. For example
declare
l_enames strarray;
l_interesting_enames strarray := new strarray( 'KING' );
begin
select ename
bulk collect into l_enames
from emp;
if( l_interesting_enames = l_interesting_enames MULTISET INTERSECT l_enames )
then
dbms_output.put_line( 'Found King' );
end if;
end;
will print out "Found King" if the string "KING" is an element of the l_enames collection.
You should pass an array index, not an array value to an exists in case you'd like to determine whether this element exists in collection. Nested tables are indexed by integers, so there's no way to reference them by strings.
However, you might want to look at associative arrays instead of collections in case you wish to reference your array element by string index. This will look like this:
DECLARE
TYPE assocArray IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
myArray assocArray;
BEGIN
myArray('foo') := 'bar';
IF myArray.exists('baz') THEN
dbms_output.put_line(myArray('baz'));
ELSIF myArray.exists('foo') THEN
dbms_output.put_line(myArray('foo'));
END IF;
END;
Basically, if your array values are distinct, you can create paired arrays referencing each other, like,
arr('b') := 'a'; arr('a') := 'b';
This technique might help you to easily look up any element and its index.
When a nested table is declared as a schema-level type, as you have done, it can be used in any SQL query as a table. So you can write a simple function like so:
CREATE OR REPLACE FUNCTION exists_in( str VARCHAR2, tab stararray)
RETURN BOOLEAN
AS
c INTEGER;
BEGIN
SELECT COUNT(*)
INTO c
FROM TABLE(CAST(tab AS strarray))
WHERE column_value = str;
RETURN (c > 0);
END exists_in;

PL/SQL procedure for loop though a table and change values

Basically I need to make a for loop that will loop though the amount of rows. In each row I need to check a value and change it if it meets the requirements.
I'm new to Oracle, so I just started building it one step at a time and I'm stuck on looping through the table rows. I need to first get the number count of the rows that a Boolean flag set to 0 (false). So then I can loop through only those rows, not every row in the table. Once I'm done with whatever I need to change in that row, set the flag to 1 (true), so when I run the procedure again it won't include that row.
Here's what I have so far:
My table:
CREATE TABLE test_table_results (
name varchar,
account number,
address varchar,
database_search NUMBER(1) DEFAULT 0 NOT NULL
CONSTRAINT searched_in_database CHECK (database_search IN (0,1))
);
The table in my database:
CREATE TABLE test_table_accounts (
name varchar,
account number,
address varchar,
);
Now the procedure will go though the results table and see if the address match, if they do it will copy the account number from the database table into the results account number, then change the flag from 0 to 1, so the next time I search though the table it won't include it because it was already searched.
create or replace PROCEDURE FIND_MATCH_ADDRESS AS
BEGIN
DECLARE
v_cnt NUMBER;
BEGIN
FOR i IN (SELECT rowid, r.* FROM test_table
WHERE database_searched = 0)
LOOP
LOOP
SELECT COUNT(1) INTO v_cnt
FROM test_table
WHERE database_searched = 0;
DBMS_OUTPUT.PUT_LINE(v_cnt);
END LOOP;
END LOOP;
END;
END FIND_MATCH_ADDRESS;
EDIT: Added the two tables in hopes to make my question/task more understandable.
Again thank you for your time!!
In your example I see some mistakes.
In the procedure you do not need Declare. Declaration block is between as and begin
create or replace PROCEDURE proc
AS
-- here variable declaration or local function or procedures
BEGIN
-- here you can write a business logic
END proc;
You can iterate over the records of a table with a For loop. In your example, you also tried to use them. You can iterate over the records of a table with a For loop. In your example, you also tried to use them.
FOR record IN (cursor)
LOOP
{...statements...}
END LOOP;
I did not quite understand why you used another loop in the loop. a loop statement is an endless loop.
loop
...
end loop;
In the loop you can now implement your logic. If you really want to use a loop, then your solution might look like this
create or replace PROCEDURE FIND_MATCH_ADDRESS
AS
v_cnt NUMBER;
BEGIN
FOR rec IN (SELECT r.name
,r.address
,r.account
,a.account as new_account
FROM test_table_results r
join test_table_accounts a on a.address = r.address
WHERE r.database_searched = 0)
LOOP
update test_table_results
set account = rec.new_account
, database_searched = true
where account = rec.account
and name = rec.name
and adress = rec.adress;
END LOOP;
END FIND_MATCH_ADDRESS;
Alternatively, you can also do that with an update. Since I do not know your tables, you should then optimize the where condition.
update test_table_results t
set database_searched = true
, account = (select account
from test_table_accounts a
where a.account = t.account
and a.name = t.name
and a.adress = t.adress)
where database_searched = false
and exists(select 1
from test_table_accounts a
where a.account = t.account
and a.name = t.name
and a.adress = t.adress);

Resources