PLSQL - Check if an object exists in a table of objects - type has no map - oracle

I have a procedure that has got this variables of type table of objects:
lsa_final_filter_ports t_modifylink_multicolumnlist;
lsa_initial_filter_ports t_modifylink_multicolumnlist;
Definition of t_modifylink_multicolumnlist is:
CREATE OR REPLACE TYPE "T_MODIFYLINK_MULTICOLUMNLIST" IS TABLE OF o_modifylink_multicolumnlist;
TYPE o_modifylink_multicolumnlist AS OBJECT(some properties);
lsa_final_filter_ports variable is populated like this
SELECT b.name INTO ls_bandwidth_name FROM bandwidth b
WHERE b.bandwidthid = lna_compatible_port_bw(i);
SELECT CAST(MULTISET(SELECT *
FROM TABLE(piosa_all_ports)
WHERE ITEMNAME4 = ls_bandwidth_name)
AS t_modifylink_multicolumnlist)
INTO lsa_final_filter_ports
FROM dual;
Where piosa_all_ports is I/O parameter having same type t_modifylink_multicolumnlist
Second parameter is initialized at the beginig of procedure with a second I/O parameter that the procedure has
lsa_initial_filter_ports := piosa_filtered_ports;
What I want to achieve is to check if an object from lsa_final_filter_ports exists in lsa_initial_filter_ports and if so then skip adding that object in lsa_initial_filter_ports which will be an output parameter used by the outside calling procedure.
What I've tried is iterating through lsa_final_filter_ports objects and checking if that object already exists like this:
FOR i in 1..lsa_final_filter_ports.COUNT LOOP
IF lsa_final_filter_ports(i) MEMBER OF lsa_initial_filter_ports THEN
CONTINUE;
END IF;
lsa_initial_filter_ports.EXTEND();
lsa_initial_filter_ports(lsa_initial_filter_ports.COUNT) := lsa_final_filter_ports(i);
END LOOP;
But with this code I'm getting the following error:
Error: PLS-00801: internal error [*** ASSERT at file pdw4.c, line
2181; Type 0x0x7f991127aef8 has no MAP method.;
NR_WIZARDVALIDATIONS__CUSTOMISATIONS__B__166833[33]
I'm not sure if this kind of comparation can be made, maybe someone can clarify this.
Thank you

Unfortunately, if you have objects in PlSql, which only exist in RAM (that means those, that are NOT stored persistently in a table), assigning them to another variables always causes a (deep) copy of the origin object.
(which is really a problem, if you often assign objects with nested objects to working-variables and back to the collection ...)
Therefore, you´ll never will be able to compare two (copies) of the same object on object-pointer level.
Only thing you can do, is comparing some unique attributes of the objects.
Let´s say, you´ll have an ID in your piosa_all_ports, then you might modify your loop above with the comparison in something similar to this:
v_exists integer;
FOR i in 1..lsa_final_filter_ports.COUNT LOOP
select nvl(max(1),0)
from table(lsa_initial_filter_ports) x
where x.id = lsa_final_filter_ports(i).id;
if (v_exists = 0) then
lsa_initial_filter_ports.EXTEND();
lsa_initial_filter_ports(lsa_initial_filter_ports.COUNT) := lsa_final_filter_ports(i);
end if;
END LOOP;
(rem.: i know, that switching PlSql- and SQL-Context in sample above is time-consuming, but otherwise, you have to write a function that tests the existance of an ID by iterating the whole list)

Related

Delphi - dbGo/Oracle method GetFieldNames returns no data

Delphi Rio - I am just starting to learn ADO, specifically the dbGo components, connected to a local Oracle RDBMS (Oracle 12.2 64 bit). I am able to connect, issue simple queries, etc. I found the method TADOConnection.GetFieldNames, and I am experimenting with it. I am not able to get it to work. Here is my code...
procedure TForm1.BitBtn1Click(Sender: TObject);
var
S1 : TStringList;
begin
S1 := TStringList.Create;
ADO1.Connected := True;
ADO1.GetFieldNames('EGR.ACCOUNTS', S1);
//ADO1.GetTableNames(S1, False);
ShowMessage(IntToStr(S1.Count));
S1.Free;
end;
I have tried with and without the Schema name, yet S1.Count always returns 0. The GetTableNames function works fine. If I go into SQL*Plus and query, I see the appropriate data
select count(*) from EGR.ACCOUNTS;
So I know my SCHEMA.TABLENAME is correct. What am I doing wrong?
To get hand on a data field by its name, you shall use this code:
var
FieldEgrAccount : TField;
begin
FieldEgrAccount := AdoQuery1.FieldByName('SomeFieldName');
Memo1.Lines.Add(FieldEgrAccount.AsString);
end;
If you really need to have all field names, use this code:
var
Names : TStringList;
begin
Names := TStringList.Create;
try
AdoQuery1.GetFieldNames(Names);
// Do something with the field names
finally
Names.Free;
end;
end;
It is much faster to use one TField per field, get it once and reuse it as many times as needed (Make the variable fields of the form or datamodule class). FieldByName is relatively costly because it has to scan the list of field names.
You need to assign it to the items property of the stringlist which is of type TStrings.
procedure GetFieldNames(const TableName: string; List: TStrings);

reach parent method using Objects type inheritance

I have Object B child of Object A. In object B I would like to call a method from Object A.
It looks like in Oracle 11 we can use this :
(SELF AS parent_object).parent_method
But I work with Oracle 10 and this does not work. Is there another way ?
Thank you
PS : I want to do that in the CONSTRUCTOR METHOD (maybe it changes something)
You're right, this feature was introduced in 11.1, as mentioned in What's New in Object-Relational Features?.
The 10g manual also talks about this limitation. The best available workaround is to create a static supertype method:
When implementing methods using PL/SQL, you cannot call a supertype
object method with the super keyword or an equivalent method in
derived objects that have overriding methods. However, you can call a
static supertype method as a workaround. See the examples in "Creating
Subtypes With Overriding Methods" for the definition of the supertype
and subtype functions.
With extreme care (it means, that whatever you will do to prevent problems, in future you anyway will get a lot of pain in different parts of your body) you can cast child type to parent type and call its methods.
Extreme care is needed because, of course, you will lose any changes made on member fields by call to this method. So you can use such way only with base methods that does not change member fields of type.
create type type_1 as object (
field1 number(1)
,member procedure init
) not final;
/
create or replace type body type_1 as
member procedure init as
begin
field1 := 0;
dbms_output.put_line('init works! Field1 = ' || field1);
end;
end;
/
create type type_2
under type_1 (
field2 number(1)
,constructor function type_2(self in out type_2, arg number) return self as result
);
/
create or replace type body type_2 as
constructor function type_2(self in out type_2, arg number) return self as result as
parent type_1;
begin
self.field2 := arg;
self.field1 := -1;
select self
into parent
from dual;
parent.init();
dbms_output.put_line('Child''s field1 = ' || self.field1);
return;
end;
end;
/
declare
child type_2 := type_2(2);
begin
return;
end;
/

LINQ to SQL - Nesting Stored Procedures

I have two stored procedures. One (test_proc_outside) makes a call to a second one (test_proc_inside).
When I create a new LINQ to SQL .dbml file visually by dragging over my test_proc_outside stored proc, the generated class (test_proc_outsideResult) actually contains the modelling for the inner proc's (test_proc_inside) result set.
Here is the code for the outside stored proc (test_proc_outside):
CREATE PROCEDURE [dbo].[test_proc_outside]
#test1 int = 1,
#test2 int = 2,
#test3 int = 3
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- The result of this proc call gets modelled into DBML:
EXEC dbo.test_proc_inside #test1, #test2
-- This SELECT statement does NOT get modelled into DBML:
SELECT #test1 AS Test1,
#test2 AS Test2,
#test3 AS Test3
END
GO
Here is the code for the inside stored proc (test_proc_inside):
CREATE PROCEDURE [dbo].[test_proc_inside]
-- Add the parameters for the stored procedure here
#test1 int = 1,
#test2 int = 2
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- This is the result set that gets modelled by the DBML file:
SELECT #test1 AS Test1_Inside,
#test2 AS Test2_Inside
END
GO
It appears that the DBML generation looks for the very first result set in the stored procedure (or nested stored procedures) and spits out the model for that.
If I change the nested proc call to a function instead, I get my desired model (test_proc_outside).
Is there a configuration setting to tell the DBML file to generate the class for the result set on the outside proc (test_proc_outside) and not to bother with the inner results from test_proc_inside?
Thanks in advance,
Craig
I think it's possible to get the result you want with L2S but you also have to get all the other results as well. You can't do it with the designer, you'll have to map the sproc by extending the DataContext in a partial class with a method that returns an instance of the IMultipleResults interface. See this blog post:
http://blogs.msdn.com/b/dinesh.kulkarni/archive/2008/05/16/linq-to-sql-tips-7.aspx
Also, if the stored proc is returning result sets that don't map to entity in your L2S model, you'll need create special types to represent those results manually by defining them in code the same way L2S normally generates types for stored procedure results in its designer.cs.

How can I call an Oracle function from Delphi?

I created a function in Oracle that inserts records in specific tables and return an output according to what occurs within the function. e.g (ins_rec return number)
How do I call this function and see its output in Delphi?
I got a reply (with all my thanks) for sql plus but I need to know how can I do this in Delphi
Just pass the user defined function as column name in the query and it will work.
Example:
Var
RetValue: Integer;
begin
Query1.Clear;
Query1.Sql.Text := 'Select MyFunction(Param1) FunRetValue from dual';
Query1.Open;
if not Query1.Eof then
begin
RetValue := Query1.FieldByName('FunRetValue').AsInteger;
end;
end;
How to accomplish it may depend on what DB access library you use (BDE? dbExpress? ADO? others), some may offer a "stored procedure" component that may work with functions as well.
A general approach it to use an anonymous PL/SQL block to call the function (and a parameter to read the return value), PL/SQL resembles Pascal a lot...:
Qry.SQL.Clear;
Qry.SQL.Add('BEGIN');
Qry.SQL.Add(' :Rez := ins_rec;');
Qry.SQL.Add('END;');
// Set the parameter type here...
...
Qry.ExecSQL;
...
ReturnValue := Qry.ParamByName('Rez').Value;
I would not have used a function, though, but a stored procedure with an OUT value. Moreover, Oracle offers packages that are a very nice way to organize procedure and functions, and they also offer useful features like session variables and initialization/finalization sections... very much alike a Delphi unit.
we run an Oracle stored procedure using this code that utilizes the BDE (I know please don't bash because we used the BDE!)
Try
DMod.Database1.Connected:= False;
DMod.Database1.Connected:= True;
with DMod.StoredProc1 do
begin
Active:= False;
ParamByName('V_CASE_IN').AsString:= Trim(strCaseNum);
ParamByName('V_WARRANT_IN').AsString:= strWarrantNum;
ParamByName('V_METRO_COMMENTS').AsString:= strComment;
Prepare;
ExecProc;
Result:= ParamByName('Result').AsString;
end;
Except

Detecting the number of ORACLE rows updated by a OCI OCIStmtExecute call

I have an ORACLE update statement that I call using the OCIStmtExecute function call.
Using an OCI function call I would like to know how many rows have been updated by the action, e.g. zero, one or more.
How do I do this ?
Use the OCIAttrGet function call on your OCIStmt statement handle with the attribute type set to OCI_ATTR_ROW_COUNT
So add the folllowing code to your program :
ub4 row_count;
rc = OCIAttrGet ( stmthp, OCI_HTYPE_STMT, &row_count, 0, OCI_ATTR_ROW_COUNT,
errhp );
where:
stmthp is the OCIStmt statement handle
errhp is the OCIError error handle
rc is the defined return code (sword)
The number of rows updated (or deleted and inserted if that is your operation) is written into the passed row_count variable
Invoke OCIAttrGet(OCI_ATTR_ROW_COUNT) on the statement handle.

Resources