PL/SQL Oracle DBMS_JOB/DBMS_SCHEDULER passing parameters - asynchronous

How do I pass parameters to DBMS_JOB/SCHEDULER async call. I have long running PL/SQL procedure, which takes few parameters, including SQL table of integers (ids). How do I create procedure which calls this PL/SQL long running procedure asynchronosly and passes parameters to it?
PROCEDURE "RUN_ARCHIVING_SEARCH"
(
a_arch_rule_code_id_list IN INLISTNUMBERS,
a_lang IN VARCHAR2,
a_session_id IN NUMBER
)
Thnx,
Beef

Instead of creating a program whose program_type is 'STORED_PROCEDURE', try creating jobs where each job's job_type is 'PLSQL_BLOCK'. This way, your job_action is actually the PL/SQL block necessary to call your stored procedure with the appropriate arguments. You could theoretically fabricate the PL/SQL block on-the-fly with the code necessary to re-create a_arch_rule_code_id.

Depending on the maximum number of numbers you might need to pass to the procedure, you're probably better off storing the numbers in a table, then the job will query the table to get the numbers to process.

Related

PLSQL: No output displayed when using dynamic query inside Stored Procedure

I have been asked to create an SP which creates temporary table and insert some records.
I am preparing some sample code for the same as mentioned below but the output is not displayed.
create or replace procedure Test
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp_1(id number(10))';
execute immediate stmt;
insert into temp_1(id) values (10);
execute immediate 'Select * from temp_1';
execute immediate 'Drop table temp_1';
commit;
end;
When i am executing the SP by (Exec Test) desired O/P is not displayed.
I am expecting O/P of "Select * from temp_1" to be displayed. But it is not happening. Please suggest where i am doing wrong.
But i am interesting in knowing why ( execute immediate 'Select * from temp_1';) do not yield any result
For two reasons. Firstly because as #a_horse_with_no_name said PL/SQL won't display the result of a query. But more importantly here, perhaps, the query is never actually executed. This behaviour is stated in the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then *execute_immediate_statement( never executes.
You would have to execute immediate into a variable, or more likely a collection if your real scenario has more than one row, and then process that data - iterating over the collection in the bulk case.
There is not really a reliable way to display anything from PL/SQL; you can use dbms_output but that's more suited for debugging than real output, and you usually have no guarantee that the client will be configured to show whatever you put into its buffer.
This is all rather academic since creating and dropping a GTT on the fly is not a good idea and there are better ways to accomplish whatever it is you're trying to do.
The block you showed shouldn't actually run at all; as you're creating temp_1 dynamically, the static SQL insert into temp_1 will error as that table does not yet exist when the block is compiled. The insert would have to be dynamic too. Any dynamic SQL is a bit of a warning sign you're maybe doing something wrong, though it is sometimes necessary; having to do everything dynamically suggests the whole approach needs a rethink, as does creating objects at runtime.

Casting a String to a Symbol in PLSQL

I've created an anonymous PLSQL block to test and I'm running into an issue with the formatting.
set serveroutput ON
BEGIN
FOR I IN (SELECT DISTINCT do.SUBOBJECT_NAME from dba_objects do WHERE do.object_name='MY_TABLE' AND do.OBJECT_TYPE='TABLE PARTITION') LOOP
dbms_output.put_line(I.subobject_name);
SELECT
t.field
INTO
some_var
FROM
MY_TABLE PARTITION(I.subobject_name) t;
END LOOP;
END;
However I get several compilation errors, which I believe are related to the fact that I.subobject_name is a string. I believe the PARTITION function wants an actual partition symbol(proper term for this?), but I can't give it in this loop.
Is there any kind of casting function that can perform what I'm looking for?
Partition IS NOT A FUNCTION. Partition is keyword
In your context your whole statement is static , thus you CANNOT pass partition name into it; partition name must be specified at compile time.
You can re-create your statement dynamically and then pass partition name in the loop -
a-la you are doing it. Just make sure you will concatenate string and not use bind variables, or your statement at run time won't be parsed and won't run.
Name for the symbol is table partition

Oracle Stored Procedure benchmarking

I am a .Net web developer working with a legacy Oracle database. In the past I have worked with orm tools like nHibernate but all database communication here is required to be done via stored procedures. Our dba is asking us to pass a bunch of administrative info to every procedure we call including the username/domain/ip of the end user. This data is then used to call another stored procedure that logs usage info each time a procedure is called.
I am not that well versed in Oracle or Pl/Sql and I am trying to write my .Net code in a clean way that meets best practices whenever possible. It seems to me that this process of passing extra data through to every procedure is messy and tedious on both the .Net and Oracle ends.
Does anyone know of a better way to accomplish the dba's goal without all the overhead? Or is this a standard way of doing things that I should get used to.
I'd use a context rather than passing additional parameters to every stored procedure call. A context is a convenient place to store arbitrary session-level state data that the stored procedures can all reference.
For example, I can create a context MYAPP_CTX for my application and create a simple package that lets me set whatever values I want in the context.
SQL> create context myapp_ctx using ctx_pkg;
Context created.
SQL> create package ctx_pkg
2 as
3 procedure set_value( p_key in varchar2, p_value in varchar2 );
4 end;
5 /
Package created.
SQL> create package body ctx_pkg
2 as
3 procedure set_value( p_key in varchar2, p_value in varchar2 )
4 as
5 begin
6 dbms_session.set_context( 'MYAPP_CTX', p_key, p_value );
7 end;
8 end;
9 /
Package body created.
When the application gets a connection from the connection pool, it would simply set all the context information once.
SQL> begin
2 ctx_pkg.set_value( 'USERNAME', 'JCAVE' );
3 ctx_pkg.set_value( 'IP_ADDRESS', '192.168.17.34' );
4 end;
5 /
PL/SQL procedure successfully completed.
Subsequent calls and queries in the same session can then just ask for whatever values are stored in the context.
SQL> select sys_context( 'MYAPP_CTX', 'USERNAME' )
2 from dual;
SYS_CONTEXT('MYAPP_CTX','USERNAME')
--------------------------------------------------------------------------------
JCAVE
Realistically, you'd almost certainly want to add a clear_context procedure to the package that called dbms_session.clear_context( 'MYAPP_CTX' ) to clear whatever values had been set in the context when a connection was returned to the connection pool to avoid inadvertently allowing context information from one session to bleed over into another. You would probably also design the package with separate procedures to set and to get at least the common keys (username, ip address, etc.) rather than having 'USERNAME' hard-coded multiple places. I used a single generic set_value method just for simplicity.

How to restrict PL/SQL code from executing twice with the same values to the input parameters?

I want to restrict the execution of my PL/SQL code from repetition. That is, I have written a PL/SQL code with three input parameters viz, Month, Year and a Flag. I have executed the procedure with the following values for the parameters:
Month: March
Year : 2011
Flag: Y
Now, If I am trying to execute the procedure with the same values to the parameters as above, I want to write some code in the PL/SQL to restrict the unwanted second execution. Can anyone help. I hope the question is no ambiguous.
You can use the function result cache: http://www.oracle-developer.net/display.php?id=504 . So Oracle can do this for you.
I would create another table that would store the 3 parameters of each request. When your procedure is called it would first check the "parameter request" table to see if the calling parameters have beem used before. If found, then exit the procedure. If not found, then save the parameters and execute the rest of the procedure.
Your going to need to keep "State" of the last call somewhere. I would recommend creating a table with a datetime column.
When your procedure is called update this table. So, next time when your procedure is called.. check this table to see when was the last time your procedure was called and then proceed accordingly.
Why not set up a table to track what arguments you've already executed it with?
In your procedure, first check that table to see if similar parameters have already been processed. If so, exit (with or without an error).
If not, insert them and do the processing necessary.
Depending on how tight the requirements are, you'll need to get a exclusive lock on that table to prevent concurrent execution.
A nice plus would be an extra column with "in progress"/"done"/"error" status so that you can check if things are going on properly. (Maybe a timestamp too if that's important/interesting.)
This setup allows you to easily clear some of the executions (by deleting some rows) if you find things need to be re-done for whatever reason.
Make an insert in the beginning of the procedure, and do a select for update tolock the table so no one else can process any data and if everything goes ok with the procedure, commit and release the table 😀

Stored procedure to return a list of sequence IDs

I need a little stored procedure to do the following logic?
procedure_name(seq_name IN varchar2(50), block_count IN int, return_ids OUT)
loop from 1 to block_count
return_ids := select 'seq_name'||.nextVal from dual;
end loop
return return_ids
Basically what I want to do is have a stored procedure that lets me pass in a sequence name, how many IDs I need and return to me the generated listed of IDs that I can use in JAVA. The reason to do this for me is to return a list of IDs that I can use in JAVA and no one else is using those sequence IDs. Where they will be used in some other bulk inserts later down the line. In essence, reserve a block of sequence IDs.
Here is one way to return an array from PL/SQL procedure.
Create a collection type of numbers, initialize it in your procedure and populate it with numbers to return. For example:
create or replace type narray as table of number;
create or replace procedure get_seq_ids(seq_name in varchar2,
block_count in number, return_ids out narray)
as
begin
return_ids := narray();
return_ids.extend(block_count);
for i in 1 .. block_count
loop
execute immediate 'select ' || seq_name || '.nextval from dual'
into return_ids(i);
end loop;
end;
/
I'd be concerned over the logic that requires IDs to be generated before records are inserted into the database.
Alternatively you may want to consider inserting rows first, selecting the ids from the rows, and then using an update statement to do your bulk operation. This however is still not as preferable as having the Java code not depend on ids until after the actual information is ready to be inserted.
You can push your information into XML (or any other data format your database can understand) and then call a stored procedure to do the bulk inserts.
Another option may be to use the RETURNING clause to return the sequence values automatically after the insert.
IMHO, the best thing you can do is just referencing sequence_name.nextval right in your INSERT INTO, in the VALUES clause.
You said you want to avoid others using the same IDs. Referencing this site:
The sequence (or Oracle, for that matter) ensures that no other session or other call to nextval within the same session gets the same number from the sequence.
So, the uniqueness of a sequence' numbers are guaranteed in Oracle.
Here's what I do for the Java application I support (which also uses bulk inserts into deeply hierarchical tables)
PROCEDURE get_nextvals
(
p_values OUT SYS_REFCURSOR,
p_count IN PLS_INTEGER
)
IS
-- return the next p_count values from the PK sequence
BEGIN
OPEN p_values FOR
SELECT
<schema>.<sequence>.nextval
FROM
dual
CONNECT BY
LEVEL <= p_count
;
END;
It was easier to just pass a cursor out to java than having the app use a table type defined in the DB.

Resources