Check if a oracle view on a dblink is available? - oracle

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;

Related

Creating cursor to connect to remote DB

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

Function returns null instead of expected value

When I call my function for a value that does not exist in the table:
SELECT pacjent_usun_PK_ERROR(5) from dual
(PESEL='5' does not exist in my table)
The function returns NULL.
But when I pass a valid value:
SELECT pacjent_usun_PK_ERROR(1) from dual
(PESEL='1' exists in my table)
The function returns 1.
Here is my function:
CREATE OR REPLACE FUNCTION pacjent_usun_PK_ERROR
( PES IN NUMBER )
RETURN NUMBER
IS
ILE NUMBER;
ZMIENNA NUMBER;
BEGIN
SELECT PACJENTID INTO ZMIENNA FROM PACJENT WHERE PESEL=PES;
SELECT COUNT(PRZYJECIEID) INTO ILE FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=ZMIENNA and rownum=1;
RETURN ILE;
END;
When I test my last SELECT from my function, with manual inserted PRZYJECIEID
(PACJENTID='1111' does not exist in my table)
SELECT COUNT(PRZYJECIEID) FROM PRZYJECIE_NA_ODDZIAL WHERE PACJENTID='1111' and rownum=1;
Result is: 0
and testing with PACJENTID='1' (exist in my table)
Result is: 1
I am trying to understand why the function returns NULL instead of 0 when there are no rows.
I am using Oracle SQL Developer
I don't know what causes that behavior; if I can figure it out I will post again.
In any case, the following version should work. I created a similar function using the tables EMP and DEPT in the standard (Oracle) SCOTT schema, and the change I show below worked in that setting. Essentially I eliminate the intermediate variable; I do everything in a single query and assignment.
create or replace FUNCTION pacjent_usun_PK_ERROR
(PES IN NUMBER)
RETURN NUMBER
IS
ILE NUMBER;
BEGIN
SELECT COUNT(PRZYJECIEID) INTO ILE
FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=(SELECT PACJENTID FROM PACJENT WHERE PESEL=PES)
and rownum=1;
RETURN ILE;
end;
Added: To clarify... if select into... returns no rows, PL/SQL should raise an exception, specifically the NO_DATA_FOUND exception. The mystery that is worth investigating is why PL/SQL does not raise this exception in this case.
Final edit - it appears this has always been the behavior in PL/SQL functions, as opposed to PL/SQL procedures. NO_DATA_FOUND is an exception (it can be handled in the usual manner) but it is not raised. See this old discussion on AskTOM: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:10321465390114
This means that when the first select into returns no data, the function is in an unhandled exception state. This is why the second select into doesn't result in a count of 0 - execution doesn't even get that far.
This also means that you may keep your function code exactly as it is, but you need to add an exception handling block:
exception
when no_data_found then return 0;
right before end;
I tested this on my "mock-up" of your function (in the SCOTT schema) and it works.

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

How to retrieve values post-execution of PL/SQL stored procedure?

Im very new to oracle database stuff. There is a PL/SQL proceedure which someone else wrote and is stored on the database I am accessing. I want my program to execute it and retrieve the result. Execution is working. I cannot retrieve the result however. Obviously I am not doing it right, but I cannot find the right way in the documentation.
Here is the gist of the stored procedure (with extraneous lines removed)
procedure ISDRAWINGVALID(DWGNO_IN in VARCHAR2) is
valid BOOLEAN;
begin
-- do some stuff to see if the drawing is valid
IF <some stuff> THEN
valid := TRUE;
ELSE
valid := FALSE;
END IF;
END ISDRAWINGVALID;
My program issues the following commands to the database to execute and retrieve the return.
BEGIN ISDRAWINGVALID( <drawingnumber> ); END;
SELECT ISDRAWINGVALID.valid FROM DUAL;
The first line works fine, the proceedure executes and has the desired effect.
The second line returns an error, invalid identifier "ISDRAWINGVALID.valid"
Clearly i am not using the right way to retrieve the value. Can someone please clue me in?
thanks
As you present the problem, there is no way to get the result.
If you can get the procedure as a function instead, you can call it directly in the select statement.
Otherwise you would have to take a long detour to solve it, involving a result table or a pl/sql package with a result function and a package variable.
The procedure you have there has been made to be called from other pl/sql code - not in a select query.
EDIT
I think I might be wrong after all.
In Java you can create a prepared statement with a call, and pick up the return value directly as a result-set.
Check this out and come back with the result: http://archive.oreilly.com/pub/a/onjava/2003/08/13/stored_procedures.html?page=2
Sorry if you are not using Java, I was not able to see what you are using.
Use a function and return a NUMBER to be used in SQL:
CREATE OR REPLACE
FUNCTION ISDRAWINGVALID(DWGNO_IN in VARCHAR2) RETURN NUMBER
IS
valid NUMBER;
BEGIN
IF <some stuff> THEN
valid := 1;
ELSE
valid := 0;
END IF;
RETURN valid;
END ISDRAWINGVALID;
Use from PL/SQL:
DECLARE
valid NUMBER;
BEGIN
valid := ISDRAWINGVALID( <drawingnumber> );
END;
/
Use from SQL:
SELECT ISDRAWINGVALID( <drawingnumber> ) FROM DUAL;

Cursor design and refactoring question

I have many cursors that all return rows with the same fields: a numeric ID field and an XMLType field. Every time I access one of these cursors (each cursor has now got its own function for access), I go through the same pattern:
--query behind cursor is designed to no more than one row.
for rec in c_someCursor(in_searchKey => local_search_key_value) loop
v_id := rec.ID
v_someXMLVar := rec.XMLDataField
end loop;
if v_someXMLVar is null then
/* A bunch of mostly-standard error handling and logging goes here */
end if;
exception
/* all cursor access functions have the same error-handling */
end;
As the pattern became more obvious, it made sense to centralize it in a single function:
function fn_standardCursorAccess(in_cursor in t_xmlCursorType, in_alt in XMLType) return XMLType is
v_XMLData XMLType;
begin
dbms_application_info.set_module(module_name => $$PLSQL_UNIT, action_name => 'fn_standardCursorAccess');
loop
fetch in_cursor
into v_XMLData;
exit when in_cursor%notfound;
end loop;
/*some additional standard processing goes here*/
return v_XML;
exception
/*standard exception handling happens here*/
end;
The problem I've run into is in calling this function. I now have to call it like this:
open v_curs for select /*blah blah blah*/ where key_field = x and /*...*/;
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
What I'd like to do is call it like this:
open v_curs for c_getSomeData(x);
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
...reason being to minimize the amount of changes to my code (I don't want to have to cut/paste all these cursors to the functions that depend on them, and in the case where multiple functions depend on the same cursor, I'll have to wrap that in a new function).
Unfortunately, this does not work, Oracle returns an error saying
Error: PLS-00222: no function with name 'C_GETSOMEDATA' exists in this scope
Is what I'm trying to do even possible?
(Oracle version is 10.2)
EDIT:
I think a better way to describe what I'm doing is pass a reference to an explicit cursor to a function that will perform some common routines on the data returned by the cursor.
It appears that I cannot use an open-for statement with an explcit cursor, is there any other way to get a reference to an explicit cursor so I can pass that reference to a function? Maybe there is some other way I could approach this problem?
EDIT:
Copying and pasting from my earlier reply to R Van Rijn's reply:
I tried declaring the cursor in the package specification, and referencing it with the package name: open v_curs for PKG.c_getSomeData(x);... This gives me a new error, saying that PKG.c_getSomeData must be a function or array to be used that way.
UPDATE:
I spoke to our DBA here, he says it is not possible to have a ref cursor point to an explicit cursor. It looks like I can't do this after all. Bummer. :(
concerning the Error PLS-00222:
An identifier being referenced as a function 'c_getSomeData' was not declared or actually represents another object (for example, it might have been declared as a procedure).
Check the spelling and declaration of the identifier. Also confirm that the declaration is placed correctly in the block structure
This means you must create a function that actually returns some value(s).
Does this test script and output represent what you are trying to do? Instead of open v_curs for c_getSomeData(x); I'm setting the cursor variable = to the output from the function.
Our Test Data:
set serveroutput on
--create demo table
drop table company;
create table company
(
id number not null,
name varchar2(40)
);
insert into company (id, name) values (1, 'Test 1 Company');
insert into company (id, name) values (2, 'Test 2 Company');
insert into company (id, name) values (3, 'Test 3 Company');
commit;
Create Packages
create or replace package test_pkg as
type cursor_type is ref cursor;
function c_getSomeData(v_companyID number) return cursor_type;
end test_pkg;
/
create or replace package body test_pkg as
function c_getSomeData(v_companyID number) return cursor_type
is
v_cursor cursor_type;
begin
open v_cursor for
select id,
name
from company
where id = v_companyID;
return v_cursor;
end c_getSomeData;
end test_pkg;
/
Run Our Procedure
declare
c test_pkg.cursor_type;
v_id company.id%type;
v_name company.name%type;
begin
c := test_pkg.c_getSomeData(1);
loop
fetch c
into v_id, v_name;
exit when c%notfound;
dbms_output.put_line(v_id || ' | ' || v_name);
end loop;
close c;
end;
/
1 | Test 1 Company
PL/SQL procedure successfully completed.
I confess to finding your requirements a trifle hard to divine. You have posted a lot of code, but as I suggested in my comment, not the parts which would illuminate the problem. So possibly the following is way off-beam. But it is an interesting issue.
The following code shows how we can define a common, geneneric REF CURSOR, populate it with specific data from different queries, and then process them in a standardised fashion. Again, I apologise if this does not fit your business logic; if such is the case, please edit your question to explain where I have made a bloomer..
Here is the generic ref cursor. ...
create or replace package type_def is
type xml_rec is record (id number, payload xmltype);
type xml_cur is ref cursor return xml_rec;
end type_def;
/
and here is the standatd processor
create or replace procedure print_xml_cur
( p_cur in type_def.xml_cur )
is
lrec type_def.xml_rec;
begin
loop
fetch p_cur into lrec;
exit when p_cur%notfound;
dbms_output.put_line('ID='||lrec.id);
dbms_output.put_line('xml='||lrec.payload.getClobVal());
end loop;
close p_cur;
end print_xml_cur;
/
Two procedures which return the standard cursor with different data....
create or replace function get_emp_xml
( p_id in emp.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(ename))
from emp
where deptno = p_id
group by deptno;
return return_value;
end get_emp_xml;
/
create or replace function get_dept_xml
( p_id in dept.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(dname))
from dept
where deptno = p_id
group by deptno;
return return_value;
end get_dept_xml;
/
Now let's put it all together ....
SQL> set serveroutput on size unlimited
SQL>
SQL> exec print_xml_cur(get_emp_xml(40))
ID=40
xml=<?xml
version="1.0"?>
<ROWSET>
<ENAME>GADGET</ENAME>
<ENAME>KISHORE</ENAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL> exec print_xml_cur(get_dept_xml(20))
ID=20
xml=<?xml version="1.0"?>
<ROWSET>
<DNAME>RESEARCH</DNAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL>
OK, so the short answer from Oracle is: "can't be done!"
The short answer from me is: "Yeah - like Oracle is gonna stop me! So yes you can....but you need to be sneaky ... oh yes, and there is a 'but' or two....in fact...ugh!"
So, how can you pass your explicit cursor by reference? By nesting it into another cursor using the CURSOR() construct!
e.g.)
CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor;
end;
/
CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor
is
test_Cur sys_refcursor;
cursor gettest is
select CURSOR( -pass our actual query back as a nested CURSOR type
select ELCTRL_EVNT_ELCTRL_DISTRCT_ID,
ELECTORAL_DISTRICT_ID,
ELECTORAL_EVENT_ID
from ELCTRL_EVNT_ELCTRL_DISTRCT
where electoral_District_id = ed_id)
from dual;
begin
open gettest;
fetch gettest into test_Cur;
return test_Cur;
end;
end;
/
So what is the problem with this solution? It has a leak! The outer gettest cursor is never closed, because we don't close it and the client will only close the reference to the nested cursor that was selected for them - not the main cursor. And we can't close it automatically because closign the parent would force closing the nested cursor that you have returned by reference - and it is entirely likely that the client has not done using it.
So we have to leave a cursor open in order to return the nested cursor.
And if the user tried calling get_Cursor again with a new value of ed_id, they would discover that the session persistence in the package meant that the cursor handle is still in use and an error would be raised.
Now, we could fix that by first checking and closing the explicit cursor:
if gettest%isopen then
close gettest;
end if;
open gettest;
fetch gettest into test_Cur;
return test_Cur;
But still - what if the user never calls this again? How long 'til Oracle garbage-collects the cursor? And how many users running how many sessions calling how many functions that use this construct will be leaving cursors open after they are done with them? Better count on a huuuuuge overhead to leave all those open cursors layin' about!
No, you would need to have the users do a callback to explicitely close it or you would be clogging the database. But doing this would require changing the scope of the explicit cursor so that both functions can access it: So we need to make it at package scope, not function scope
CREATE OR REPLACE package CFSDBA_APP.test_Cursor
as
function get_cursor(ed_id number) return sys_refcursor;
function close_cursor return sys_refcursor;
end;
/
CREATE OR REPLACE package body CFSDBA_APP.test_Cursor
as
cursor l_gettest(p_ed_id in number) is
select CURSOR(
select ELCTRL_EVNT_ELCTRL_DISTRCT_ID, ELECTORAL_DISTRICT_ID, ELECTORAL_EVENT_ID
from ELCTRL_EVNT_ELCTRL_DISTRCT
where electoral_District_id = p_ed_id)
from dual;
function get_cursor(ed_id number) return sys_refcursor
is
l_get_Cursor sys_refcursor;
begin
open l_gettest (ed_id);
fetch l_gettest into l_get_Cursor;
return l_get_cursor;
end;
function close_cursor return sys_refcursor
is
begin
if l_gettest%isopen then
close l_gettest;
end if;
return pkg_common.generic_success_cursor;
end;
end;
/
OK, plugged the leak. Except it cost us a network round trip instead of the hard parse,...oh wait - and also except embedding a bind variable into an explicit cursor declared at this level is probably going to cause scoping issues of its own which was the reason we wanted to do this in the first place!
Oh, and in a session-pooling environment can two users step on each other's cursors? IF they aren't very carefull about doing an open-fetch-close before returning the session to the pool - we could wind up with some really interesting (and impossible to debug) results!
And how much do you trust the maintainers of the client code to be extra-diligent on this? YEah - me too.
So the short answer is: Yes, with a bit of sneakiness it could be done despite Oracle saying it can't.
The better answer is: But please don't! The extra round trip and potential for memory leaks and client code errors causing data problems makes this a very scary proposition.
It appears that what I wanted to do (have an open-for statement reference an existing explicit cursor) is simply not allowed in Oracle. :(

Resources