How to implement a trie data structure in Pascal? - dictionary

I would like to make a try in Pascal. I started is but I the insert method does not work correctly. Here is a part of the program:
type PVrchol = ^Vrchol;
Vrchol = record
endOfTheWord:boolean;
deti:array['a'..'z'] of PVrchol;
end;
var trie:PVrchol;
FUNCTION getChildnode(current:PVrchol;k:char):PVrchol;
Begin
if current^.deti[k]<>nil then
getChildnode:=current^.deti[k]
else
getChildNode:=nil;
End;
PROCEDURE insertWord(word:string;var trie:PVrchol);
var i:integer;
current, childNode:PVrchol;
Begin
current:=trie;
childNode:=current;
for i:=1 to Length(word) do begin
childNode:=getChildnode(current,word[i]);
if childNode=nil then begin
new(childNode);
current^.deti[word[i]]:=childNode;
end;
current:=childNode;
end;
current^.endOfTheWord:=true;
End;
BEGIN
new(trie);
//there are some methods reading the words from input, and calling the insertWord procedure.
END.
The insertWord procedure always gets a word, so the parameter wont be "".

The problem might be that records are not automatically zeroed as classes are? Try adding
fillchar(childnode,sizeof(childnode),#0);
after the new(childnode)

Related

PL/SQL if then else statements not running

I have written following code in oracle pl/sql
create or replace procedure sorting_criteria(criteria in varchar)
as
begin
if(criteria='lowest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount ASC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else if(criteria='highest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount DESC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end;
/
But it is giving following error: Error at line 35: PLS-00103: Encountered the symbol ";" when expecting one of the following: Please help
The ELSE-IF statement in PL/SQL has to be written as ELSIF. Otherwise, you should close the second IF with an other END IF; statement.
You can solve the issue by changing the ELSE IF at line 17 to an ELSIF
The answer by #GregorioPalamà correctly addresses your issues. But you can drastically reduce the workload by changing your thinking away from "If...then...else" to the "set of" and letting SQL do the work. In this case the only difference is sorting either ascending or descending on amount. The same effect can be achieved by sorting ascending on amount or minus amount; and SQL can make that decision. So you can reduce the procedure to validating the parameter and a single cursor for loop:
create or replace procedure sorting_criteria(criteria in varchar2)
as
cursor ptr(c_sort_criteria varchar2) is
select p_name
from product
order by case when c_sort_criteria = 'lowest price'
then amount
else -amount
end ;
begin
if criteria in ('lowest price', 'highest price')
then
for rec in ptr(criteria)
loop
dbms_output.put_line('Product: ' || rec.p_name );
end loop;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end sorting_criteria;
/
See demo here. For demonstration purposed I added the amount to the dbms_output.
A couple notes:
While it is not incorrect using p_... as a column name, it is also
not a good idea. A very common convention (perhaps almost a
standard) to use p_... to indicate parameters. This easily leads to
confusion; confusion amongst developers is a bad thing.
IMHO it is a bug to name a local variable the same as a table
column name. While the compiler has scoping rules which one to use
it again leads to confusion. The statement "where table.name = name"
is always true, except when at least one of them is null, which possible could lead to updating/deleting every row in your table. In this
case p_name is both a column and a local variable.

I am unable to create a plsql function inside a while loop.Is it possible to write plsql function inside while loop?

create or replace FUNCTION ACHEHBBDA40(p_sum_date IN VARCHAR2) RETURN NUMBER AS
CURSOR BISNES_T_INFO IS
SELECT a.TARGETID, a.CLIENTID,b.BSNSID
FROM CLIENT_XREF_T a
INNER JOIN BISNES_T b
ON
a.CLIENTID = b.CLIENTID AND
a.TYPE = '1' AND
b.WORKKBN = '0';
BEGIN
DBMS_OUTPUT.PUT_LINE('PGM_NAME');
LOOP
BEGIN
FETCH BISNES_T_INFO
INTO l_compid,l_bsnsid,l_clientid;
--beginning of nested function in declaration section
FUNCTION getClientXref(l_clientid VARCHAR2) RETURN Number AS
CURSOR CLIENT_XREF_T_INFO IS
SELECT d.BRNO, d.TRNO
FROM CLIENT_XREF_T x INNER JOIN CLIENT_T c ON x.CLIENTID = c.CLIENTID;
BEGIN
OPEN CLIENT_XREF_T_INFO;
FETCH CLIENT_XREF_T_INFO
INTO l_brno,l_trno
END;
return 1;
END getClientXref;
UTL_FILE.PUT_LINE(v_filehandle,l_compid ||CHR(9)|| l_brno ||CHR(9)|| l_trno);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Close the file after the process is over
DBMS_OUTPUT.PUT_LINE('MSGID_ERREND : ' ||commonUtilities.GC_MSGID_ERREND);
DBMS_OUTPUT.PUT_LINE('MSG_ERREND : ' ||commonUtilities.GC_MSG_ERREND);
DBMS_OUTPUT.PUT_LINE('MSGID_NO_DATA : ' ||commonUtilities.GC_MSGID_NO_DATA);
DBMS_OUTPUT.PUT_LINE('MSG_NO_DATA : ' ||commonUtilities.GC_MSG_NO_DATA);
RETURN lc_failure;
EXIT;
END;
END LOOP;
When i create the nested function getClientXref inside the while loop its throws the compile time error "Error(213,21): PLS-00103: Encountered the symbol "GETCLIENTXREF" when expecting one of the following: := . ( # % ; " in sql developer.
Honestly I don't understand the purpose of the code you posted: it never calls the function you are trying to declare... But, since your question is about syntax, I have to correct what all other posters and commentators are saying:
You CAN declare procedures and functions inside a loop
The following code works!
begin
for c in (select * from dict) loop
DECLARE
-- procedure inside a loop
procedure local_print_current_row is
begin
-- here I am even accessing the external
-- "c" for loop variable
dbms_output.put_line(c.table_name || ' -> ' || c.comments);
end;
BEGIN
local_print_current_row;
END;
end loop;
end;
of course this is just a "toy" example to illustrate the syntax, but PL/SQL allows you to nest declarations (not only of variables) almost everywhere using the declare/begin/exception/end construct.
in your code you wrote the comment "--beginning of nested function in declaration section ", but you didn't actually define any nested declaration section. you need the DECLARE keyword.
A lot of people do not realize that in PL/SQL "begin/end" is not simply the same of "{"/"}" in java. The complete syntax of the begin/end block allows all these parts:
DECLARE
<declarations>
BEGIN
<code>
EXCEPTION
<exception handlers>
END
It is just optional to write the "DECLARE" and "EXCEPTION" sections, but the BEGIN/END block is actually made of all the above parts.
in any DECLARE section you can declare:
function and procedures
types
cursors
variables
exceptions
...
and all the things you declare in that section will be visible only within the corresponding begin[/exception]/end sections.
Moreover you can nest other blocks wherever you can write actual "runnable" code. This kind of nesting can be done:
declare
...
begin
declare
procedure MyLocalProc is
procedure NestedProc is
begin
end;
begin
....
declare
...
begin
...
exception
..
end
...
exception
end
begin
...
end
exception when others then
declare
...
begin
...
end
end
P.S.:Note that after a "procedure is" or "function ... is" the "DECLARE" section is implicit: this is why you can start declaring stuff immediately after a procedure/function declaration without writing "declare". This does not happen for triggers, where you actually have to write "declare" if you want to add local declarations.
I don't really get it why you have to create a function inside your while loop. Functions were not really meant for that kind of approach. You can just create your function separately and call it each time you needed inside your while loop and pass your parameters.

Oracle PL/SQL - ORA-01403 “No data found” when using “SELECT INTO”

I have a pl sql code that execute three queries sequentially to determine a match level and do some logic
The issue is - when first query has no results (completely valid scenario) I get ORA-01403 No data found.
I understand that I need to incorporate [ Exception clause when NO_DATA_FOUND ]- but how to add it and continue to the next query?
PL/SQL Code
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =2
if A is null then
do some logic 2;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =3
if A is null then
do some logic 3;
end if
END PL/SQL Code
Declare
--your declarations
begin
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
Begin
MATCH_LEVEL =1;
if A is null then
do some logic;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('Error...');
END;
--- and son on for other blocks
end;
Just surround your SELECT INTO with begin-end;
begin
-- your faulty statement here
Exception
When NO_DATA_FOUND Then
-- Do what you want or nothing
WHEN TOO_MANY_ROWS THEN
-- what if you get more then one row? and need specific handler for this
When OTHERS Then
-- do something here or nothing (optional - may happen if you have more than your SELECT INTO between 'begin' and 'Exception')
end;
This is like try block of PL/Sql
With this technique you can log the reason your statement failed.
For a SELECT ... INTO ... statement, the PL/SQL engine assume there will be one, and only one row returned by your query. If there is no row, or more than one, an exception is raised.
FWIW, you can handle such cases without resorting on exception handling by using aggregate functions. That way, there will always be only one row in the result set.
Assuming A can't be NULL in your rows:
SELECT MAX(A) into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- A would be NULL if there was *no* row. Otherwise, it is *the* value for *the* row
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
If the NULL value is a possible case, just add an extra COUNT(*) column:
SELECT MAX(A), COUNT(*) into A, HAS_FOUND_ROW FROM SAMPLE WHERE SOME CONDITION;
if HAS_FOUND_ROW > 0 then
...
end if;
Oracle will not allow you to open an implicit cursor (i.e. a select statement in the body of a code block) that returns no rows. You have two options here (3 really, counting #Sylvain's answer, but that is an unusual approach): use an explicit cursor or handle the error.
Explicit Cursor
An explicit cursor is one found in the DECLARE section it must be opened and fetched manually (or in a FOR loop). This has the added advantage that, if you parameterize the query properly, you can write it once and use it multiple times.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
cursor cur_params (some_column_value number) is
SELECT A FROM SAMPLE WHERE some_column = some_column_value;
BEGIN
MATCH_LEVEL := 1;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
end;
Handle the error
If you choose to handle the error, you'll need to create a BEGIN...END block around the code that is going to throw the error. When disregarding an error, it's crucial that you ensure that you are only disregarding the specific error you want avoid, when generated from the specific statement you expect it from. If you simply add the EXCEPTION section to your existing BEGIN...END block, for instance, you couldn't know which statement generated it, or even if it was really the error you expected.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
BEGIN
MATCH_LEVEL := 1;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
end;
While I'd discourage it, you can catch any other errors in the same exception blocks. However, by definition, those errors would be unexpected, so it would be a poor practice to discard them (you'll never know they even happened!). Generally speaking, if you use a WHEN OTHERS clause in your exception handling, that clause should always conclude with RAISE;, so that the error gets passed up to the next level and is not lost.

Return ref_cursor from for loop of cursor object

I am trying to get a ref_cursor to be assigned to a variable inside a for loop then returned at the end of a function. The loop in going through a local cursor if it gets more than 1 result.
I have noted where the error occurs in the code. I am not sure how to create a loop where i can get a ref_cursor for the current point in the loop, assign it to a variable and then return it to the function. Could someone someone help me figure out how to do that? Below is my i-th attempt at logic based of reading around the google searches.
The error is "PLS-00382: expression is of wrong type" and i know that i obviously am not assigning the correct variably type based on this error but the code below with the error is an illustration of what I want to do and what I need help accomplishing.
FUNCTION GET_PARCEL(p_lat in number, p_long in number) return sys_refcursor
IS
v_distance number(10) := 100000000;
v_shortest_dist number(10) := v_distance;
v_centroid SDO_GEOMETRY;
v_rc_ref_cursor sys_refcursor;
v_ref_geom SDO_GEOMETRY := mdsys.SDO_GEOMETRY(2001, 8311, NULL, SDO_ELEM_INFO_ARRAY(1, 1, 1), SDO_ORDINATE_ARRAY(120.3214, -10.7088));
cursor query_cursor is select * from PARCEL_TABLE where code = 20134;
BEGIN
for query_row in query_cursor loop
v_centroid := SDO_GEOM.SDO_CENTROID(query_row.geometry, 0.05);
IF (v_centroid is not null) then
v_distance := SDO_GEOM.SDO_DISTANCE(v_centroid, v_ref_geom, 0.05);
IF v_distance < v_shortest_dist THEN
v_shortest_dist := v_distance;
v_rc_ref_cursor := query_row; -- Error on this line
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE('Centroid is not initialised for some reason.');
END IF;
end loop;
return v_rc_ref_cursor;
END;
As far as I know, you cannot build up a cursor. Cursors are created and maintained by the database with connections to their source data, transaction and session context, and the like.
I would suggest you declare a type, instantiate it, build up the values in the type.
When you are done, create a cursor by selecting * from table (cast (variable as your_type)).
Munge the cursor into a ref_cursor and return that.
Pro tip is to remember you have data in your table and you can use it for logic. Turns out if I use the IDENTIFIER or ROWID of the row/s which i want then i can do a where clause into a ref cursor that looks up by IDENTIFIER or ROWID.
eg.
open v_rc_ref_cursor for select * from PARCEL_TABLE n where n.identifier = query_row.identifier;
super simple stuff :)

how would I enter this function into toad to run?

Here is the code
vResult VARCHAR2(200);
BEGIN
fvData:=TRIM(fvData);
IF SUBSTR(fvData,LENGTH(fvData)-1,1)<>'*' THEN
fvData:=fvData||'**';
END IF;
vResult:=SUBSTR(fvData,1,InStr(fvData,'^^') - 1);
fvData:=SUBSTR(fvData,InStr(fvData,'^^') + 3);
RETURN vResult;
END StringExtract;
/
I have tried entering different ways, with fvdata=365 but nothing happens, I have try entering small SQL like
select InStr(367,'^^^^') - 1
from dual;
but I can't figure out how to do this big function.
In an attempt to help you get this working take a look at the following code. I have left out the detail of your function so we don't complicate things but you should be able to just plug that back in.
DECLARE
inputValue VARCHAR2(200) := :inputValue; -- << this bind variable allows you to provide different input each you run the code
FUNCTION stringExtract(fvData varchar2) RETURN varchar2
IS
vResult VARCHAR2(200);
BEGIN
vResult := TRIM(fvData);
RETURN vResult;
END stringExtract;
BEGIN
DBMS_OUTPUT.PUT_LINE(LENGTH(inputValue));
DBMS_OUTPUT.PUT_LINE(LENGTH(stringExtract(inputValue)));
END;
Hopefully you can take that as a template and adapt to get your program running.

Resources