In my pl/SQL code there is an error which states that "The number specified in exact fetch is less than the rows returned" any ideas - sql

This is my PL/SQL error:
Error report
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 67
00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
PL/SQL script:
DECLARE
cursor cur is (select distinct sor_ident
from CLEVA_OWNER_BE.f_polices, CLEVA_OWNER_BE.f_sit_objet_risque, CLEVA_OWNER_BE.f_produitass, cleva_owner_be.f_desc_stat_100, CLEVA_OWNER_BE.f_p_c_client, CLEVA_OWNER_BE.f_garantie_dyn
where pol_ident = sor_ptrpolid
and sor_ident = d100_ptrsorid
and bpcl_ident = pol_ptrclid
and pol_ptrpasid = pas_ident
and sor_ident = gad_ptrsorid
and pas_code_produit IN ('BFOV','BFOV2','BFOV5','BFOV6')
and BPCL_LIB24 = '1'
and d100_lib01 IN ('1','2','4','5','6')
and d100_lib09 > '1997'
and ( pol_codetat in ('1', '3', '6') or ( pol_codetat = '0' and pol_datcre > trunc ( sysdate - 183 ) ) )
and gad_code IN('RCVVY','RCLTY','RCVCT','RCHTY')
);
type tt_cur is varray(50) of cur%rowtype;
t_cur tt_cur;
type tt_cld is varray(50) of CLEVA_OWNER_BE.f_clause_dyn%rowtype;
t_cld tt_cld := tt_cld();
cld_key pls_integer;
v_ordre varchar2(3900);
v_desc varchar2(5000);
v_titre varchar2(5000);
BEGIN
t_cld.extend();
t_cld(1).CLD_IDENT := null;
t_cld(1).CLD_PTRSORID := null;
t_cld(1).CLD_TEXTE := null;
t_cld(1).CLD_ORDRE := null;
t_cld(1).CLD_CODE := 'RCVT7';
t_cld(1).CLD_SEQUENTIEL := null;
t_cld(1).CLD_SERVICES := null;
t_cld(1).CLD_INT_LOT_IMP := null;
t_cld(1).CLD_INT_LOT_EXP := null;
t_cld(1).CLD_LIBRE := 0;
t_cld(1).CLD_CHAPITRE := null;
t_cld(1).CLD_S_CHAPITRE := null;
t_cld(1).CLD_SS_CHAPITRE := null;
t_cld(1).CLD_TYPE := 2;
t_cld(1).CLD_TABLE := null;
t_cld(1).CLD_REFECHO := null;
t_cld(1).CLD_PTRSARID := null;
t_cld(1).CLD_TITRE := null;
t_cld.extend(49,1);
Line 67 select nvl( max(CLD_ident), 30000000) into cld_key
from CLEVA_OWNER_BE.f_clause_dyn
where CLD_ident between 30000000 and 49999999;
select TGA_ORDRE, tga_description, tga_titre into v_ordre, v_desc, v_titre
from cleva_owner_be.f_tarif_gar
where TGA_CODE = 'RCVT7';
OPEN cur;
LOOP
FETCH cur BULK COLLECT INTO t_cur LIMIT 50 ;
FOR i IN 1 .. t_cur.COUNT
LOOP
cld_key := cld_key + 1;
t_cld(i).CLD_IDENT := cld_key;
t_cld(i).CLD_PTRSORID := t_cur(i).sor_ident;
t_cld(i).CLD_TEXTE := v_desc;
t_cld(i).CLD_TITRE := v_titre;
t_cld(i).CLD_ORDRE := v_ordre;
END LOOP;
FORALL j IN 1..t_cur.COUNT
INSERT INTO CLEVA_OWNER_BE.f_clause_dyn VALUES t_cld(j);
COMMIT;
EXIT WHEN cur%NOTFOUND ;
END LOOP;
END
;

Related

ORACLE SQL function to return JSON response

I have table like below. I am trying to pull all BRKPK_CNTNR_ID, SSP_Q with respect to each OVRPK_CNTNR_ID
enter image description here
Below query runs fine and produces desired result.
DECLARE
json_objects JSON_OBJECT_LIST := JSON_OBJECT_LIST() ;
counter NUMBER := 1;
BEGIN
FOR ovrpk_detail IN (SELECT OVRPK_CNTNR_ID ,BRKPK_CNTNR_ID,ITEM_DISTB_Q FROM OVRPK_DET od WHERE od.OVRPK_CNTNR_ID='92000000356873110552') LOOP
begin
json_objects.extend;
json_objects(counter) :=JSON_UTIL.JSON_OBJECT(
JSON_ATTRIBUTE_LIST(
JSON_UTIL.JSON_ATTRIBUTE('over_pack_container_id',ovrpk_detail.OVRPK_CNTNR_ID),
JSON_UTIL.JSON_ATTRIBUTE('break_pack_container_id',ovrpk_detail.BRKPK_CNTNR_ID),
JSON_UTIL.JSON_ATTRIBUTE('item_distributed_quantity',ovrpk_detail.ITEM_DISTB_Q)
));
counter := counter+1;
END;
END LOOP;
dbms_output.put_line( JSON_UTIL.JSON_ARRAY(json_objects));
END;
I am expecting is to create function that takes OVRPK_CNTNR_ID as input and returns JSON_OBJECT_LIST as output.
Here is my query I tried.
CREATE OR REPLACE FUNCTION get_json_objects(ovrk_cntnr_id IN CHAR)
RETURN JSON_OBJECT_LIST IS
json_objects JSON_OBJECT_LIST := JSON_OBJECT_LIST();
BEGIN
DECLARE counter NUMBER := 1;
FOR ovrpk_detail IN (SELECT OVRPK_CNTNR_ID ,BRKPK_CNTNR_ID FROM OVRPK_DET od WHERE od.OVRPK_CNTNR_ID= ovrk_cntnr_id) LOOP
begin
json_objects.extend;
json_objects(counter) :=JSON_UTIL.JSON_OBJECT(
JSON_ATTRIBUTE_LIST(
JSON_UTIL.JSON_ATTRIBUTE('over_pack_container_id',ovrpk_detail.OVRPK_CNTNR_ID),
JSON_UTIL.JSON_ATTRIBUTE('break_pack_container_id',ovrpk_detail.BRKPK_CNTNR_ID)
));
counter := counter+1;
END;
END LOOP;
RETURN json_objects;
END;
DECLARE
json_return_object JSON_OBJECT_LIST;
ovrk_cnt_i char := '92000000356873110552'
BEGIN
json_return_object := get_json_objects(ovrk_cnt_i);
dbms_output.put_line('JSON Object is: ' || json_return_object);
END;
I am missing something, would anyone help me to resolve this issue? Thank you
Note: I am using Oracle 12.c database which does not support JSON however, JSON_UTIL is library written internally to address same
We do not have your packages or functions so this is difficult to test.
Your code has a DECLARE without a corresponding BEGIN or END. If you get rid of all the unnecessary DECLARE/BEGIN/END statements then you can simplify it to:
CREATE OR REPLACE FUNCTION get_json_objects(
ovrk_cntnr_id IN CHAR
)
RETURN JSON_OBJECT_LIST
IS
json_objects JSON_OBJECT_LIST := JSON_OBJECT_LIST();
counter NUMBER := 1;
BEGIN
FOR ovrpk_detail IN (
SELECT OVRPK_CNTNR_ID,
BRKPK_CNTNR_ID
FROM OVRPK_DET
WHERE OVRPK_CNTNR_ID = ovrk_cntnr_id
) LOOP
json_objects.extend;
json_objects(counter) :=JSON_UTIL.JSON_OBJECT(
JSON_ATTRIBUTE_LIST(
JSON_UTIL.JSON_ATTRIBUTE('over_pack_container_id',ovrpk_detail.OVRPK_CNTNR_ID),
JSON_UTIL.JSON_ATTRIBUTE('break_pack_container_id',ovrpk_detail.BRKPK_CNTNR_ID)
)
);
counter := counter+1;
END LOOP;
RETURN json_objects;
END;
/
If your package functions are callable from SQL then you should be able to simplify it further to:
CREATE OR REPLACE FUNCTION get_json_objects(
i_ovrpk_cntnr_id IN OVRPK_DET.OVRPK_CNTNR_ID%TYPE
) RETURN JSON_OBJECT_LIST
IS
json_objects JSON_OBJECT_LIST;
BEGIN
SELECT JSON_UTIL.JSON_OBJECT(
JSON_ATTRIBUTE_LIST(
JSON_UTIL.JSON_ATTRIBUTE('over_pack_container_id', OVRPK_CNTNR_ID),
JSON_UTIL.JSON_ATTRIBUTE('break_pack_container_id', BRKPK_CNTNR_ID)
)
)
BULK COLLECT INTO json_objects
FROM OVRPK_DET
WHERE OVRPK_CNTNR_ID = i_ovrpk_cntnr_id;
RETURN json_objects;
END;
/
DECLARE
json_return_object JSON_OBJECT_LIST;
ovrk_cnt_i OVRPK_DET.OVRPK_CNTNR_ID%TYPE := '92000000356873110552'
BEGIN
json_return_object := get_json_objects(ovrk_cnt_i);
dbms_output.put_line('JSON Object is: ' || json_return_object);
END;
/

I keep getting this error: ORA-06502: PL/SQL: numeric or value error: character to number conversion error

I keep getting this error: ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 44
ORA-06512: at "SYS.DBMS_SQL", line 1721
Please what am I doing wrong here, because I have checked and it seems the code is fine. I declare a boolean payment_status, and reassigned a value 'tru' to it
DECLARE
pyld_id number(30);
p_amt number(30);
p_user_id number(30);
p_reference varchar2(100);
p_name varchar2(100);
p_narration varchar2(100);
p_payment_date DATE default sysdate;
p_net_amt NUMBER default null;
p_payment_type_id number(30);
p_transaction_type_id number(30);
p_payment_id number(30);
payment_status boolean ;
done boolean ;
p_order number(30);
p_id number(30);
p_parent_id number(30);
p_order_id number(30);
BEGIN
p_payment_id := 1;
done := TRUE;
p_reference := 'vartesr56';
p_payment_id := 45;
p_user_id := 19411 ;
p_amt := 2000;
p_net_amt := 2000;
p_name := 'Test';
p_narration := 'Product Activation';
p_transaction_type_id := 1;
p_payment_type_id := 1;
payment_status := TRUE;
PSTK_PAYMENT_PACKAGE.add_payment(p_amt, p_user_id, p_reference, p_name, p_narration, p_payment_date, p_net_amt, p_payment_type_id, p_transaction_type_id, p_payment_id, payment_status);
if payment_status != TRUE then
---do somethinh
DBMS_OUTPUT.ENABLE ('NULL');
p_id := 19411;
p_order_id := 3080514;
p_user_id := 19411;
-- get the parent id of the user
DBMS_OUTPUT.PUT_LINE('wants to assign');
select draft_parent_user_id into p_parent_id from users where id = p_id fetch first 1 rows only;
-- set the parent id of the user
update users set PARENT_USER_ID = p_parent_id where id = p_id;
DBMS_OUTPUT.PUT_LINE('updated');
order_pkg.confirm_order(p_order_id=>p_id,p_user_id=>p_user_id ,p_done => done);
if done = true then
---Do something
DBMS_OUTPUT.PUT_LINE('NULL');
else
-- apex_error.add_error (
--p_message => 'Unable to activate package. Please contact admin',
-- p_display_location => apex_error.c_inline_in_notification );
DBMS_OUTPUT.PUT_LINE('NULL');
end if;
END IF;
END;

How convert long text to UTF8 in Oracle 11g?

I have large text around 20000 I need convert to UTF8.
select convert('large text','UTF8') from dual
I get error:
ORA-01704: string literal too long
Try this get another error
declare
v_hugetxt varchar2(32767);
begin
v_hugetxt:='huge text'
select convert(v_hugetxt,'UTF8') into v_hugetxt
from dual;
end;
I get error
ORA-01460: unimplemented or unreasonable conversion requested tips ยท
Incompatible character sets can cause an ORA-01460
I found solution but worked
FYI
create or replace function v_blobtoclob(v_blob_in in blob) return clob is
v_file_clob clob;
v_file_size integer := dbms_lob.lobmaxsize;
v_dest_offset integer := 1;
v_src_offset integer := 1;
v_blob_csid number := dbms_lob.default_csid;
v_lang_context number := dbms_lob.default_lang_ctx;
v_warning integer;
v_length number;
begin
dbms_lob.createtemporary(v_file_clob, true);
dbms_lob.converttoclob(v_file_clob,
v_blob_in,
v_file_size,
v_dest_offset,
v_src_offset,
v_blob_csid,
v_lang_context,
v_warning);
return v_file_clob;
exception
when others then
dbms_output.put_line('Error found');
end;
CREATE OR REPLACE FUNCTION clob_to_blob (p_clob CLOB, p_charsetname VARCHAR2)
RETURN BLOB
AS
l_lang_ctx INTEGER := DBMS_LOB.default_lang_ctx;
l_warning INTEGER;
l_dest_offset NUMBER := 1;
l_src_offset NUMBER := 1;
l_return BLOB;
BEGIN
DBMS_LOB.createtemporary (l_return, FALSE);
DBMS_LOB.converttoblob (
l_return,
p_clob,
DBMS_LOB.lobmaxsize,
l_dest_offset,
l_src_offset,
CASE WHEN p_charsetname IS NOT NULL THEN NLS_CHARSET_ID (p_charsetname) ELSE DBMS_LOB.default_csid END,
l_lang_ctx,
l_warning);
RETURN l_return;
END;
declare
v_hugetxt clob;
v_hugetxt2 blob;
v_hugetxt3 clob;
offset int;
clobsize number;
begin
v_hugetxt:='huge text';
select bnf.clob_to_blob(v_hugetxt,'UTF8') into v_hugetxt2 from dual;
select bnf.v_blobtoclob(v_hugetxt2) into v_hugetxt3 from dual;
dbms_output.put_line('Print CLOB');
offset:=1;
loop
exit when offset > dbms_lob.getlength(v_hugetxt3);
dbms_output.put_line(dbms_lob.substr(v_hugetxt3, 255, offset));
offset := offset + 255;
end loop;
DBMS_OUTPUT.PUT_LINE('clob out = ' || v_hugetxt3);
end;
That's it

A functions code is written in oracle. I am unable to understand what it is doing

Here is the function:
create or replace FUNCTION FUNC_PART(
p_TEXT varchar2,
p_COLUMN number,
p_SEPARATOR varchar2
) RETURN varchar2 AS
v_POS_ number;
v_POS2 number;
V_COLUMN NUMBER;
BEGIN
V_COLUMN:=p_COLUMN;
v_POS_ := 1;
v_POS2 := INSTR(p_TEXT, p_SEPARATOR, v_POS_);
WHILE (V_COLUMN >1 AND v_POS2> 0) LOOP
v_POS_ := v_POS2 + 1;
v_POS2 := INSTR(p_TEXT, p_SEPARATOR, v_POS_);
V_COLUMN :=V_COLUMN - 1;
END LOOP;
IF V_COLUMN > 1 THEN
v_POS_ := LENGTH(RTRIM(p_TEXT)) + 1;
END IF;
IF v_POS2 = 0 THEN
v_POS2 := LENGTH(RTRIM(p_TEXT)) + 1;
END IF;
RETURN SUBSTR (p_TEXT, v_POS_, v_POS2 - v_POS_);
END;
I did not understand this code carefully, but from the results, the following two pieces of code have the same meaning.
select FUNC_PART('asdfghsdfg', 3, 's')
from dual;
select regexp_substr('asdfghsdfg', '[^s]+', 1, 3)
from dual;
This is likely to be migrated from other databases to the code in oracle, because oracle does not need such a custom function

The error 'Grid index out of range' when switching between the databases

This is the procedure to display the Customer Database:
procedure TfrmMain.mnuCustomerClick(Sender: TObject);
var
j: integer;
begin
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
con.LoginPrompt := False;
con.Open('DriverID=SQLite;Database=C:\Users\katiee\Documents\Embarcadero\Studio\Projects\ProgramDatabase;');
query.Connection := con;
query.sql.Text := 'SELECT * FROM CustDatabase ORDER BY ID';
query.Open();
query.First;
sgdDatabases.colCount := 9;
sgdDatabases.FixedCols := 0;
for j := 0 to sgdDatabases.rowCount do
sgdDatabases.ColWidths[j] := 100;
sgdDatabases.Cells[0, 0] := 'ID';
sgdDatabases.Cells[1, 0] := 'First Name';
sgdDatabases.Cells[2, 0] := 'Last Name';
sgdDatabases.Cells[3, 0] := 'Address';
sgdDatabases.Cells[4, 0] := 'Town';
sgdDatabases.Cells[5, 0] := 'County';
sgdDatabases.Cells[6, 0] := 'Postcode';
sgdDatabases.Cells[7, 0] := 'Telephone No.';
sgdDatabases.Cells[8, 0] := 'E-Mail';
row := 1;
while not query.EOF do
begin
ID := query.FieldByName('ID').AsString;
firstname := query.FieldByName('First Name').AsString;
lastname := query.FieldByName('Last Name').AsString;
address := query.FieldByName('Address').AsString;
town := query.FieldByName('Town').AsString;
county := query.FieldByName('County').AsString;
postcode := query.FieldByName('Postcode').AsString;
telno := query.FieldByName('TelNo').AsString;
email := query.FieldByName('Email').AsString;
sgdDatabases.Cells[0, row] := ID;
sgdDatabases.Cells[1, row] := firstname;
sgdDatabases.Cells[2, row] := lastname;
sgdDatabases.Cells[3, row] := address;
sgdDatabases.Cells[4, row] := town;
sgdDatabases.Cells[5, row] := county;
sgdDatabases.Cells[6, row] := postcode;
sgdDatabases.Cells[7, row] := telno;
sgdDatabases.Cells[8, row] := email;
sgdDatabases.RowCount := sgdDatabases.RowCount + 1;
row := row + 1;
query.Next;
end;
end;
This is the procedure to display the Employee Database, which is basically identical except "SELECT * FROM EmplDatabase":
procedure TfrmMain.mnuEmployeeClick(Sender: TObject);
var
i: integer;
begin
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
con.LoginPrompt := False;
con.Open('DriverID=SQLite;Database=C:\Users\kasio\Documents\Embarcadero\Studio\Projects\ProgramDatabase;');
query.Connection := con;
query.sql.Text := 'SELECT * FROM EmplDatabase ORDER BY ID';
query.Open();
query.First;
sgdDatabases.colCount := 9;
sgdDatabases.FixedCols := 0;
for i := 0 to sgdDatabases.RowCount do
sgdDatabases.ColWidths[i] := 100;
sgdDatabases.Cells[0, 0] := 'ID';
sgdDatabases.Cells[1, 0] := 'First Name';
sgdDatabases.Cells[2, 0] := 'Last Name';
sgdDatabases.Cells[3, 0] := 'Address';
sgdDatabases.Cells[4, 0] := 'Town';
sgdDatabases.Cells[5, 0] := 'County';
sgdDatabases.Cells[6, 0] := 'Postcode';
sgdDatabases.Cells[7, 0] := 'Telephone No.';
sgdDatabases.Cells[8, 0] := 'E-Mail';
row := 1;
while not query.EOF do
begin
ID := query.FieldByName('ID').AsString;
firstname := query.FieldByName('First Name').AsString;
lastname := query.FieldByName('Last Name').AsString;
address := query.FieldByName('Address').AsString;
town := query.FieldByName('Town').AsString;
county := query.FieldByName('County').AsString;
postcode := query.FieldByName('Postcode').AsString;
telno := query.FieldByName('TelNo').AsString;
email := query.FieldByName('Email').AsString;
sgdDatabases.Cells[0, row] := ID;
sgdDatabases.Cells[1, row] := firstname;
sgdDatabases.Cells[2, row] := lastname;
sgdDatabases.Cells[3, row] := address;
sgdDatabases.Cells[4, row] := town;
sgdDatabases.Cells[5, row] := county;
sgdDatabases.Cells[6, row] := postcode;
sgdDatabases.Cells[7, row] := telno;
sgdDatabases.Cells[8, row] := email;
sgdDatabases.RowCount := sgdDatabases.RowCount + 1;
row := row + 1;
query.Next;
end;
end;
When I run the program, I can open either of the databases on the first click, but then if I click again on either of the Customer or Employee buttons or try to change the database, the following error shows: "Project ProjectQuote.exe raised exception class EInvalidGridOperation with message 'Grid index out of range'".
If I delete the line
sgdDatabases.RowCount := sgdDatabases.RowCount + 1;
from the code, it displays both databases, but only shows the first four rows from the database even if there's more.
(I am aware of the uselessly repeated code and no I can't use anything else other than TStringGrid)
This line of your code looks wrong to me:
for j := 0 to sgdDatabases.rowCount do
sgdDatabases.ColWidths[j] := 100;
The [Index] of a StringGrid's ColWidths property is a column number, not a row number, so sgdDatabases.rowCount should have nothing to do with it. If, at the time the above code executes, the number of rows in the grid is greater than the number of columns, you will get an "Index out of range" error when the value of j reaches a value which represents an invalid column number.
In any case, even if that code were valid in that respect , there is an "off by one" error involving sgdDatabases.rowCount. The row numbers are zero-based, so it should be sgdDatabases.rowCount - 1 (assuming you were attempting to refer to a particular row by index, of course).
A more general point is that you can single-step through your code using the IDE's debugger; if you do that, you will see the exception occur when one particular line is executed, and that's the place to start looking for the cause. You should always include the location of the exception in your SO question, because readers should not have to guess this.
Usually, the IDE debugger will find the exception even if you don't single-step, as long as you go to
Tools | Debugger Options | Embarcadero Debuggers | Language Exceptions
in the IDE and check the checkbox Notify on Language Exceptions.
Btw, it would be better if you wrote a general-purpose routine to populate a StringGrid from a Dataset, maybe along the following lines:
procedure TForm1.DatasetToGrid(Dataset : TDataset; Grid : TStringGrid);
var
Col,
Row : Integer;
begin
Grid.RowCount := 1;
Row := 0;
// The following gives the column headers the names of the
// Dataset fields.
for Col := 0 to Dataset.FieldCount - 1 do
Grid.Cells[Col, Row] := Dataset.Fields[Col].FieldName;
Inc(Row);
Dataset.First;
while not Dataset.Eof do begin
for Col := 0 to Dataset.FieldCount - 1 do begin
// Oops! we don't need this Row := Grid.RowCount;
Grid.Cells[Col, Row] := DataSet.Fields[Col].AsString;;
end;
Dataset.Next;
Grid.RowCount := Grid.RowCount + 1;
Inc(Row);
end;
end;
One of the benefits of doing it that way is that all your mistakes are in one place, not duplicated in duplicated code, so if you fix them once, you're done.