ora-01422 Fetch Size - oracle

I am getting the ORA-1422 error. Here is the error:
Connecting to the database Quantum Train.
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "TRAIN.UPDATE_MASTER_TO_NULL", line 26
ORA-06512: at line 2
Process exited.
Disconnecting from the database Quantum Train.
PROCEDURE UPDATE_MASTER_TO_NULL
is
-- This gets the pnm_auto_keys for the records in the warehouse and location with the specified manufacturer
Cursor Csr is
Select pnm.loc_auto_key
from parts_master pnm join warehouse whs on pnm.whs_auto_key = whs.whs_auto_key
where whs.warehouse_code = 'SHOP' and
pnm.loc_auto_key <> '39';
-- pnm.loc_auto_key <> '39' and
-- pnm.loc_auto_key <> '26' and
-- pnm.loc_auto_key <> '14';
loc_key integer;
Begin
For i in Csr Loop
-- Now get the loc_auto_key for your new location
Select loc2.loc_auto_key
into loc_key
From PARTS_MASTER loc2
Where loc2.loc_auto_key is null;
-- Assigne the new loc_auto_key to the selected record.
Update parts_master pnm2
Set pnm2.loc_auto_key = loc_key
where pnm2.loc_auto_key = i.loc_auto_key;
End Loop;
Commit;
End UPDATE_MASTER_TO_NULL;
Thanks,
Jeff

The problem is that this select statement is returning more than one row
Select loc2.loc_auto_key
into loc_key
From PARTS_MASTER loc2
Where loc2.loc_auto_key is null;
If you modify that query to return only one row, then your code should work.
Also, the entire procedure can be replaced and improved in efficiency by using a single update statement similar to this:
UPDATE parts_master pm
SET pm.loc_auto_key =
(SELECT *
FROM (SELECT pm2.loc_auto_key
FROM PARTS_MASTER pm2
WHERE pm2.loc_auto_key IS NULL)
WHERE ROWNUM = 1)
WHERE pm.whs_auto_key IN (SELECT w.whs_auto_key
FROM warehouse w
WHERE w.warehouse_code = 'SHOP')
AND pm.loc_auto_key <> '39';

Related

Stored procedure is taking too much time to update the table columns

I have created a stored procedure which is taking too much of time to update the columns of the table. Say 3 hrs to update 2.5k records out of 43k records.
So can I reduce the time of updating the records. Below is my logic for the same.
procedure UPDATE_MST_INFO_BKC
(
P_SAPID IN NVARCHAR2
)
as
v_cityname varchar2(500):='';
v_neid varchar2(500):='';
v_latitude varchar2(500):='';
v_longitude varchar2(500):='';
v_structuretype varchar2(500):='';
v_jc_name varchar2(500):='';
v_jc_code varchar2(500):='';
v_company_code varchar2(500):='';
v_cnt number :=0;
begin
select count(*) into v_cnt from structure_enodeb_mapping where RJ_SAPID=P_SAPID and rownum=1;
if v_cnt > 0 then
begin
select RJ_CITY_NAME, RJ_NETWORK_ENTITY_ID,LATITUDE,LONGITUDE,RJ_STRUCTURE_TYPE,RJ_JC_NAME,RJ_JC_CODE,'6000'
into v_cityname,v_neid,v_latitude, v_longitude, v_structuretype,v_jc_name,v_jc_code,v_company_code from structure_enodeb_mapping where RJ_SAPID=P_SAPID and rownum=1;
update tbl_ipcolo_mast_info set
CITY_NAME = v_cityname,
NEID = v_neid,
FACILITY_LATITUDE = v_latitude,
FACILITY_LONGITUDE = v_longitude,
RJ_STRUCTURE_TYPE = v_structuretype,
RJ_JC_NAME = v_jc_name,
RJ_JC_CODE = v_jc_code,
COMPANY_CODE = v_company_code
where SAP_ID=P_SAPID;
end;
end if;
end UPDATE_MST_INFO_BKC;
What adjustments can I make to this?
As far as I understand your code, It is updating TBL_IPCOLO_MAST_INFO having SAP_ID = P_SAPID Means It is updating one record and you must be calling the procedure for each record.
It is a good practice of calling the procedure once and update all the record in one go. (In your case 2.5k records must be updated in one call of this procedure only)
For your requirement, Currently, I have updated the procedure code to only execute MERGE statement, which will be same as multiple SQLs in your question for single P_SAPID.
PROCEDURE UPDATE_MST_INFO_BKC (
P_SAPID IN NVARCHAR2
) AS
BEGIN
MERGE INTO TBL_IPCOLO_MAST_INFO I
USING (
SELECT
RJ_CITY_NAME,
RJ_NETWORK_ENTITY_ID,
LATITUDE,
LONGITUDE,
RJ_STRUCTURE_TYPE,
RJ_JC_NAME,
RJ_JC_CODE,
'6000' AS COMPANY_CODE,
RJ_SAPID
FROM
STRUCTURE_ENODEB_MAPPING
WHERE
RJ_SAPID = P_SAPID
AND ROWNUM = 1
)
O ON ( I.SAP_ID = O.RJ_SAPID )
WHEN MATCHED THEN
UPDATE SET I.CITY_NAME = O.RJ_CITY_NAME,
I.NEID = O.RJ_NETWORK_ENTITY_ID,
I.FACILITY_LATITUDE = O.LATITUDE,
I.FACILITY_LONGITUDE = O.LONGITUDE,
I.RJ_STRUCTURE_TYPE = O.RJ_STRUCTURE_TYPE,
I.RJ_JC_NAME = O.RJ_JC_NAME,
I.RJ_JC_CODE = O.RJ_JC_CODE,
I.COMPANY_CODE = O.COMPANY_CODE;
END UPDATE_MST_INFO_BKC;
Cheers!!
3 hours? That's way too much. Are sap_id columns indexed? Even if they aren't, data set of 43K rows is just too small.
How do you call that procedure? Is it part of another code, perhaps some unfortunate loop which does something row-by-row (which is, in turn, slow-by-slow)?
A few objections:
are all those variables' datatypes really varchar2(500)? Consider declaring them so that they'd take table column's datatype, e.g. v_cityname structure_enodeb_mapping.rj_city_name%type;. Also, there's no need to explicitly say that their value is null (:= ''), it is so by default
select statement which checks whether there's something in the table for that parameter's value should be rewritten to use EXISTS as it should perform better than rownum = 1 condition you used.
also, consider using exception handlers (no-data-found if there's no row for a certain ID; too-many-rows if there are two or more rows)
select statement that collects data into variables has the same condition; do you really expect more than a single row for each ID (passed as a parameter)?
Anyway, the whole procedure's code can be shortened to a single update statement:
update tbl_ipcolo_mst_info t set
(t.city_name, t.neid, ...) = (select s.rj_city_name,
s.rj_network_entity_id, ...
from structure_enodeb_mapping s
where s.rj_sapid = t.sap_id
)
where t.sap_id = p_sapid;
If there is something to be updated, it will be. If there's no matching t.sap_id, nothing will happen.

Any efficient solution for that

declare
cursor cur1 is select * from address where aid in
(select Min(aid) from address group by
country,state,city,street_name,locality,house_no);
cursor cur2 is select * from address;
cur1_aid address.aid%type;
cur1_country address.country%type;
cur1_city address.city%type;
cur1_state address.state%type;
cur1_streetAddress address.street_name%type;
cur1_locality address.locality%type;
cur1_houseNo address.house_no%type;
cur2_aid address.aid%type;
cur2_country address.country%type;
cur2_city address.city%type;
cur2_state address.state%type;
cur2_streetAddress address.street_name%type;
cur2_locality address.locality%type;
cur2_houseNo address.house_no%type;
begin
open cur1;
loop
fetch cur1 into cur1_aid,cur1_country,cur1_state,cur1_city,cur1_streetAddress,cur1_locality,cur1_houseNo;
exit when cur1%NOTFOUND;
open cur2;
loop
fetch cur2 into cur2_aid,cur2_country,cur2_state,cur2_city,cur2_streetAddress,cur2_locality,cur2_houseNo;
exit when cur2%NOTFOUND;
if(cur1_country=cur2_country) and (cur1_state=cur2_state) and (cur1_city=cur2_city) and (cur1_streetAddress=cur2_streetAddress) and (cur1_locality=cur2_locality) and (cur1_houseNo=cur2_houseNo) then
if (cur1_aid!=cur2_aid) then
update employee_add set aid=cur1_aid where aid=cur2_aid;
delete address where aid=cur2_aid;
end if;
end if;
end loop;
close cur2;
end loop;
close cur1;
DELETE FROM employee_add a
WHERE ROWID > (SELECT MIN(ROWID) FROM employee_add b
WHERE b.eid=a.eid and b.aid=a.aid
);
end;
/
I have three table Employee(eid,ename) ,Address(aid,country,state,city,streetaddress,locality,houseNo) and a relationship table (M2M) MANY TO MANY TABLE employee_add(eid,aid),
I want to remove duplicates from address table and employee_add table without data loss
Assuming this is a one time de-duplication you could:
Create a new temporary set of eid <-> aid relationships based on the current address attached to an employee and always pick the min address record with matching data (this is what you are doing above)
Delete existing eid <-> aid relationships
Insert new relationships from step 1, drop step 1 data
Delete addresses that no longer have any employee attached
Something like this (untested as you did not provide any DDL or DML to create a working example from):
-- Step 1
CREATE TABLE employee_add_new AS
SELECT ea.eid,
(SELECT MIN(a2.aid)
FROM address a2
WHERE a2.country = a.country
AND a2.state = a.state
AND a2.city = a.city
AND a2.street_name = a.street_name
AND a2.locality = a.locality
AND a2.house_no = a.house_no) AS aid
FROM employee_add ea
INNER JOIN address a
ON a.aid = ea.aid;
-- Step 2
TRUNCATE TABLE employee_add;
-- Step 3
INSERT INTO employee_add
(eid,
aid)
SELECT eid,
aid
FROM employee_add_new;
DROP TABLE employee_add_new;
-- Step 4
DELETE FROM address a
WHERE NOT EXISTS (SELECT NULL
FROM employee_add ea
WHERE ea.aid = a.aid);
You could also change step 2 and 3 to drop the existing employee_add table and rename employee_add_new to employee_add, but I have no idea what your table structure looks like (columns, FKs, indexes, etc).

Oracle, ROWNUM=1 with FOR UPDATE clause?

My statement:
SELECT ROW_ID DATA_T WHERE CITY_ID=2000 AND IS_FREE=0 AND ROWNUM = 1
is used to retrieve the first row for a db table that has many entries with CITY_ID equal to 2000.
The ROW_ID that is returned is then used in an UPDATE statement in order to use this row and set IS_FREE=1.
That worked very well until two threads called the SELECT statement and the got the same ROW_ID obviously... That is my problem in a few words.
I am using ORACLE DB (12.x)
How do I resolve the problem? Can I use FOR UPDATE in this case?
I want every "client" somehow to get a different row or at least lock on of them
Something like this
function get_row_id return number
as
cursor cur_upd is
SELECT ROW_ID FROM TB WHERE CITY_ID=2000 AND IS_FREE=0 AND ROWNUM = 1
FOR UPDATE SKIP LOCKED;
begin
for get_cur_upd in cur_upd
loop
update TB
set IS_FREE = 1
where ROW_ID = get_cur_upd.ROW_ID;
commit work;
return get_cur_upd.ROW_ID;
end loop;
return null;
end;
commit or not after update depends on your logic.
Also you can return row_id without update&commit and do it later outside func.

ORA-01422: exact fetch returns more than requested number of rows pl/sql

I have this proc in my PL/SQL.
when i run it in sql dev, it is giving me exactly one row (one column)
PROCEDURE Newpo_code(sp_code OUT VARCHAR2)
IS
BEGIN
SELECT To_char(Max(num))
INTO sp_code
FROM (SELECT "ordernumber" num
FROM "purchaseorder"
WHERE ROWNUM = 1
ORDER BY "pkpurchaseorderid" DESC)
WHERE ROWNUM = 1;
SELECT Substr(sp_code, 10, 2)
INTO sp_code
FROM "purchaseorder";
END newpo_code;
but when i run it from code level
it is giving me exception
ORA-01422: exact fetch returns more than requested number of rows
What am I missing here?
I searched this exception but found no helpful results.
There is not much to go on, but most likely this is the line failing:
select substr(SP_CODE,10,2) into SP_CODE from "PurchaseOrder";
Why? Because there are probably more than 1 row in the PurchaseOrder table, which field you are trying to fit in one variable, SP_CODE.
You probably need a where clause on PurchaseOrder.
PROCEDURE Newpo_code(sp_code OUT VARCHAR2)
IS
BEGIN
SELECT To_char(Max(num))
INTO sp_code
FROM (SELECT "ordernumber" num
FROM "purchaseorder"
WHERE ROWNUM = 1
ORDER BY "pkpurchaseorderid" DESC)
WHERE ROWNUM = 1;
SELECT Substr(sp_code, 10, 2)
INTO sp_code
FROM dual;
END newpo_code;
Done!
Thanks anyways.

Why I'm getting the ORA-01003: no statement parsed error?

Why am I getting this error and what does it mean by no statement parsed.
ORA-01003: no statement parsed
Here is the code:
PROCEDURE ORIGINAL_TABLE.UPDATE_GROUPS IS
-- cursor loaded with the swam groups
CURSOR cursor1 IS
SELECT ID, NEW_DESCRIPTION
FROM NEW_TABLE.NEW_GROUP_TABLE#DB_LINK.X;
BEGIN
FOR C1_REC IN cursor1 LOOP
UPDATE
ORIGINAL_TABLE."GROUPS"
SET
GROUP_ID = C1_REC.ID
WHERE
ORIGINAL_TABLE."GROUPS".DESCRIPTION = C1_REC.NEW_DESCRIPTION;
IF (SQL%ROWCOUNT = 0) THEN
INSERT INTO
ORIGINAL_TABLE.GROUPS("GROUP_ID", "DESCRIPTION")
VALUES (C1_REC.ID, C1_REC.NEW_DESCRIPTION);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;
What I try to do with the code above is to update and old table with the values from a new table and in case that the new group doesn't exist insert it.
Update: Changed %ROWCOUNT > 0 for %ROWCOUNT = 0
Use MERGE statement, it does update/insert stuff more efficiently and pay attention your plsql doesn't provide it is intended for. It tries to make an update statement and if a record found it inserts another record. In order to fix it use
IF (SQL%ROWCOUNT = 0)
I presume the reason of the issue is the . in DBLINK name.
Moreover I would suggest to get rid of quotes for tables/fields just in case as well as schema name.
Another words delete all ORIGINAL_TABLE.
merge into groups g
using (
SELECT ID, NEW_DESCRIPTION
FROM NEW_TABLE.NEW_GROUP_TABLE#DB_LINK.X
) nt
on (nt.NEW_DESCRIPTION = g.description )
when matched then update set g.group_id = nt.id
when non matched then insert(GROUP_ID, DESCRIPTION)
values(nt.id, nt.NEW_DESCRIPTION)

Resources