Creating cursor to connect to remote DB - oracle

Was trying to get the data from remote DB table via DB link in a procedure like my_procedure (DB_LINK).
How can I create cursor to connect with remote DB via this parameter in Oracle?

I am not sure that what you are asking is possible in the way you are describing it. The DB LINK is declared at the database level and I am not sure that it can be designated as a parameter directly. I could be wrong though.
We achieve similar results at my work by doing something like that :
-- untested
PROCEDURE my_procedure(target_db IN VARCHAR2) IS
BEGIN
IF target_db = 'database1' THEN
EXECUTE IMMEDIATE 'UPDATE myTable' || database1_dbLinkName ||
' SET a = b WHERE x = z';
NULL;
ELSIF target_db = 'database2' THEN
-- ...
NULL;
END IF;
END;
I don't remember the exact syntaxe for the content of database1_dbLinkName. I believe it's just "#yourDbLinkName".
I recommend you read this page if you want to know more : https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4410488400346721382

Related

Oracle Stored Procedure posing a prob

[EDIT]Editing the code to reflect changes coming from comments
I have a problem with one of the stored procedures I'm trying to create in an Oracle database.
The goal is to update every table which has an indiv column.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
AS
BEGIN
FOR item IN (
select table_name , owner
from all_tab_columns
where column_name = 'INDIV' AND OWNER ='K'
)
LOOP
EXECUTE IMMEDIATE 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue' USING newU, lastU;
END LOOP;
END sp_majUserOnAllK;
exec sp_majUserOnAllK( 'hum','hum');
Problem is, when I try to execute the stored procedure, I got an error message with no detail at all ('non valid SQL').
I tried taking the code out of the stored procedure. And there, it works. Only the beginning is changing to :
DECLARE
newU NVARCHAR2(50);
lastU NVARCHAR2(50);
req VARCHAR2(100);
CURSOR ctable IS
select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='KEXPLOIT';
BEGIN
newU := 'hum';
lastU := 'hum';
FOR item IN ctable
....
Like that, it works perfectly and does exactly what it is supposed to do.
As the only difference is the assignation of the variable, I think I may have a problem with my procedure declaration but I can't find a solution. The compilation is ok.
Any idea ?
Your procedure's syntax is not correct. Try this.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
IS
req VARCHAR2(100);
BEGIN
FOR item IN (select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='K')
LOOP
req := 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue';
EXECUTE IMMEDIATE req USING newU, lastU;
END LOOP;
-- return 1; -- note: procedures do not return values
END;
/
A five-second Google search on "dbeaver exec command" brought this up among the first few hits:
https://github.com/dbeaver/dbeaver/issues/749
In it, we learn that EXEC is not supported by dbeaver.
EXEC is an SQL*Plus command. It is not Oracle SQL, and it is not PL/SQL. SQL*Plus is a shell program of sorts for interacting with Oracle databases; it has its own language, distinct from SQL and PL/SQL.
SQL Developer and Toad (and perhaps other similar programs) support (most of) SQL*Plus, but apparently dbeaver (with which I am not familiar) does not.
The link I copied above suggests using the CALL command instead. See the link for examples.
As an aside, when we use EXEC in SQL*Plus and SQL Developer, there is no semicolon at the end of the procedure call. Adding an unnecessary semicolon, however, does not throw an error (SQL*Plus is, apparently, smart enough to simply ignore it).

Check if a oracle view on a dblink is available?

I've got a lot of stored procedure working on a view on a database-link.
I would like to check if the view is available before to execute all the procedures so I've created a check() function in an object oriented programming way.
Now I can make: if(checked()=1) then ..work.. else null; end if;
The function is:
create or replace FUNCTION CHECK_MYVIEW RETURN NUMBER IS
CHECKED NUMBER;
BEGIN
BEGIN
select 1
into CHECKED
from MYVIEW
where rownum = 1
;
EXCEPTION WHEN OTHERS THEN
CHECKED:=0;
END;
RETURN CHECKED;
END CHECK_MYVIEW;
I've written the check query after some test and the result is fine.
With this kind of select I can tell if the view have some record and even if there is some connection problem with the remote database. But this is my own solution.
Is there an optimized oracle query to obtain the same feature?
For any kind of exception don't work without the raise of an exception...
You could try to check metadata:
DECLARE
checked INT;
BEGIN
SELECT COUNT(*)
INTO checked
FROM all_views#dblink
WHERE name = 'xxx'
AND owner = 'yyy';
--checked 0 -- not exists, 1 - exists
END;

ORA-00900: invalid SQL statement for Oracle Procedure

I am trying to execute my below procedure but kept getting error (ORA-00900: invalid SQL statement)
CREATE OR REPLACE PROCEDURE RESETUSERSESSION (run IN VARCHAR2)
IS
cursor usersessiondetail_cur IS
SELECT usd.CLIENTID,usd.OPERID,usd.REGISTER,usd.MACHINE_ID,usd.SESSIONNUMBER
FROM cashiering_dev.CSH_USER usr, cashiering_dev.CSH_USERSESSIONDETAIL usd
WHERE usr.clientid = usd.clientid
AND usr.operid = usd.operid
AND usr.register = usd.register
AND usr.machine_id = usd.machine_id
AND usr.sessionnumber = usd.sessionnumber
AND usr.Machine_ID = 'basrytest'
AND usd.LOGOFFDATETIME IS NULL;
BEGIN
OPEN usersessiondetail_cur;
FOR vItems in usersessiondetail_cur
LOOP
EXECUTE IMMEDIATE 'UPDATE csh_UserSessionDetail
SET ClientID =vItems.CLIENTID
WHERE ClientID =vItems.CLIENTID
AND OperID =vItems.OPERID
AND Register =vItems.REGISTER
AND Machine_ID =vItems.MACHINE_ID
AND SessionNumber =vItems.SESSIONNUMBER';
END LOOP;
CLOSE usersessiondetail_cur;
END;
Your SQL is invalid because the cursor projection names are not in scope when the dynamic SQL string is executed. You need to use placeholders like this:
FOR vItems in usersessiondetail_cur
LOOP
EXECUTE IMMEDIATE 'UPDATE csh_UserSessionDetail
SET ClientID = :p1
WHERE ClientID = :p2
AND OperID = :p3
AND Register = :p4
AND Machine_ID = :p5
AND SessionNumber = :p6'
using vItems.CLIENTID
, vItems.CLIENTID
, vItems.OPERID
, Items.REGISTER
, vItems.MACHINE_ID
, vItems.SESSIONNUMBER;
END LOOP;
Your dynamic code is not an anonymous PL/SQL block or a CALL statement so parameters are passed by position not name, which means you must pass vItems.CLIENTID twice. Find out more.
Other observations
First and foremost, there is absolutely no need to implement dynamic execution for this SQL.
The OPEN and CLOSE cursor statements are not used with a FOR cursor loop.
You do not need an explicit cursor declaration for this query.
The row-by-agonising row UPDATE with a cursor loop is bad practice and needlessly inefficient compared to set-based UPDATE statement.
Your procedure doesn't use the run parameter ...
... but the cursor does have a hardcoded string for MACHINE_ID.
Lastly, the UPDATE statement doesn't actually change the state of the table, because it sets CLIENT_ID = CLIENT_ID, so the whole procedure is pointless.
Apart from that, everything is fine.
I assume you're writing this as a test for understanding how to use dynamic SQL rather than as an implementation of business logic. But even if it is a test it is better to write a proper piece of code which does something. Especially when you're sharing the code with others on StackOverflow. Posting code with so many issues is distracting because potential respondents don't know which to tackle.
A much simpler approach with just FOR loop. We dont require to Open Close cursor in this case as this is internally handled by Oracle. Also I do not understand the need of updating Client ID again. If we are selecting the Client ID in where Clause there is no significance of Updating. Anyhow Enjoy :)
CREATE OR REPLACE
PROCEDURE RESETUSERSESSION(
run IN VARCHAR2)
AS
BEGIN
FOR vItems IN
(SELECT usd.CLIENTID,
usd.OPERID,
usd.REGISTER,
usd.MACHINE_ID,
usd.SESSIONNUMBER
FROM cashiering_dev.CSH_USER usr,
cashiering_dev.CSH_USERSESSIONDETAIL usd
WHERE usr.clientid = usd.clientid
AND usr.operid = usd.operid
AND usr.register = usd.register
AND usr.machine_id = usd.machine_id
AND usr.sessionnumber = usd.sessionnumber
AND usr.machine_id = 'basrytest'
AND usd.LOGOFFDATETIME IS NULL
)
LOOP
UPDATE csh_UserSessionDetail
SET ClientID =vItems.CLIENTID
WHERE ClientID =vItems.CLIENTID
AND OperID =vItems.OPERID
AND Register =vItems.REGISTER
AND Machine_ID =vItems.MACHINE_ID
AND SessionNumber =vItems.SESSIONNUMBER;
END LOOP;
END;
/

Oracle returning result from dynamic SQL

Hi I am trying to return a value from Oracle using dynamic SQL. I am getting an error SQL command not properly ended. I can't figure out what I am doing wrong. Here is the latest code I've tried:
PROCEDURE get_record_counts
AS
v_EXT_RECCOUNT VARCHAR2(05) := '0';
BEGIN
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.EXT_TABLE || ' RETURN v_EXT_RECCOUNT into v_EXT_RECCOUNT ';
END get_record_counts;
You'd want
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.ext_table
INTO v_ext_reccount
assuming that r_cls.ext_table resolves to a varchar2 variable that contains a table name that the caller has appropriate permissions on. In the snippet you posted, that is not a valid variable name but I'm guessing that there is more code that you've removed that declares that variable.

Receive a db link name as a variable in Oracle PLSQL

I have the following function in PLSQL which connects remotely to different Database Links to change passwords:
FUNCTION fun_change_password(DB_LINK_VARIABLE varchar2)
RETURN binary_integer IS
jobid binary_integer;
BEGIN
dbms_job.submit#DB_LINK_VARIABLE (jobid,'begin execute immediate ''alter user MYUSER identified by mypassw''; end;');
COMMIT;
RETURN jobid;
END;
My goal is to specify which DB Link to use sending its name in a varchar2 variable called *DB_LINK_VARIABLE*. But when I compile this into a package, the parser sends me an error:
PLS-00352: Unable to access another database 'DB_LINK_VARIABLE'
Obviously, I pre-configured and tested all my datalinks and works perfectly.
How can I use variable 'DB_LINK_VARIABLE' into this code?
You can do this with dynamic SQL by executing an anonymous PL/SQL block.
Below is a simple example where I execute dbms_utility.get_time function over a database link.
$ cat so35.sql
declare
function remote_time(p_dblink in varchar2) return number is
v_time number;
begin
execute immediate
'begin :time := dbms_utility.get_time#' || p_dblink || '; end;'
using out v_time;
return v_time;
end;
begin
dbms_output.put_line('time = ' || remote_time('foo'));
end;
/
SQL> select dbms_utility.get_time as local, dbms_utility.get_time#foo as remote from dual;
LOCAL REMOTE
---------- ----------
77936814 1546395927
SQL> #so35.sql
time = 1546396850
PL/SQL procedure successfully completed.
SQL>
PLS-00352: Unable to access another database 'DB_LINK_VARIABLE'
Error message shows, oracle is looking for a db link called DB_LINK_VARIABLE instead of the value associated to it.
You may need to do a check on variable, and make the hardcoding of the db link , instead of using a bind variable for it.!
Functions are compiled code in DB, so I guess oracle would do a semantic check on this during compilation itself, rather than doing it in runtime.
If it was just a SQL call to remote db, EXECUTE IMMEDIATE would have been used. Since it is PL/SQL there is no way for it, but for having multiple IF conditions, to validate the variable name, and making the full name in your PL/SQL block.

Resources