Oracle: Dynamically create package scripts without writing to a file - oracle

I have built my own repository of tables in my oracle database.
I use this repository to create standardazied packages per table.
Up to now I write a bunch of scripts into a utitlity folder.
I don't want to go that extra way though the file system anymore.
This is an example file:
CREATE OR REPLACE PACKAGE vpk_0003_produkte
AS
TYPE t_cursor IS REF CURSOR;
PROCEDURE p_insert (p_rc OUT NUMBER,
p_rc_text OUT VARCHAR2,
p_logsql IN NUMBER,
p_logperformance IN NUMBER,
p_user_id IN NUMBER,
p_mandant IN NUMBER,
-- PK Columns
p_id IN OUT x_0003_produkte.id%TYPE
-- Other Columns
, p_requestid IN x_0003_produkte.requestid%TYPE);
PROCEDURE p_update (p_rc OUT NUMBER,
p_rc_text OUT VARCHAR2,
p_logsql IN NUMBER,
p_logperformance IN NUMBER,
p_user_id IN NUMBER,
p_mandant IN NUMBER,
p_where IN VARCHAR2,
-- PK Columns
p_id IN OUT x_0003_produkte.id%TYPE
-- Other Columns
, p_requestid IN x_0003_produkte.requestid%TYPE);
END vpk_0003_produkte;
Is there a way to execute these lines directly without writing a file?
I know of "OPEN CURSOR FOR" in conjunction with a variable that contains a SELECT to get data. But how to execute/write?
ADDITIONAL INFO: One package code easily has 27.000 characters! and around 1000 lines.
Thanks.

If you can assemble the entire source of the package DDL into a single VARCHAR2(32000) local variable, you should be able to use EXECUTE IMMEDIATE to run the DDL
EXECUTE IMMEDIATE l_your_ddl_stmt;
Depending on the Oracle version, you may be able to call EXECUTE IMMEDIATE with a CLOB if the DDL statement might exceed 32000 bytes. You can also use dbms_sql passing in a collection of VARCHAR2(4000) strings that are combined into the DDL that you want to execute.
Assuming that you are checking your generated code into source control, though, rather than just checking in the code that generates the package, it may well be easier to generate the files on the file system as an intermediate step since that allows you to both commit to source control and to create the object in the database.

Related

How to transfer pop up window functionality into code in Oracle SQL for variable declaration?

I am preparing Oracle SQL program for my team to use. This can eventually used by other teams as well by changing few where conditions. We all are using Toad for Oracle to run the query. So I added variables in the query. See the example code below.
DECLARE v_lob VARCHAR(2);BEGIN v_lob := 't' ;END;
select :v_lob as Test from dual
My issue is when Toad has a pop-up window option to bind the variable. Since my team is using v_lob := 't' any way, I prefer them not to have enter it each time when they query. How can I remove the pop-up window option and use the value 't' for the variable as in the code?
You might want to use a CONTEXT for holding a large number of session variables. This solution should work with any IDE.
One user, one time, must create the context and create a package for setting the context values (based on this oracle-base article):
--Create a package on a shared schema.
create or replace package context_api is
procedure set_parameter(p_name varchar2, p_value varchar2);
end;
/
create or replace package body context_api is
procedure set_parameter(p_name varchar2, p_value varchar2) is
begin
dbms_session.set_context('my_context', p_name, p_value);
end;
end;
/
--Create a context on a shared database.
--(Note that contexts are global objects and don't belong to a specific schema.)
create or replace context my_context using context_api;
Each user then needs code like this in their worksheets:
--At the top of the worksheet, set the variables like this:
begin
context_api.set_parameter('v_lob', 'asdf');
end;
/
--Call the variables with SYS_CONTEXT:
select sys_context('my_context', 'v_lob') as test from dual;
TEST
----
asdf

Hung up on For In Loop and variable for external table loader

I have to load a number of files every day into our database system. My solution was to use a java procedure to generate a table of all the files in the directory folder and loop through each of them through the external table loader. I'm running into two hangups with this
Declare
what_to_load VARCHAR2(255);
CURSOR folder_contents
IS
select filename
from database.DIR_LIST
where filename like 'DCOpenOrders_%'
and filename like '%.csv';
BEGIN
DELETE FROM database.DIR_LIST;
database.GET_DIR_LIST( 'directory_path_files_are_in' );
FOR each_record IN folder_contents
LOOP
what_to_load := each_record.filename;
EXECUTE IMMEDIATE 'DROP table database.my_table';
execute immediate 'CREATE table database.my_table
(Region VARCHAR2(10),
District VARCHAR2(10),
Originating_Store VARCHAR2(80),
Order_Date VARCHAR2(30),
Ship_Location VARCHAR2(10),
Orig_Ord_No VARCHAR2(30),
Field_G VARCHAR2(30),
Line_No VARCHAR2(10),
POS_UPC VARCHAR2(30),
Item_Descr VARCHAR2(80),
Ord_Qty VARCHAR2(10),
Line_Status VARCHAR2(30),
Report_Date VARCHAR2(30),
Ship_Type VARCHAR2(30),
ERR_FLAG VARCHAR2(10),
ERR_LOG VARCHAR2(800)
)
ORGANIZATION EXTERNAL
( type oracle_loader
default directory WORK_DIR
access parameters
( records delimited by NEWLINE
skip 1
fields terminated by '',''
optionally enclosed by ''"''
missing FIELD VALUES are NULL)
location ('''||each_record.filename||''')
)
reject limit unlimited';
Execute Immediate 'Grant All on database.my_table to USER';
* merge statement goes here*
End Loop;
commit;
end;
Again, the idea is that every time this runs it will get the new list of csv files in the dir_list table with the java procedure get_dir_list, then for every file name I set as equal to the variable and use the variable in the external table loader to load up the file.
I'm running into [s]two[/s] problems
EDIT: Ok, making the corrections below to cursor row identification, now I hit the point where when I go to the second pass through my cursor appears to be wrong or missing - it will go through a loop just fine if the only action is to do a put_line. But with an execute immediate statement in there such as the "Grant All" then as soon as it completes one pass it throws ORA-08103 at the top of the loop and refuses to go on
3) I'm aware of an ask tom on this (https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:37593123416931) that says to use the alter table command. However when I try that it doesn't accept my attempt at that
execute immediate 'alter table database.my_table location('''||filename||''')';
throws out an error (plus I'd still need to get it to do another loop there to put the name of the current file into the external loader)
Any suggestions or help? I should note that we are on windows, not unix (since most solutions people offer on these places assume the latter) and I can't grab another program or module to do the job due to approval restrictions (since that seems to be another common solution)
Thanks!
For your first problem, your cursor loop variable is confusingly called filename. Your are referring to that record directly, instead of the column from the cursor. Changing the name slightly to make it a little clearer:
FOR filenames IN folder_contents
LOOP
what_to_load := filesnames.filename;
The rest is less obvious, but it isn't going to be happy that you're dropping and recreating the table in the middle of a block that refers to it statically. You need to make all references dynamic:
execute immediate 'Grant All on database.my_table ...';
-- grant to who/what? and why?
And your merge will have to be dynamic too. At least unless you can get the alter table to work, but you haven't said what the problem is with that. Actually, from what you posted, that's the same cursor variable reference problem:
execute immediate 'alter table database.my_table location('''||filenames.filename||''')';
If you aren't dropping/creating the table in the block, and create it once statically and just alter it, then you can use a static merge - just the alter needs to be dynamic.
A simpler approach might be to create the external table once, with a specific fixed name; loop through the list of real files; and for each of those in turn, rename or copy that to the fixed file name and perform the merge. Each time you query the external table it rereads the file anyway, so changing its contents in the background is OK. Dropping/recreating or even altering the table then wouldn't be necessary.
You could also, as that Ask Tom pst mentions, supply all the file names to the external table at once, as they have the same structure, either with the drop/create or with the alter approach.

How to use Oracle Collection of Record type in table?

I was trying to explore Oracle Collection and Record type. How do they work ? I wrote below script, but unable to compile them. What is wrong here ?
create or replace package pkg is
type ty_rec is record(empno emp.empno%type,ename emp.ename%type,sal emp.sal%type);
end pkg;
/
create or replace package body pkg is
end pkg;
/
create or replace type ty_varray is varray(5) of pkg.ty_rec;
/
create or replace type ty_table is table of pkg.ty_rec;
/
create table tab1(
id number,
col_arr ty_varray,
col_tab ty_table
) nested table col_tab store as tab1_col_tab;
/
Also, anyone could explain the Nested Table vs Varray when we are using them in table column. How do they stores data and which one is faster ?
Note: I'm using scott schema, which has default emp table
Records are a PL/SQL construct. This means they cannot be used in pure SQL statements. So you need to create the "record" type as a pure SQL object:
create or replace type ty_emp_rec as object
(empno number
,ename varchar2(20)
,sal number);
/
create or replace type ty_emp_varray is varray(5) of ty_emp_rec;
/
create or replace type ty_emp_table is table of ty_emp_rec;
/
I agree it would be highly neat if your code worked, not least because the ability to define attributes using the %TYPE syntax would be extremely useful. But alas that's restricted to the PL/SQL engine too.
This limitation is down to the ability to declare heap table columns with user-defined types, as your last example shows. Oracle requires its columns to be strongly-typed. The %TYPE syntax would create all sorts of problems here: consider what would happen if emp.empno changed from NUMBER to VARCHAR2, or vice versa.

Log In procedure not working in oracle 11g

My table contain only two row. When i am giving empid and password of another row stil login is done. This is my procedure
CREATE OR REPLACE PROCEDURE prco_LoginCheck(Emp_Id IN number,
Cpassword In varchar2,
cur_out out Types.cursor_type) AS
BEGIN
open cur_out for
select count(*) from TBL_REGISTRATION a
where a.confirm_password= Cpassword and a.emp_id=Emp_Id;
END prco_LoginCheck;
Aside from the fact that storing a password in a table is a horribly insecure design (you should only be storing a hash of the password plus some random salt), and the fact that it doesn't make a lot of sense to define your own weak ref cursor type rather than using the built-in sys_refcursor (unless you really need to work with ancient versions of Oracle), and that returning a cursor from a procedure seems like a really odd way to do a login rather than writing a function that returns a boolean or some indicator, the problem is one of naming conventions.
When you write the SQL statement
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= Cpassword
and a.emp_id=Emp_Id;
I assume that your intention is to compare the emp_id column in tbl_registration to the emp_id parameter. That is not actually what is happening, however. Your unqualified emp_id is resolved as a column in the table not the parameter. Normally, people use a naming convention to ensure that parameter names don't match column names. I, for example, generally use a p_ prefix for parameters and l_ for local variables. That would look something like
CREATE OR REPLACE PROCEDURE prco_LoginCheck(p_Emp_Id IN number,
p_password In varchar2,
p_cur_out out Types.cursor_type) AS
BEGIN
open p_cur_out for
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= p_password
and a.emp_id=p_Emp_Id;
END prco_LoginCheck;
It is also possible to use your existing parameter names and just fully qualify the emp_id to force it to resolve to the parameter name
CREATE OR REPLACE PROCEDURE prco_LoginCheck(Emp_Id IN number,
Cpassword In varchar2,
cur_out out Types.cursor_type) AS
BEGIN
open cur_out for
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= Cpassword
and a.emp_id=prco_LoginCheck.Emp_Id;
END prco_LoginCheck;

PL/SQL Procedure - Ticket system

I'm trying to make a Procedure in PL/SQL for a ticket system for my website.
I want a simple procedure that saves some data into the database.
It will have name of the user (random VARCHAR2) and a number and the date of today as input.
When this procedure is called it will automatically higher a number in the database (Maybe primary key) and add the input into the database. But I have no idea how to make something like this with the option to have more people access it at once.
Can anyone help please?
For the people that want to know what I have already.
CREATE OR REPLACE PROCEDURE VoegBoeteToe
(v_speler IN number, v_date IN date, v_boete IN number)
IS VoegBoeteToe
DECLARE
v_nextNum NUMBER;
BEGIN
SELECT BETALINGSNR
INTO v_nextNum
FROM BOETES
ORDER BY BETALINGSNR DESC;
v_nextNum := v_nextNum + 1;
INSERT INTO BOETES VALUES(v_nextNum,v_speler,v_date,v_boete);
end;
/
The fourth line of your procedure should be IS instead of IS VoegBoeteToe. In addition, remove the fifth line (DECLARE), which is not needed in a procedure. And use a sequence to derive the sequential number safely.
So, when all's said and done, your procedure should look something like:
CREATE SEQUENCE BOETES_SEQ;
CREATE OR REPLACE PROCEDURE VoegBoeteToe
(v_speler IN number, v_date IN date, v_boete IN number)
IS
BEGIN
INSERT INTO BOETES VALUES(BOETES_SEQ.NEXTVAL,v_speler,v_date,v_boete);
end;
It would also be a very good idea to include the list of field names in the BOETES table into which you're inserting values. This will make life better for anyone who has to maintain your code, and may save you from making a few errors.
Share and enjoy.
You need to create SEQUENCE
CREATE SEQUENCE DUMMY_SEQ
/
Then in Your code You can use something like that:
select DUMMY_SEQ.NEXTVAL into myVar from dual;
First, create a sequence. A sequence generates an ever incrementing number whenever it is invoked. See this for some nice examples. The documentation could be useful too.
CREATE SEQUENCE user_sequence
START WITH 1000 -- your sequence will start with 1000
INCREMENT BY 1; -- each subsequent number will increment by 1
Then,
SELECT user_sequence.NEXTVAL INTO v_nextNum FROM DUAL;

Resources