I'm learning about Oracle Apex, Oracle SQL and PL/SQL and I'm currently learning about functions. I have written a function in which a table is created, data is stored and the table is returned to the calling query. The problem is the code won't compile. I can't see where the problem may be and I've compared the code to various online resources including this site. Everything looks fine to me, so the answer is not obvious to me.
This is the code I wrote:
CREATE OR REPLACE TYPE t_table AS OBJECT
(
futureValues NUMBER
);
CREATE OR REPLACE TYPE t_futureValues AS TABLE OF t_table;
/
CREATE OR REPLACE FUNCTION "CALCULATE_VALUE"( lastRowMinus0 IN NUMBER DEFAULT 1,
lastRowMinus1 IN NUMBER DEFAULT 2,
lastRowMinus2 IN NUMBER DEFAULT 3,
lastRowMinus3 IN NUMBER DEFAULT 4,
lastRowMinus4 IN NUMBER DEFAULT 5,
lastRowMinus5 IN NUMBER DEFAULT 6,
lastRowMinus6 IN NUMBER DEFAULT 7 )
RETURN t_futureValues AS
tableObject t_futureValues;
predictedValue NUMBER := 0;
lastRowMinus0Value NUMBER := 0;
lastRowMinus1Value NUMBER := 0;
lastRowMinus2Value NUMBER := 0;
lastRowMinus3Value NUMBER := 0;
lastRowMinus4Value NUMBER := 0;
lastRowMinus5Value NUMBER := 0;
lastRowMinus6Value NUMBER := 0;
avgDiff NUMBER := 0;
BEGIN
tableObject := t_futureValues();
lastRowMinus0Value := 3;
lastRowMinus1Value := 6;
lastRowMinus2Value := 9;
lastRowMinus3Value := 12;
lastRowMinus4Value := 14;
lastRowMinus5Value := 20;
lastRowMinus6Value := 60;
avgDiff := (lastRowMinus5Value - lastRowMinus6Value) + avgDiff;
avgDiff := (lastRowMinus4Value - lastRowMinus5Value) + avgDiff;
avgDiff := (lastRowMinus3Value - lastRowMinus4Value) + avgDiff;
avgDiff := (lastRowMinus2Value - lastRowMinus3Value) + avgDiff;
avgDiff := (lastRowMinus1Value - lastRowMinus2Value) + avgDiff;
avgDiff := (lastRowMinus0Value - lastRowMinus1Value) + avgDiff;
avgDiff := avgDiff / 6;
predictedValue := avgDiff + lastRowMinus0Value;
begin
for i in 2..13 loop
predictedValue := predictedValue + avgDiff;
IF predictedValue < 0 THEN
predictedValue := 0;
END IF;
insert into tableObject(futureValues)
values(predictedValue);
end loop;
end;
RETURN (tableObject);
END;
The error message I get is:
ORA-06545: PL/SQL: compilation error - compilation aborted ORA-06550: line 6, column 1: PLS-00103: Encountered the symbol "CREATE" ORA-06550: line 0, column 0: PLS-00565: T_TABLE must be completed as a potential REF target (object type)
I have no doubt that the problem is something simple, so if anyone knows, then I'd be grateful.
I don't work with types very often, but your insert is trying to insert into an array. A table must be the target of an insert.
Use the following code:
CREATE OR REPLACE TYPE brianl.t_table AS OBJECT
(
futureValues NUMBER
);
CREATE OR REPLACE TYPE brianl.t_futureValues AS TABLE OF t_table;
/
CREATE OR REPLACE FUNCTION brianl."CALCULATE_VALUE"( lastRowMinus0 IN NUMBER DEFAULT 1,
lastRowMinus1 IN NUMBER DEFAULT 2,
lastRowMinus2 IN NUMBER DEFAULT 3,
lastRowMinus3 IN NUMBER DEFAULT 4,
lastRowMinus4 IN NUMBER DEFAULT 5,
lastRowMinus5 IN NUMBER DEFAULT 6,
lastRowMinus6 IN NUMBER DEFAULT 7 )
RETURN t_futureValues AS
tableObject t_futureValues;
predictedValue NUMBER := 0;
lastRowMinus0Value NUMBER := 0;
lastRowMinus1Value NUMBER := 0;
lastRowMinus2Value NUMBER := 0;
lastRowMinus3Value NUMBER := 0;
lastRowMinus4Value NUMBER := 0;
lastRowMinus5Value NUMBER := 0;
lastRowMinus6Value NUMBER := 0;
avgDiff NUMBER := 0;
BEGIN
tableObject := t_futureValues();
lastRowMinus0Value := 3;
lastRowMinus1Value := 6;
lastRowMinus2Value := 9;
lastRowMinus3Value := 12;
lastRowMinus4Value := 14;
lastRowMinus5Value := 20;
lastRowMinus6Value := 60;
avgDiff := (lastRowMinus5Value - lastRowMinus6Value) + avgDiff;
avgDiff := (lastRowMinus4Value - lastRowMinus5Value) + avgDiff;
avgDiff := (lastRowMinus3Value - lastRowMinus4Value) + avgDiff;
avgDiff := (lastRowMinus2Value - lastRowMinus3Value) + avgDiff;
avgDiff := (lastRowMinus1Value - lastRowMinus2Value) + avgDiff;
avgDiff := (lastRowMinus0Value - lastRowMinus1Value) + avgDiff;
avgDiff := avgDiff / 6;
predictedValue := avgDiff + lastRowMinus0Value;
begin
for i in 2..13 loop
predictedValue := predictedValue + avgDiff;
IF predictedValue < 0 THEN
predictedValue := 0;
END IF;
tableobject.extend();
tableobject(tableobject.count).futureValues := predictedValue;
-- insert into tableObject(futureValues)
-- values(predictedValue);
end loop;
end;
RETURN (tableObject);
END;
Related
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
;
I want to sum that thing but only use the 'for loop' (no power functions). I've already created a loop that generates powers:
Program powers;
Var
i, n, result : integer;
writeln('enter N'), read(n);
BEGIN
Result := 1;
for i := 1 to n do
begin
Result := Result * n;
end;
writeln('result=',result);
END.
But I neither have any idea on how to make that code generate multiple powers ( this code only generates n^n) nor how to make a loop that sums them together.
It's best to break problems down into smaller problems. In this case, you may wish to have a pow function to handle the exponentiation for you.
Hopefully the pow function is acceptable if it's not a library function.
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
Then the main portion of your program is simpler.
program powers;
var
n, i, sum : integer;
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
begin
sum := 0;
write('enter N:');
readln(n);
for i := 0 to n do
sum := sum + pow(i, i);
writeln('result=', sum);
end.
Now that I finally understand what you're asking, please try this:
Program powers;
Var
i, j, n, p, result : integer;
BEGIN
write('enter N:');
readln(n);
Result := 0;
for i := 1 to n do
begin
p := 1;
for j := 1 to i do
p := p * i;
Result := Result + p;
end;
writeln('result=', result);
END.
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
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.
when add a lot of picture node to the teetree control,and set text offset to 30,then scroll to the end,the last line display picture only.
how should i do?
this is the result of the error:
procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
imgBox:TImageShape;
curLeft,curTop:Integer; //绘制完成文档视图后当前左上角坐标
begin
curLeft:=-100-15;
curTop:=0;
for i:=0 to 10 do
begin
imgBox := TImageShape.Create(Self);
imgBox.Width := 100;
imgBox.Height := 100;
curLeft := curLeft + 100 + 15 * 2;
if (curLeft + 100 + 15)>= Tree1.Width then
begin
curLeft := 15;
curTop := curTop + 100 + 15 * 2;
end;
imgBox.Left := curLeft;
imgBox.Top := curTop;
imgBox.Font.Style:=[fsBold];
imgBox.Font.Size:=16;
imgBox.Text.VertAlign:=vtaBottom;
imgBox.Text.VertOffset:=30;
imgBox.Text.Add('Node'+inttostr(i));
imgBox.ImageListIndex:=0;
imgBox.Tree:=Tree1;
end;
end;
I've added it to the public tracker here with a possible fix to be further investigated.