"Maximum open cursors exceeded": How to know for some DB object how many cursors are open? - sql

In Finacle, sometimes users are facing "The resource is busy" error due to the oracle error generated in back-end as "Maximum open cursors exceeded". Not that this error comes often, but we want to take precautionary measures and that is why we are trying to generate an alert for the Finacle engineers i.e. for ourselves so that whenever the maximum no of cursors exceeds for some DB object, we can get the alert. For that, we need to know what is the command to know how many cursors are open for the DB objects at a given time. Can somebody guide us on this matter please.
Another thing to clear my concept, does Oracle use any default cursor for querying a table? Because, my guess is this error is generating because number of users for this software has increased and there are lots of sessions getting opened for the same DB object with select and update query and that is how number of open cursors is getting increased.

I guess you don't close your cursors after you use it, For example, I customized the open_cursor parameter to allow user to have only one opened cursor (3 cursors oracle uses for its own needs):
SQL> conn / as sysdba
Connected.
SQL> alter system set open_cursors=4 scope=memory
2 /
System altered.
SQL> conn hr/hr
Connected.
SQL> var l_cursor refcursor;
SQL> ed
Wrote file afiedt.buf
1 begin
2 open :l_cursor for select 1 from dual;
3* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> var l_cursor1 refcursor;
SQL> ed
Wrote file afiedt.buf
1 begin
2 open :l_cursor1 for select 1 from dual;
3* end;
SQL> /
begin
*
ERROR at line 1:
ORA-01000: maximum open cursors exceeded
As you see I face the same error you had. Let's close the l_cursor cursor (the print command displays a cursor's content and closes it) and then open l_cursor1 once again:
SQL> print l_cursor
1
----------
1
SQL> ed
Wrote file afiedt.buf
1 begin
2 open :l_cursor1 for select 1 from dual;
3* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> print l_cursor1
1
----------
1
As you can see Oracle can successfully open the l_cursor1 cursor.
As a quick solution of your problem you can increase the open_cursor parameter with command (you system might need additional resources to keep the current performance):
alter system set open_cursors=800 scope=both
I used scope=memory, because I want this demo doesn't affect my system after the database server is restarted. You have to specify scope=both to persist this parameter in spfile. But I guess you have to exam your system and find out if you have opened cursors that aren't used anymore.
Your guess about "does Oracle use any default cursor for querying a table?" is true, Oracle uses cursors to query tables, you can make sure:
SQL> var l_cursor refcursor;
SQL> ed
Wrote file afiedt.buf
1 begin
2 open :l_cursor for select 1 from dual;
3* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> select * from employees;
select * from employees
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-01000: maximum open cursors exceeded
SQL> print l_cursor
1
----------
1
SQL> select * from employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME
----------- -------------------- -------------------------
EMAIL PHONE_NUMBER HIRE_DAT JOB_ID SALARY
------------------------- -------------------- -------- ---------- ----------
COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
-------------- ---------- -------------
205 Shelley Higgins
...
To get all opened cursors check this answer

Related

Query all views for specific text in Oracle database views

I was wondering if anyone had a query that would search all views to find specific text. The database version we are on is Oracle Database 12c. This will only be run in our dev/test database.
I'm newer to the company, new to this database structure, and new to using Oracle. I've only used MSSQL in the past. I couldn't find a data dictionary and felt bad always having to ask what something meant or where it was located.
I was trying to investigate some before asking. I'm trying to learn what all the columns mean and where all the data is connected to. I'm open to other suggestions.
For SQL, I have one that searches through the views and columns for data and is rather fast. I don't have an exact time. But, I thought it would be similar to running it in Oracle unless the database is a little different to where maybe running something like that won't return as quick. I found some queries for Oracle that search all tables, but I don't have access to any of the tables. How we have been given access is going through: other users > users > views > then query on that view.
I found this link that I thought might work - Oracle Search all tables all columns for string
When I run the first query in the accepted answer I get this error:
Error report -ORA-00932: inconsistent datatypes: expected - got CHAR
ORA-06512: at line 6
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action:"`
The string that I am searching for contains numbers and letters. Ex. 123ABC
When I run the second query, I let it run for four hours and still nothing returned. Is there anyway to speed that one up?
I'm open to any other queries, suggestions, and help of pointing me in the right direction.
Thank you!
You have to understand that searching all CHAR (and its variations) datatype columns (as "123ABC" is a string) within the whole database is a tedious and a long time running process. It takes no time in a few relatively small tables; but, on a large database, it really takes a long time. You can't use any indexes, so ... be patient.
Also, note that code (behind that link) searches through ALL_TAB_COLUMNS view which contains not only your tables' columns (owned by you), but everything you have access to, and that contains various users. Have a look; that's my 11gXE database on a laptop:
SQL> select owner, count(*) from all_tab_columns group by owner;
OWNER COUNT(*)
------------------------------ ----------
MDSYS 736
CTXSYS 320
SYSTEM 54
APEX_040000 3327
SCOTT 69
XDB 35
SYS 15211
7 rows selected.
SQL> select count(*) from user_tab_columns;
COUNT(*)
----------
69
SQL>
See the difference? Using ALL_TAB_COLUMNS, you're searching through ~20.000 columns. In my own schema (and USER_TAB_COLUMNS), that's only 70 of them.
Therefore, consider switching to USER_TAB_COLUMNS (if you do, remove all OWNER column references).
PL/SQL procedure (in this case (regarding code you took from the question whose link you posted), an anonymous PL/SQL block) won't display anything until it is over.
Alternatively, you could create a "log" table, an autonomous transaction stored procedure (so that you could insert into the log table and commit) so that you'd then "trace" execution from another session. Something like this:
Log table and procedure:
SQL> create table search_log (table_name varchar2(30), column_name varchar2(30));
Table created.
SQL> create or replace procedure p_log (par_table_name in varchar2,
2 par_column_name in varchar2)
3 is
4 pragma autonomous_transaction;
5 begin
6 insert into search_log (table_name, column_name)
7 values (par_table_name, par_column_name);
8 commit;
9 end;
10 /
Procedure created.
Code from the link you posted; switched to USER_TAB_COLUMNS, searching for table/column that contains the 'KING' string:
SQL> DECLARE
2 match_count integer;
3 v_search_string varchar2(4000) := 'KING';
4 BEGIN
5 FOR t IN (SELECT table_name, column_name
6 FROM user_tab_columns
7 WHERE data_type like '%CHAR%'
8 )
9 LOOP
10 EXECUTE IMMEDIATE
11 'SELECT COUNT(*) FROM '|| t.table_name||
12 ' WHERE '||t.column_name||' = :1'
13 INTO match_count
14 USING v_search_string;
15 IF match_count > 0 THEN
16 --dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
17 p_log(t.table_name, t.column_name);
18 END IF;
19 END LOOP;
20 END;
21 /
PL/SQL procedure successfully completed.
SQL> select * From search_log;
TABLE_NAME COLUMN_NAME
------------------------------ ------------------------------
EMP ENAME
SQL>
Only one table found; EMP and its ENAME column.

Why does my query return no records inside a program (PL/SQL), but does when ran manually in SQL?

Hi everyone and thank you for taking the time to help me.
I have the following query:
SELECT owner, object_name
FROM all_objects
WHERE owner IN ('EDI')
ORDER BY object_type, object_name;
As you can see in the screenshot it returns some values.
When I call the query from inside a program, it is not returning any values (see second screenshot).
Test code to illustrate this is:
CREATE OR REPLACE PROCEDURE my_test
AS
BEGIN
DBMS_OUTPUT.put_line('Pre-Loop');
FOR indx IN (SELECT owner, object_name
FROM all_objects
WHERE owner IN ('EDI')
ORDER BY object_type, object_name)
LOOP
DBMS_OUTPUT.put_line('Object: ' || indx.owner || '.' || indx.object_name);
END LOOP;
DBMS_OUTPUT.put_line('Post-Loop');
END;
/
BEGIN
my_test();
END;
/
The EDI schema is brand new, so I suspect this is a grants/privileges issue, but I can't seem to find what I may be missing in order for this to work. I have tried running this as both the EDI user and SYS.
EDIT after getting an answer:
I mentioned in a comment about finding an alternative to the official answer to this question and wanted to make sure it was shared for anyone reading this later so they can weigh the decision the same.
Applying grants like EXECUTE ANY PROCEDURE or SELECT ANY TABLE to the user that is expected to run the code will work, but I am sure there are reasons not to give such wide open grants.
Your stored procedure is a definer's rights stored procedure. That means that it doesn't have access to privileges that are granted via roles only those privileges that are granted directly to the owner of the procedure. Ad hoc SQL, on the other hand, runs with the privileges of whatever roles are enabled for the current session in addition to the user's direct grants. Most likely, the owner of the procedure has access to the tables in question via roles rather than via direct grants.
You can test this by running
set role none;
and then running the ad hoc SQL statement. If my wager is right, the ad hoc SQL will now return 0 rows since you've disabled all the roles for the session.
Depending on what you are going to do with the procedure, you may be able to solve the problem by turning it into an invoker's rights stored procedure.
CREATE OR REPLACE PROCEDURE my_test
AUTHID CURRENT_USER
AS
That will cause the procedure to run with the privileges of the invoker's session (including privileges granted through roles) rather than those of the definer. Assuming that all the users you want to call the procedure will have access to the EDI tables, that should be sufficient.
This is what you have now - no result at all:
SQL> CREATE OR REPLACE PROCEDURE my_test
2 AS
3 BEGIN
4 DBMS_OUTPUT.put_line('Pre-Loop');
5
6 FOR indx IN (SELECT owner, object_name
7 FROM all_objects
8 WHERE owner IN ('SCOTT')
9 AND rownum < 5 -- you don't have that
10 ORDER BY object_type, object_name)
11 LOOP
12 DBMS_OUTPUT.put_line('Object: ' || indx.owner || '.' || indx.object_name);
13 END LOOP;
14
15 DBMS_OUTPUT.put_line('Post-Loop');
16 END;
17 /
Procedure created.
SQL> BEGIN
2 my_test();
3 END;
4 /
PL/SQL procedure successfully completed.
But, if you enable serveroutput, here it is!
SQL> set serveroutput on --> this!
SQL>
SQL> BEGIN
2 my_test();
3 END;
4 /
Pre-Loop
Object: SCOTT.BONUS
Object: SCOTT.DEPT
Object: SCOTT.EMP
Object: SCOTT.SALGRADE
Post-Loop
PL/SQL procedure successfully completed.
SQL>

How to kill running sessions of a user in oracle with single sql query?

I want to automate following scenario -
Kill all of sessions of a user.
I want to achieve this in single sql query. I looked at Dropping connected users in Oracle database and one of the comments has given single sql query, but that is not helping kill all sessions in single query.
Can someone help me with single query to kill all session of a user?
Simply put (as far as I can tell), you can't do that.
Sessions are killed one-by-one, not all-sessions-for-user.
If you want to do that using a single line of code (as a final result), create a stored procedure which will do the job for you.
Connected as user which has privileges to do that (such as SYS, unless you created your own user for such purposes):
SQL> set serveroutput on;
SQL> create or replace procedure p_kill (par_username in varchar2) is
2 l_str varchar2(100);
3 begin
4 for cur_r in (select s.sid, s.serial#
5 from v$session s
6 where s.username = par_username
7 )
8 loop
9 l_str := 'alter system kill session ' || chr(39) ||
10 cur_r.sid || ', '|| cur_r.serial# || chr(39);
11 dbms_output.put_line(l_str);
12 execute immediate l_str;
13 end loop;
14 end;
15 /
Procedure created.
I have two sessions (opened in two separate SQL*Plus windows) for user MIKE and I want to kill them all:
SQL> exec p_kill ('MIKE');
alter system kill session '12, 133'
alter system kill session '139, 265'
PL/SQL procedure successfully completed.
SQL>
How does it reflect to MIKE? Like this:
SQL> select * From tab;
select * From tab
*
ERROR at line 1:
ORA-00028: your session has been killed
SQL>
So, as you can see, at the end this is all you need:
exec p_kill ('MIKE');

Why do you use Cursor in SQL Server?

I have been working on SQL Server for a long time and recently I was moved to a project which uses Oracle.
While I have heard of the term Cursor, I have not encountered one in the SQL Server. So my questions are:
Why do you even bother declaring Cursor in SQL Server? (or under what circumstances do you actually need a cursor?)
Why declaring cursor is mandatory in Oracle?
Why do you even bother declaring Cursor in SQL Server? (or under what circumstances do you actually need a cursor?) Anytime you can't do set based processing record by record processing may be needed; then you would need to use a cursor. Set based logic is all or nothing when in the transaction. Perhaps I'm processing individual records and I'm willing to accept situations where partial works. In this case I could manage each record individually get 99% complete and have the one that "fails" write out to a log. However usually this too can be done via set based logic if one thinks it though.
Why declaring cursor is mandatory in Oracle?
Unlike SQL server which returns data sets directly, Oracle returns data sets via REF CURSOR from package, procedure and function. So if you want a dataset back to work with, you must user a REF cursor.
Cursor returns the consistent data on the time of the cursor opening.
To show this I will open a cursor then I will change a row and I will compare the results of the database and the cursor:
SQL> conn hr/hr
Connected.
SQL> select employee_id, email from employees;
EMPLOYEE_ID EMAIL
----------- -------------------------
100 SKING
101 NKOCHHAR
...
205 SHIGGINS
206 WGIETZ
107 rows selected.
SQL> var rc refcursor
SQL> ed
Wrote file afiedt.buf
1 begin
2 open :rc for
3 select employee_id
4 , email
5 from employees
6 order by 1;
7* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> update employees set email = 'xxxxxx' where employee_id = 206;
1 row updated.
SQL> commit;
Commit complete.
SQL> print rc
EMPLOYEE_ID EMAIL
----------- -------------------------
100 SKING
101 NKOCHHAR
...
205 SHIGGINS
206 WGIETZ
107 rows selected.
SQL> select employee_id, email from employees;
EMPLOYEE_ID EMAIL
----------- -------------------------
100 SKING
101 NKOCHHAR
...
205 SHIGGINS
206 xxxxxx
107 rows selected.
As you can see cursor has the data that was in the database in the time of cursor opening. This is very important behavior, assume you want to work with bank accounts, for example, you want to calculate the sum. If someone changes the table data you will have consistent data and you will give the right answer anyway.
There are multiple situations when you need to use cursor in sql server and they are probably the same as in oracle. For example if you need to call a stored proc for every row of a dataset you need to use a cursor since you cannot do that with a "regular" query.

Dynamic Cursors

I use a cursor for the statement:
SELECT NAME FROM STUDENT WHERE ROLL = 1;
I used:
CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL = roll;
--roll is a variable I receive via a procedure, and the procedure works fine for the received parameter.
Upon executing this, I am able to retrieve all records with roll = 1.
Now, I need to retrieve the records of a group (possibly via a cursor), just like:
SELECT NAME FROM STUDENT WHERE ROLL IN (2, 4, 6);
But the values in the IN clause are known only at run time. How should I do this? That is, is there any way I could assign parameters to the WHERE clause of the cursor?
I tried using an array in the declaration of the cursor, but an error pops up telling something like: standard types cannot be used.
I used:
CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL IN (rolls);
--rolls is an array initialized with the required roll numbers.
First, I assume that the parameter to your procedure doesn't actually match the name of a column in the STUDENT table. If you actually coded the statement you posted, roll would be resolved as the name of the column, not the parameter or local variable so this statement would return every row in the STUDENT table where the ROLL column was NOT NULL.
CURSOR C
IS SELECT NAME
FROM STUDENT
WHERE ROLL = roll;
Second, while it is possible to use dynamic SQL as #Gaurav Soni suggests, doing so generates a bunch of non-sharable SQL statements. That's going to flood the shared pool, probably aging other statements out of cache, and use a lot of CPU hard-parsing the statement every time. Oracle is built on the premise that you are going to parse a SQL statement once, generally using bind variables, and then execute the statement many times with different values for the bind variables. Oracle can go through the process of parsing the query, generating the query plan, placing the query in the shared pool, etc. only once and then reuse all that when you execute the query again. If you generate a bunch of SQL statements that will never be used again because you're using dynamic SQL without bind variables, Oracle is going to end up spending a lot of time caching SQL statements that will never be executed again, pushing useful cached statements that will be used again out of the shared pool meaning that you're going to have to re-parse those queries the next time they're encountered.
Additionally, you've opened yourself up to SQL injection attacks. An attacker can exploit the procedure to read any data from any table or execute any function that the owner of the stored procedure has access to. That is going to be a major security hole even if your application isn't particularly security conscious.
You would be better off using a collection. That prevents SQL injection attacks and it generates a single sharable SQL statement so you don't have to do constant hard parses.
SQL> create type empno_tbl is table of number;
2 /
Type created.
SQL> create or replace procedure get_emps( p_empno_arr in empno_tbl )
2 is
3 begin
4 for e in (select *
5 from emp
6 where empno in (select column_value
7 from table( p_empno_arr )))
8 loop
9 dbms_output.put_line( e.ename );
10 end loop;
11 end;
12 /
Procedure created.
SQL> set serveroutput on;
SQL> begin
2 get_emps( empno_tbl( 7369,7499,7934 ));
3 end;
4 /
SMITH
ALLEN
MILLER
PL/SQL procedure successfully completed.
create or replace procedure dynamic_cur(p_empno VARCHAR2) IS
cur sys_refcursor;
v_ename emp.ename%type;
begin
open cur for 'select ename from emp where empno in (' || p_empno || ')';
loop
fetch cur into v_ename;
exit when cur%notfound;
dbms_output.put_line(v_ename);
end loop;
close cur;
end dynamic_cur;
Procedure created
Run the procedure dynamic_cur
declare
v_empno varchar2(200) := '7499,7521,7566';
begin
dynamic_cur(v_empno);
end;
Output
ALLEN
WARD
JONES
Note:As mentioned by XQbert,dynamic cursor leads to SQL injection ,but if you're not working on any critical requirement ,where security is not involved then you can use this .
Maybe you can pass rolls as a set of quoted comma separated values.
e.g. '1', '2' etc
If this value is passes into the procedure in a varchar input variable, the it can be used to get multiple rows as per the table match.
Hence the cursor
SELECT NAME FROM STUDENT WHERE ROLL IN (rolls);
will be evaluated as
SELECT NAME FROM STUDENT WHERE ROLL IN ('1','2');
Hope it helps