WITH Hold Option in Oracle - oracle

I am learning Oracle.
CURSOR A FOR TABLE1
LOOP
DELETE B TABLE RECORD BASED ON CURSOR A
END LOOP
I am reading a Cursor from Table A, and based on the result i am deleting 2 more table entries. Please let me know in this case i need to provide FOR UPDATE OF while declaring Oracle or Its not needed. In DB2 i know we need to give with hold option.
How to get the current user details in the query in oracle. In sql server if we give USER then the details will come.

This link may be useful while learning Oracle: http://www.oracle.com/pls/db112/portal.all_books
Open this link, click on PL/ at the top of the page, then open PL/SQL Language Reference,
and in chapter 13 PL/SQL Language elements find DELETE Statement extension:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/delete_statement.htm#LNPLS1187
Basically, to delete a record fetched from a cursor, a cursor must use FOR UPDATE clause, and a delete command must have WHERE CUFFENT OF <cursor-variable> clause.An example:
CURSOR c1 IS
SELECT employee_id, job_id, salary
FROM employees FOR UPDATE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_emp_id, my_job_id, my_sal;
EXIT WHEN c1%NOTFOUND;
IF my_job_id = 'SA_REP' THEN
DELETE employees
WHERE CURRENT OF c1;
END IF;
END LOOP;
END;

Related

Trying to create a DML file of the owner inserts Oracle

I am trying to create a DML file that contains all the inserts to a database using only a script and asking only for the owner name, I found some documentation about the creation of files in Oracle and some other about how to get the insert statements.
This is the query that gets the inserts
SELECT /*insert*/ * FROM ALL_TAB_COLUMNS WHERE OWNER = 'OwnerName';
And this is what I`m trying to do in order to create the file with the selected rows from the query
DECLARE
F1 UTL_FILE.FILE_TYPE;
CURSOR C_TABLAS IS
SELECT /*insert*/ * FROM ALL_TAB_COLUMNS WHERE OWNER = 'BETA';
V_INSERT VARCHAR2(32767);
BEGIN
OPEN C_TABLAS;
LOOP
FETCH C_TABLAS INTO V_INSERT;
EXIT WHEN C_TABLAS%NOTFOUND;
F1 := UTL_FILE.FOPEN('D:\Desktop\CENFOTEC\4 Cuatrimestre\ProgramaciĆ³n de Bases de Datos\Proyecto\FileTests','TestUno.dml','W');
UTL_FILE.PUT_LINE(F1, V_INSERT);
UTL_FILE.FCLOSE (F1);
END LOOP;
CLOSE C_TABLAS;
END;
I'm having trouble with the fetch, I'm getting this error: wrong number of values in the INTO list of a FETCH statement
I know that it is a basic one, but I can't figure out how many columns I am getting from the query above
Although I'm trying this way i wouldn't mind changing it, I need to create a DML file of all the inserts needed to replicate the database of the given user. Thanks a lot
In SQL Developer, when you use:
SELECT /*insert*/ * FROM ALL_TAB_COLUMNS WHERE OWNER = 'OwnerName';
Then the /*insert*/ hint is processed by SQL Developer on the client-side and converts the returned result set into DML statements.
To quote #ThatJeffSmith in his answer where he gave the above solution:
here is a SQL Developer-specific solution
That behaviour is specific to the SQL Developer client application.
In the Oracle database, when you use:
SELECT /*insert*/ * FROM ALL_TAB_COLUMNS WHERE OWNER = 'OwnerName';
Then /*insert*/ is an inline comment and it is IGNORED and has zero effect on the output of the query.
Therefore, when you do:
DECLARE
F1 UTL_FILE.FILE_TYPE;
CURSOR C_TABLAS IS
SELECT /*insert*/ * FROM ALL_TAB_COLUMNS WHERE OWNER = 'BETA';
V_INSERT VARCHAR2(32767);
BEGIN
OPEN C_TABLAS;
LOOP
FETCH C_TABLAS INTO V_INSERT;
EXIT WHEN C_TABLAS%NOTFOUND;
F1 := UTL_FILE.FOPEN('D:\Desktop\CENFOTEC\4 Cuatrimestre\ProgramaciĆ³n de Bases de Datos\Proyecto\FileTests','TestUno.dml','W');
UTL_FILE.PUT_LINE(F1, V_INSERT);
UTL_FILE.FCLOSE (F1);
END LOOP;
CLOSE C_TABLAS;
END;
/
The PL/SQL anonymous block will be processed by the database's PL/SQL engine on the server-side and it will context-switch and pass the cursor's SQL to the database's SQL engine where it will be run and the /*insert*/ comment is ignored and it will return all the columns.
I can't figure out how many columns I am getting from the query above.
One column for every column in the ALL_TABS_COLUMNS table. You can use:
SELECT * FROM all_tabs_columns FETCH FIRST ROW ONLY
And then count the columns. I made it 37 columns (but might have miscounted).
However
Trying to generate INSERT statements that correspond to all the rows in the ALL_TAB_COLUMNS table so that you can recreate the database is WRONG. You need to generate the DDL statements for each table and not generate DML statements to try to modify a data dictionary table (which, likely as not, if you try to modify data dictionary tables will leave your database in an unusable state).
If you want to recreate the database then use the answers in this question or backup the database and then restore it to the new database.

oracle: populate a table from another schema using PL/sql procedure

hi i'm newb in pl/sql :), this is for educational pruposes only.
the schama Dispatching including a table named Employes.and PRF schema that include a table named ZONE.
Dispatching : Employes(num_emp number,name nvarchar2,design_unit varchar2,design_zone varchar2)
and PRF: ZONE(num_zone number,design_zone varchar2,number_of_units number).
the problema is writing a pl/sql procedure to populate ZONE table from Employes table. this is my procedure :
create or replace procedure zoneD as
cursor cur is select design_zone,design_unit from dispatching.employes group by design_zone,design_unit;
varzone cur%rowtype;
begin
open cur;
fetch cur into varzone;loop
exit when cur%notfound;
insert into zone(num_zone,design_zone,nbr_of_unit) values (num_zone.nextval,varzone.design_zone,0);
update zone set nbr_of_unit =( select count(design_unit) from dispatching.employes);
end loop;
close cur;
end zoneD;
the unit is a town , each zone contains many units. in a simple way the prob the procedure does not insert the data i dont know if it is the right way to do that. (sorry about my english :)).
It seems that you are connected as PRF and want to fetch values that belong to DISPATCHING user. In order to do that, DISPATCHING has to grant (at least) SELECT on its EMPLOYEES table to PRF:
-- connect as DISPATCHING
grant select on employees to prf;
A procedure (as you're practicing PL/SQL) should utilize a cursor FOR loop as it is much easier to maintain than a loop which uses explicitly declared cursor (as you don't need to declare it as well as variable(s) you need to store its values into), open it, worry when to exit the loop and - finally - close it. Cursor FOR loop does all of that for you (OK, except writing a SELECT statement which is just the same as the one you'd use while declaring an explicit cursor).
-- connect as PRF
create or replace procedure zoned as
begin
-- cursor FOR loop - you can select both DESIGN_ZONE and count number of
-- units so that you wouldn't have to update that value separately
for cur_r in (select e.design_zone, count(*) number_of_units
from dispatching.employees e -- naming the owner which granted SELECT on its table to PRF user
group by e.design_zone
)
loop
insert into zone (num_zone, design_zone, number_of_units)
values (num_zone.nextval, cur_r.design_zone, cur_r.number_of_units);
end loop;
end;
/
That should do it (unless I made a typo).
Finally, a suggestion, if I may: do format your code properly. The one you posted is a mess difficult to read - no indentation, too long lines (break them!), and it contains only several lines. Imagine what happens when you have thousands of lines of code - who do you expect to debug it? Just a month or two after you've done with that code, you'll forget what you did and why (so comment it), and - if it is unformatted - you'll get a headache. Today's GUI tools offer automatic formatting, so - use it. Otherwise, there are free online formatters, such as Instant SQL Formatter.

PL/SQL "WHERE CURRENT OF" vs "ROWID"

Why Oracle made "where current of" syntax when you can use "rowid"? Example:
BEGIN
FOR rec IN (SELECT t.column1, t.rowid rid FROM test_table) LOOP
UPDATE test_table tb SET column1 = some_function(rec.column1) WHERE tb.rowid = rec.rid;
END LOOP;
COMMIT;
END;
DECLARE
CURSOR cur IS SELECT t.column1 FROM test_table;
param1 test_table.column1%TYPE;
BEGIN
LOOP
FETCH cur INTO param1;
UPDATE test_table tb SET tb.column1 = some_function(param1) WHERE CURRENT OF cur;
EXIT WHEN cur%NOTFOUND;
END LOOP;
COMMIT;
END;
Where Current Of is used to identify LAST FETCHED ROW in cursor. It's more safe, because You have 100% confidence, that f.e. You updating LAST FETCHED ROW from curosr. With Rowids there's danger, because it's really easy to mess up something.
Using WHERE CURRENT OF clause without having FOR UPDATE clause mentioned in your SELECT statement could be risky. Reason behind that is when you are not applying FOR UPDATE clause then you are not exclusively placing Row level lock to those rows which you are intending to update in the following UPDATE DML. And hence it opens an opportunity for outside world of violating data consistency i.e. some other user from different session may be looking to UPDATE same rows of your targeted table.
Also, in you learn more about WHERE CURRENT OF clause you will notice that during this clause Oracle internally makes use of ROWID's only to reach/identify the rows which needs to be updated.
Hope it helps !! Happy Programming

use of FOR UPDATE statement

I am using PL/SQL (Oracle 11g) to update the EMPLOYEES table salary column.
I have used two separate scripts to do the same thing i.e update the salary of employees.
One script uses FOR UPDATE OF statement where as another script doesn't uses it. In both cases I found that oracle holds the row level locks until we execute the ROLLBACK or COMMIT commands.
Then what is the difference in between two scripts?
Which one is better to use?
Here are the two scripts I am talking about:
-- Script 1: Uses FOR UPDATE OF
declare
cursor cur_emp
is
select employee_id,department_id from employees where department_id = 90 for update of salary;
begin
for rec in cur_emp
loop
update Employees
set salary = salary*10
where current of cur_emp;
end loop;
end;
--Script 2: Does the same thing like script 1 but FOR UPDATE OF is not used here
declare
cursor cur_emp
is
select employee_id,department_id from employees where department_id = 90;
begin
for rec in cur_emp
loop
update Employees
set salary = salary*10
where Employee_ID = rec.employee_id;
end loop;
end;
I found that Oracle acquired the row level locks on both cases. So, what is the benefit of using FOR UPDATE OF and Which is the better way of coding?
When you specify FOR UPDATE, the row is locked at the point that you SELECT the data. Without the FOR UPDATE, the row is locked at the point you UPDATE the row. In the second script, another session could potentially lock the row between the time that the SELECT was executed and the point that you tried to UPDATE it.
If you are dealing with a SELECT statement that returns relatively few rows and a tight inner loop, it is unlikely that there will be an appreciable difference between the two. Adding a FOR UPDATE on the SELECT also gives you the opportunity to add a timeout clause if you don't want your script to block indefinitely if some other session happens to have one of the row you're trying to update locked.

PL/SQL Procedure and a crystal report

I am trying to use the following procedure as a datasource for my crystal report. The query works as I expected but the problem is I can't figure out how to fetch back the data from those dummy tables - IFS_PR_DUMMY_TAB and IFS_PR_DUMMY2_TAB
CREATE OR REPLACE procedure dummy9_IFS_FR2_Sales (cdate IN date)
as
acontract customer_order.contract%type;
ashowroom customer_order.district_code%type;
aorderno customer_order.order_no%type;
amount number(10);
bcontract customer_order.contract%type;
bshowroom customer_order.district_code%type;
borderno customer_order.order_no%type;
bamount number(10);
CURSOR c2 IS
select contract, district_code ,count(order_no),
SUM(CUSTOMER_ORDER_API.Get_Total_Sale_Price__(order_no))
from CUSTOMER_ORDER
where order_no IN (select distinct order_no from customer_order_line where state IN ('Released') ) AND state IN ('Released') and to_char(date_entered,'MM/DD/YYYY')>=to_char(cdate,'MM/DD/YYYY')
and contract IN
('CERA','SAN','WOD','QEM','PIP','COT','KIT','MAR','PROJ')
group by contract,district_code, date_entered ;
CURSOR c2 IS
select contract, district_code ,count(order_no),
SUM(CUSTOMER_ORDER_API.Get_Total_Sale_Price__(order_no))
from CUSTOMER_ORDER
where order_no IN (select distinct order_no from customer_order_line where state IN ('Reserved') ) AND state IN ('Reserved') and to_char(date_entered,'MM/DD/YYYY')>=to_char(cdate,'MM/DD/YYYY')
and contract IN
('CERA','SAN','WOD','QEM','PIP','COT','KIT','MAR','PROJ')
group by contract,district_code, date_entered ;
begin
--For Released Orders
OPEN c1;
DELETE FROM IFS_PR_DUMMY_TAB;
loop
fetch c1 into acontract, ashowroom, aorderno, amount;
exit when c1%notfound;
Insert into IFS_PR_DUMMY_TAB
(DCONTRACT ,DSHOWROOM ,DORDERNO,DAMOUNT) values (acontract,ashowroom,aorderno,amount);
end loop;
close c1;
--For Reserved Orders
OPEN c2;
DELETE FROM IFS_PR_DUMMY2_TAB;
loop
fetch c2 into bcontract, bshowroom, borderno, bamount;
exit when c2%notfound;
Insert into IFS_PR_DUMMY2_TAB
(ECONTRACT ,ESHOWROOM ,EORDERNO,EAMOUNT) values (bcontract,bshowroom,borderno,bamount);
end loop;
close c2;
end;
The best way to solve your problem is to have your procedure return result sets. In Oracle we use REF CURSORS to achieve this. You don't need to populate the temporary tables any more, but we can use one of them to define the signature of the REF CURSOR.
create or replace package report_records as
type order_recs is ref cursor
return IFS_PR_DUMMY_TAB%rowtype;
end;
/
This procedure returns two ref cursors.
create or replace procedure dummy9_ifs_fr2_sales
(cdate in date
, c_released_orders in out report_records.order_recs
, c_reserved_orders in out report_records.order_recs
)
begin
open c_released_orders for
select contract
, district_code
,count(order_no)
,sum(customer_order_api.get_total_sale_price__(order_no))
from customer_order
where order_no
in (select distinct order_no
from customer_order_line
where state in ('Released') )
AND state in ('Released')
and to_char(date_entered,'MM/DD/YYYY')>=to_char(cdate,'MM/DD/YYYY')
and contract in ('CERA','SAN','WOD','QEM','PIP','COT','KIT','MAR','PROJ')
group by contract,district_code, date_entered ;
open c_released_orders for
select contract
, district_code
,count(order_no)
,sum(customer_order_api.get_total_sale_price__(order_no))
from customer_order
where order_no in (select distinct order_no
from customer_order_line
where state in ('Reserved') )
AND state in ('Reserved')
and to_char(date_entered,'MM/DD/YYYY')>=to_char(cdate,'MM/DD/YYYY')
and contract in ('CERA','SAN','WOD','QEM','PIP','COT','KIT','MAR','PROJ')
group by contract,district_code, date_entered ;
end;
/
As a matter of interest, if your date_entered column is a DATE datatype then you shouldn't use the TO_CHAR() conversion. If you are looking to handle rows which have a time element there are more efficient ways of handling that.
Ref Cursors are explained in detail in the Oracle PL/SQL User's Guide. Find out more.
edit
I'm not a Crystal Reports person. Google only seems to throw out some pretty old documentation (like this). But the consensus seems to be that CR is pretty restricted when it comes to interacting with Oracle stored procedures.
Apparently Crystal Reports needs the parameters declared as IN OUT. Also it appears it can only handle one such ref cursor parameter. Furthermore the ref cursor needs to be the first argument in the procedure's signature. Finally, and to my mind completely incredibly, the "stored procedure cannot call another stored procedure." We are used to design patterns which state that calling programs shouldn't have to know anything about the internals of the called program, but here we seem to have the internal workings of a called program being determined by the sort of program which calls it. That's pretty lame.
So, anyway, the above solution won't work for Crystal Reports. The only solution is to break it up into two procedures, with signatures like this:
create or replace procedure dummy9_ifs_fr2_sales_released
(c_released_orders in out report_records.order_recs
, cdate in date
)
as ...
create or replace procedure dummy9_ifs_fr2_sales_reserved
(c_reserved_orders in out report_records.order_recs
, cdate in date
)
as ...
These procedures could be bundled into a package (assuming CR can cope with that construct).
If the two procedure solution is not acceptable then I think you're left with David's approach: abandon stored procedures altogether, and just use raw SQL in the report.
Your code sucks.
Firstly, why are you using explicit cursors? Why wouldn't you just insert the rows into the tables?
Secondly, why delete when you could truncate much faster?
Thirdly, to_char(date_entered,'MM/DD/YYYY')>=to_char(cdate,'MM/DD/YYYY') applies a function to a column (so an index can't be used and the optimiser cannot get a good estimate of cardinality), and it converts the dates to a stupid character format with the month in the leading position so that it does not even do a correct comparison! 02-november-2009 sorts greater than 01-march-2010 in your logic.
Fourthly, why on earth are you using a stored procedure for this? Just run the damn queries and Union All them together if you need to.
This reminds me of all of the crap I saw from offshore report developers for two years at my previous job. Complete incompetence.

Resources