Checking Count of Records from a Cursor - oracle

I have a custom-defined cursor that is pulling from a function that returns a nested table. I'm using the TABLE function (DDS_COMPILE) in the SQL of the cursor definition to pull the data. I have built a simple check after the pull to FETCH records into a Record DataType Object (also custom-defined to match) and, if no data if found using the %NOTFOUND, it should raise an Exception.
When I run the routine for a given set of input parameters (i.e., STORE X, for CLASS Y), the code returns 1 record. I can see the record if I run the function that queries the database by itself and the record displays only if my FETCH statement is commented out. If the FETCH is uncommented, it throws a NoData Exception. So what is wrong with my FETCH?
Here's a snippet of the code:
OPEN DataReturn FOR
SELECT
DDSRecs.OrgID
, DDSRecs.DistrictCode
, DDSRecs.DistrictName
, DDSRecs.StoreNumber
, DDSRecs.StoreName
, DDSRecs.AssociateLastName
, DDSRecs.AssociateFirstName
, DDSRecs.AssociateMiddleName
, DDSRecs.AssociateLDAP
, DDSRecs.AssociateUID
, DDSRecs.AssociateDeptName
, DDSRecs.DeptHeadCount
, DDSRecs.JobCode
, DDSRecs.ActSourceableStatus
, DDSRecs.CertTargetDate
, DDSRecs.CertName
, DDSRecs.CertExpiredOn
, DDSRecs.CertRevokedOn
, DDSRecs.CertRecertStartsOn
, DDSRecs.CourseNumber
, DDSRecs.CourseNumberName
, DDSRecs.CourseHours
, DDSRecs.RegID
, DDSRecs.CourseStatus
, DDSRecs.CourseActionStatus
, DDSRecs.CourseTargetDate
, DDSRecs.CourseCompletionDate
, DDSRecs.CourseDenseRank
, DDSRecs.DueDateStatus
, DDSRecs.DueDate
, DDSRecs.RegAction
, DDSRecs.DeliveryMode
, DDSRecs.MMUFlag
, DDSRecs.ErrorMessage
FROM TABLE(HDT_CORE_MAIN.DDS_COMPILE(
FinalOrgID
, ParamReportType
, ParamCourseNumberCheck
, ParamNameKeywordCheck
)) DDSRecs
;
LOOP
FETCH DataReturn INTO DDS_ERR_CHECK;
IF DataReturn%NOTFOUND THEN
RAISE DDS_ERR_NO_DATA;
END IF;
END LOOP;
CLOSE DataReturn;
There's much more to the procedure, of course, that's not related to this issue. Utlimately, it's the loop at the end that isn't working as intended. Anyone got any ideas?
Thanks in advance for your help! :)
UPDATE:
I've tried a couple of suggestions and changed the FETCH LOOP to the following:
LOOP
x := x + 1;
FETCH DataReturn INTO DDS_ERR_CHECK;
IF DataReturn%FOUND THEN
DBMS_OUTPUT.PUT_LINE('x = ' || x);
EXIT;
ELSE
RAISE DDS_ERR_NO_DATA;
END IF;
END LOOP;
This sees the 1 record (as it does not throw the Exception), but the 1 record is not displayed in the Output Variables. Note that the X did increment to 1 as intended.

You have no exit statement to terminate your loop - it will keep on fetching until %notfound no matter how many rows in your table function.

Related

How to work with checkbox and how to insert the records which is checked

Here in my oracle apex report i am having 2 apex_items ,checkbox and textfield..
I need to insert the records which is checked in report if I have selected 5 checkbox then it should insert all the 5 records, currently only one record is getting inserting, please help..
SQL in interactive report query:
select APEX_ITEM.CHECKBOX(1,pdcq.CHEQUE_NUMBER)"SELECT",
pdc.CUST_NAME ,pdr.RECEIPT_NUMBER ,pdcq.INSTALLMENT,
pdcq.CHEQUE_NUMBER,pdcq.CHEQUE_AMOUNT, APEX_ITEM.TEXT(2,null) Remarks
from pdc_customer pdc,pdc_receipt pdr,pdc_cheques pdcq
where pdc.CUST_ID=pdr.CUST_ID and pdc.CUST_ID=pdcq.CUST_ID and pdcq.status='Pending' AND pdcq.RECEIPT_NUMBER=pdr.receipt_number
and pdcq.APPROVER=NVL(SYS_CONTEXT('APEX$SESSION','APP_USER'), USER)
Code
begin
select NVL(SYS_CONTEXT('APEX$SESSION','APP_USER'), USER) into v_user from dual;
FOR I in 1 ..APEX_APPLICATION.G_f01.COUNT
LOOP
FOR J in 1 ..APEX_APPLICATION.G_f02.COUNT
LOOP
UPDATE PDC_STATUS_HISTORY SET STATUS='Approved',
REMARKS=APEX_APPLICATION.G_F02(j),
APPROVED_DATE=sysdate ,
APPROVED_BY=v_user
WHERE APPROVAL_LEVEL=1 and
CHEQUE_NUMBER=APEX_APPLICATION.G_f01(i);
END LOOP;
END LOOP;
end;
The issue you're probably running into is the following. The array for the checkbox only contains elements for the selected rows, whereas the array for the text element will contain an element for every row. So the index for the selected rows will not match the text row if not all checkboxes are checked. To work around that you can add a 3rd column of type APEX_ITEM.HIDDEN
select
APEX_ITEM.CHECKBOX(1,pdcq.CHEQUE_NUMBER)"SELECT",
pdc.CUST_NAME ,
...
APEX_ITEM.TEXT(2,null) Remarks,
APEX_ITEM.HIDDEN(3,pdcq.CHEQUE_NUMBER) hidden_checkno
from pdc_customer pdc
JOIN pdc_receipt pdr ON pdc.CUST_ID=pdr.CUST_ID
...
The process would then become:
BEGIN
FOR I in 1 ..APEX_APPLICATION.G_f01.COUNT
LOOP
FOR J in 1 ..APEX_APPLICATION.G_f03.COUNT
LOOP
IF APEX_APPLICATION.G_F01(i) = APEX_APPLICATION.G_F03(j) THEN
UPDATE PDC_STATUS_HISTORY
SET STATUS='Approved',
REMARKS=APEX_APPLICATION.G_F02(j),
APPROVED_DATE=sysdate ,
APPROVED_BY=NVL(SYS_CONTEXT('APEX$SESSION','APP_USER'), USER)
WHERE APPROVAL_LEVEL = 1 AND
CHEQUE_NUMBER = APEX_APPLICATION.G_f01(i);
END IF;
END LOOP;
END LOOP;
END;
Note1: The SELECT FROM DUAL is not needed, you can use that expression in your update statement directly.
Note2: Use ANSI JOIN style syntax instead of the very very old oracle syntax.

Converting function from Oracle PL/SQL to MS SQL Server 2008

I have several Oracle functions that are similar to the one below. I don't know much about Oracle and although I have made in roads on a major query re-write. I'd like to ask for some help on how to convert this function to SQL Server 2008.
I have tried using the online conversion tool at www.sqlines.com and benefited from many pages there... but not successful in converting this function....
Thanks in advance, John
Oracle source:
function OfficeIDMainPhoneID(p_ID t_OfficeID)
return t_OfficePhoneID
is
wPhID t_OfficePhoneID;
wPhID1 t_OfficePhoneID;
cursor cr_phone
is
select Office_PHONE_ID,IS_PHONE_PRIMARY
from Office_PHONE
where Office_ID = p_ID
order by SEQ_NUMBER;
begin
wPhID :=NULL;
wPhID1:=NULL;
for wp in cr_phone
loop
if wPhID is NULL
then wPhID1:=wp.Office_PHONE_ID;
end if;
if wp.IS_PHONE_PRIMARY = 'Y'
then
wPhID:=wp.Office_PHONE_ID;
Exit;
end if;
end loop;
if wPhID is NULL
then wPhID:=wPhID1;
end if;
return(wPhID);
end OfficeIDMainPhoneID;
SQL Server attempt:
create function OfficeIDMainPhoneID(#p_ID t_OfficeID)
returns t_OfficePhoneID
as
begin
declare #wPhID t_OfficePhoneID;
declare #wPhID1 t_OfficePhoneID;
declare cr_phone cursor local
for
select Office_PHONE_ID,IS_PHONE_PRIMARY
from Office_PHONE
where Office_ID = #p_ID
order by SEQ_NUMBER;
set #wPhID =NULL;
set #wPhID1=NULL;
declare wp cursor for cr_phone
open wp;
fetch wp into;
while ##fetch_status=0
begin
if #wPhID is NULL
begin set #wPhID1=wp.Office_PHONE_ID;
end
if wp.IS_PHONE_PRIMARY = 'Y'
begin
set #wPhID=wp.Office_PHONE_ID;
Exit;
end
fetch wp into;
end;
close wp;
deallocate wp;
if #wPhID is NULL
begin set #wPhID=#wPhID1;
end
return(#wPhID);
end ;
To answer the question about the functions as written
If you just want to fix the cursor so it works, one problem is the two "fetch wp into;" statements. You are saying "fetch the data and put it into" and then not giving it anything to put it into. Declare a couple of variables, put the data into them, then later use the variables, not the code. You need one variable per item returned in your cursor definition, so one each for Office_PHONE_ID and IS_PHONE_PRIMARY.
Also, you are trying to declare variables (and the function) as t_OfficePhoneID, I suspect that should be something like INT OR BIGINT instead (whatever the table definition for the column is).
Declare #OP_ID INT, #ISPRIMARY CHAR(1) --Or whatever the column is
later (in two locations),
fetch wp into (#OP_ID, #ISPRIMARY);
then use #OP_ID instead of wp.Office_PHONE_ID, and so on.
HOWEVER, I would throw away all the code in the function after declaring #wPhID, and do something else. Cursors suck if you can get what you want with a simple set based request. If you work your way through the oracle code, it is doing the following:
Get the id of the first phone number marked primary (in sequence order). If it didn't find one of those, just get the id of the first non-primary phone number in sequence order. You can do that with the following
set #wPhID = select TOP 1 Office_PHONE_ID
from Office_PHONE
where Office_ID = #p_ID
order by CASE WHEN IS_PHONE_PRIMARY = 'Y' THEN 0 ELSE 1 END, SEQ_NUMBER;
Return #wPhID and you're done.
I used "CASE WHEN IS_PHONE_PRIMARY = 'Y' THEN 0 ELSE 1 END" in the order by because I don't know what other values are possible, so this will always work. If you know the only possible values are 'Y' and 'N', you could use something like the following instead
order by IS_PHONE_PRIMARY DESC, SEQ_NUMBER;

Oracle Cursor Scripts errors

I have to construct a block to pull information from two tables using a cursor. As a further challenge, I have to identify the first item on the pull. I tried to use an IF statement to work through this. It errors out in several areas and I have no idea what I am doing wrong. Not asking for the answer per say, just enough of a push to get me moving again. Thanks. Here is the code I've put together so far:
DECLARE
CURSOR cur_pled IS
SELECT dd_pledge.idpledge, dd_pledge.pledgeamt, dd_pledge.paymonths, dd_payment.paydate, dd_payment.payamt
FROM dd_pledge, dd_payment
WHERE dd_payment.idpledge = dd_pledge.idpledge AND
dd_pledge.idpledge = 104
ORDER BY dd_pledge.idpledge, dd_payment.paydate;
TYPE type_pled IS RECORD
(pledID dd_pledge.idpledge%TYPE,
pledAmt dd_pledge.pledgeamt%TYPE,
payMonths dd_pledge.paymonths%TYPE,
payDate dd_payment.paydate%TYPE,
payAmt dd_payment.payamt%TYPE);
rec_pled type_pled;
lv_id_num dd_pledge.idpledge%TYPE := 0;
BEGIN
OPEN cur_pled;
LOOP
FETCH cur_pled INTO rec_pled;
EXIT WHEN cur_pled%NOTFOUND;
IF rec_pled.type <> lv_id_num THEN
DBMS_OUTPUT.PUT_LINE('First Payment');
ELSE DBMS_OUTPUT.PUT_LINE('Other Payment');
END IF;
END LOOP;
CLOSE cur_pled;
DBMS_OUTPUT.PUT_LINE(pledID || ' ' || dd_pledge.pledgeamt || ' ' ||
dd_pledge,payMonths || ' ' || dd_payment.payDate || ' ' ||
dd_payment.payAmt);
END;
There are loads of errors in your code. If you had formatted it correctly, you would have spotted some of them yourself!
Things that leapt out at me:
You're referring to dd_pledge in the final dbms_output.put_line, but dd_pledge isn't a variable. I think you meant to use rec_pled instead.
You refer to pledID in your final dbms_output.put_line statement - but this is a field defined in the record type, NOT a defined variable. I think you probably meant to use rec_pled.pledid
You're selecting the results of the cursor into rec_pled.type - however, "type" is not a field declared in the type_pled's definition! Did you mean rec_pled.idpledge instead?
You have dd_pledge,payMonths in your final dbms_output.put_line statement - the comma should be a full stop: rec_pled.payMonths
You're outputting the results after you've closed the results. Because this is just a record variable, you're only going to be outputting the results from the last row in the query.
Why aren't you doing a cursor for loop? That takes care of the exiting and declaring a record for you.
Anyway, I think you can achieve your results by using an analytic function in your query, rather than needing to use PL/SQL to do the work:
SELECT plg.idpledge,
plg.pledgeamt,
plg.paymonths,
pay.paydate,
pay.payamt,
case when row_number() over (partition by plg.idpledge, pay.paydate) = 1 then 'First Payment'
else 'Other Payment'
end type_of_payment
FROM dd_pledge plg
inner join dd_payment pay on (pay.idpledge = plg.idpledge)
--WHERE plg.idpledge = 104 -- add back in if you really need to do it for a single id
ORDER BY plg.idpledge, pay.paydate;

How to create an Oracle stored procedure with a parameter?

I'm a novice at SQL and am trying to create a Stored Procedure in Oracle database. The SPROC needs two date parameters (from_date and to_date) for my report to run. Maybe I'm confusing this with SQL Server code.
My code looks like this:
CREATE PROCEDURE uSP_RevPerSalesman
#from_date DATE
#to_date DATE
AS
BEGIN
SELECT DISTINCT
C.CUSTOMER_CODE
, MS.SALESMAN_NAME
, SUM(C.REVENUE_AMT)
FROM
C_REVENUE_ANALYSIS C
, M_CUSTOMER MC
, M_SALESMAN MS
WHERE
C.CUSTOMER_CODE = MC.CUSTOMER_CODE AND
MC.SALESMAN_CODE = MS.SALESMAN_CODE AND
MC.COMP_CODE = 'W1' AND
MS.COMP_CODE = '00' AND
C.REVENUE_DATE >= :from_date AND
C.REVENUE_DATE <= :to_date
GROUP BY
C.CUSTOMER_CODE, MS.SALESMAN_NAME
ORDER BY
C.CUSTOMER_CODE
END
GO
I get an error message when I run this code. The error message I get is:
ERROR ORA-00900: invalid SQL statement
When I run only the SELECT code, it works and gives me the right results. I just can't seem to make this into a SPROC.
Remove the GO, that is not valid in Oracle. Try a semicolon at the end instead, or a /, depending on where you're running this.

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