Here is what I am working with:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept in VARCHAR2 DEFAULT
'Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT',
'Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting',
Mgr in VARCHAR2 DEFAULT NULL) AS
vManager Varchar2(30) := Mgr;
vDepartment Varchar2(30) := Dept;
I want the default values to be that entire list of strings. Later in my code, I want the option to be able to specify a few managers, or a few departments, or not input anything, so that the entire list is returned:
WHERE m.first_name ||' ' || m.last_name IN (vManager) -- NULL if I don't input anything
OR d.department_name IN (vDepartment) -- All departments if I don't input anything
When I call the function, and don't input anything, I'm expecting to return all possible rows. That is the desired effect.
In order to be able to pass a list of strings as a parameter you should change the parameter to a list.
CREATE OR REPLACE TYPE t_departments IS TABLE OF VARCHAR2(30);
And then create your procedure based on that new type:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN t_departments DEFAULT t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting'), Mgr IN VARCHAR2 DEFAULT NULL) AS
Later you could use MEMBER OF instead of IN to check if a certain value is in the table:
...
WHERE d.department_name MEMBER OF Dept
...
Alternatively, if you can't change the type of the procedure parameters, you could just use variables:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN VARCHAR2 DEFAULT NULL, Mgr IN VARCHAR2 DEFAULT NULL) AS
TYPE t_departments IS TABLE OF VARCHAR2(30);
v_all_departments t_departments := t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting');
vManager VARCHAR2(30);
vDepartment t_departments;
BEGIN
IF Dept IS NULL THEN
vDepartment := v_all_departments;
ELSE
vDepartment := t_departments(Dept);
END IF;
...
WHERE d.department_name MEMBER OF vDepartment
...
END;
I think something is unclear here...
First, this is a procedure - not a function, therefor it will not return a thing.
A varchar is not an array.
When you are getting or returning a varchar2 - there is only one possible string to pass back and forth... If you want to use a list - you should check the array types PL/SQL offers.
http://docs.oracle.com/cd/A97630_01/appdev.920/a96624/05_colls.htm
Third - if you have constant values, in a database the place for it is within a table, not as constants in a procedure.
I would recommend you to check out Temporary tables - they're a great data facilitator for "within session" communication for a list that you would like to return. http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:5824706700346810645
These would be a great "in session"/"in connection" joins, like the one you described above (select ... where last_name in (select manager from temptable) for example).
Another thing - using the last name for identifying records in the DB is not the way you wanna go. You can use unique identifiers for that, and foreign keys. (if you are unfamiliar with the terms - What are database constraints? .
If I did not answer your question (or pointed you out to the answer) - please clarify your question, since I am not sure (probably as many others) what exactly you want to do (logically).
It's a quite unclear what you're asking but here is a minimal example how you can pass a list of values that can be used as a part of a SQL query to a PL/SQL subprogram:
-- SQL type so that can be used in SQL context (the select-statement)
create or replace type strlist_t is table of varchar2(4000);
/
create or replace procedure foo(p_owners in strlist_t default strlist_t('JANI')) is
begin
for i in (select * from all_tables
where owner in (select * from table(p_owners)))
loop
dbms_output.put_line(i.owner || '.' || i.table_name);
end loop;
end;
/
show errors
begin
-- define your own criteria
foo(strlist_t('SYS', 'JANI'));
-- use the default criteria
foo;
end;
/
Hope this helps !
Related
Is there any replacement for updateOnDuplicate option in bulckCreate method in sequelize ORM with NodeJs for oracle database . OtherWise Suggest a oracle procedure which accepts Array of Objects as Input Parameter With NodeJS , Sequelize ,Oracle .
**If I use below **
RoleModel.bulkCreate(data, {
updateOnDuplicate: ["role", "roleId", "ROLECODE"]
});
**Getting below Error
**
Error: oracle does not support the updateOnDuplicate option.
**For Oracle Procedure I Followed below
**
create or replace TYPE ROLE_OBJECT as object(
"ROLE" VARCHAR2(255 CHAR),
"ROLECODE" VARCHAR2 (255 CHAR),
"ROLEID" NUMBER(1)
);
create or replace TYPE ROLE_ARRAY is table of ROLE_OBJECT;
create or replace PROCEDURE ROLESUPDATING(useResultIn IN VARCHAR2)
IS
BEGIN
FOR i IN 1 .. useResultIn.count
LOOP
dbms_output.put_line(useResultIn.count);
END LOOP;
END;
BEGIN
ROLESUPDATING([{'role':'admin','rolecode':'adm','roleid':'1'}]);
END;
/*
It is not giving the length of the array
It is giving below Error
ORA-06531: Reference to uninitialized collection
ORA-06512: at "Collect.ROLESUPDATING", line 4
ORA-06512: at line 7
*/
[{prop:value}] are all javascript syntax, none of which makes any sense in PL/SQL. Instead, you can do this:
create or replace PROCEDURE ROLESUPDATING(useResultIn IN ROLE_ARRAY)
IS
BEGIN
FOR i IN 1 .. useResultIn.count
LOOP
dbms_output.put_line(useResultIn.count);
END LOOP;
END;
BEGIN
ROLESUPDATING(ROLE_ARRAY(ROLE_OBJECT('admin','adm',1)));
END;
Note that in addition to restructuring your actual call to ROLESUPDATING, it was also necessary to charge your parameter type from varchar2 to role_array (the collection type). In PL/SQL, you instantiate an object or collection by calling the type name like a function and passing in the property values like arguments. You can also nest the call to an object type and a collection type to create the collection in one statement, as in my answer.
User(
id :number ,
name : string pk)
How can I get the biggest latest id to insert a new one? The objective is to take the previous id and add a value but the id is not auto-increment. I was thinking about making a query to get the latest id but I hope there may be other optimized suggestion
thanks
You can do what you want. But just because you can do something does not mean you should. I repeat you should use a serial or identity. #Stefanov.sm is corrent in a MVCC environment this is a very bad idea; because it is a virtual guarantee you will get a duplicate at some point.
One way to get this is to write a function to do the insert. This function get the MAX(ID) + an increment. It also handles the duplicate error and retries the insert if needed.
create or replace
function establish_user (user_name_in text )
returns integer
language plpgsql
as $$
declare
k_id_inc constant integer := 1;
l_id integer;
l_inserted boolean = false;
begin
select coalesce(max(id) + k_id_inc)
into l_id
from users;
while not l_inserted
loop
begin
insert into users (id, name)
values (l_id, user_name_in);
l_inserted = true;
exception
when others then
if sqlstate != '23505'
or sqlerrm not like '%users_id_uk%'
then
raise;
else
l_id = l_id + 1;
end if;
end;
end loop;
return l_id;
end;
$$;
See Demo here.
I want to run this code from my query windows inside Excel but I get an error after it completes the first statement. If can run it fine in PCC. I'm assuming I need to create a stored procedure with a variable. Parent = 'B-8579-K' is the variable value.
How to I create the stored procedure?
How do I call the stored procedure and pass on the variable?
Will the call even work from within Excel without causing an error?
delete z_Expl_BOM_Temp;
insert into z_Expl_BOM_Temp
select
Parent, L1_Child_Seq, L1_Child, L1_Child_QTY,
L2_Child_Seq, L2_Child, L2_Child_QTY,
L3_Child_Seq, L3_Child, L3_Child_QTY,
L4_Child_Seq, L4_Child, L4_Child_QTY,
L5_Child_Seq, L5_Child, L5_Child_QTY
from
EGC_Expl_BOM_TT
where
Parent = 'B-8579-K' ;
Creating Stored Procedures in Pervasive are documented here.
An example of a stored procedure that takes a parameter is:
CREATE PROCEDURE Checkmax(in :classid integer);
BEGIN
DECLARE :numenrolled integer;
DECLARE :maxenrolled integer;
SELECT COUNT(*) INTO :numenrolled FROM Enrolls WHERE class_ID = :classid;
SELECT Max_size INTO :maxenrolled FROM Class WHERE id = :classid;
IF (:numenrolled >= :maxenrolled) THEN
PRINT 'Enrollment Failed. Number of students enrolled reached maximum allowed for this class' ;
ELSE
PRINT 'Enrollment Possible. Number of students enrolled has not reached maximum allowed for this class';
END IF;
END;
And then calling it:
CALL Checkmax(101)
I finally got the SP created without an error:
CREATE PROCEDURE EGC_Expl_BOM_TT(in :CParent varchar(20));
BEGIN
delete from z_Expl_BOM_Temp;
insert into z_Expl_BOM_Temp
select
Parent, L1_Child_Seq, L1_Child, L1_Child_QTY,
L2_Child_Seq, L2_Child, L2_Child_QTY,
L3_Child_Seq, L3_Child, L3_Child_QTY,
L4_Child_Seq, L4_Child, L4_Child_QTY,
L5_Child_Seq, L5_Child, L5_Child_QTY
from
EGC_Exploded_BOM
where
Parent = :CParent;
END;
I can call is just fine in PCC. However, when I run the CALL in Excel's query window is will execute the procedure but when it completes I get this error:
"The query did not run, or the database table could not be opened.
Check the database server or contact your database administrator. Make sure the external database is available and hasn't been moved or reorganized, then try the operation again."
This might now be turning into a VBA question. Can I run the CALL with VBA?
Is it possible to use interfaces with objects in plsql?
For example say I have a bunch of objects and want to sort them by date with a generic function. Could I have something like the following?
create or replace interface DateInterface
(
member function get_date return date
)
/
create or replace type TypeA implements DateInterface
(
my_date date,
member function get_date return date
)
/
create or replace type body TypeA is
member function get_date return date is
begin
return my_date;
end;
end;
/
create or replace type dateTable as table of DateInterface;
/
function EarliestDate (dates dateTable) returns date is
l_earliestDate date;
begin
l_earliestDate := dates(1);
for i in dates.first .. dates.last
loop
if l_earliestDate.get_date > dates(i).get_date then
l_earliestDate := dates(i);
end if;
end loop;
return l_earliestDate;
end;
I know I could have them inherit a class, but is there anything for doing this with an interface which would be more flexible?
Oracle supports interfacing external sources like Java and C languages within its procedures and functions using the LANGUAGE clause in them. Implement the date sorting logic of the objects in a procedure or a function and interface with other programming languages.
For reference, see Oracle's documentation
I'm late to the party but Oracle has sort of OO support now in PLSQL:
https://docs.oracle.com/en/database/oracle/oracle-database/18/adobj/inheritance-in-sql-object-types.html#GUID-A5FE7A3B-7C1C-430A-8095-76AE955119C9
You have inheritance etc. with that. So you could do sort of an interface for PLSQL Object Type Objects
But it comes with problems. Eg. storing in tables.
See https://oracle-base.com/articles/8i/object-types
How can i get Delphi 7 to return a '0' or '1' when the FieldType of a TQuery descendant's Field is a ftBoolean? By default this returns 'TRUE' or 'FALSE', that is
Query1.Fields[0].AsString would return '0', not 'FALSE'
Use
(Query1.Fields[0] as TBooleanField).DisplayValues := 'TRUE;FALSE';
to set a string in the form of 'TRUE;FALSE' (or '1;0'). This allows you to define what values AsString will return.
If you added the field in design time, and/or you got yourself a boolean field component, you can use that too, without the typecast:
Query1YourBooleanField.DisplayValues := 'TRUE;FALSE';
By the way, it's not the query that returns '0', not is it the query that 'is' ftBoolean. These are the fields in the query that represent fields in the table or query result set.
Why not simple use Query1.Fields[0].AsInteger if you want a numerical representation for the boolean ? I think that should work ...
Modify STextFalse and STextTrue resourcestrings in 'dbconsts.pas'. You can put a modified version of the file to your project folder, or go about just like localizing your application.
If you want to modify the strings at run-time you can use the below (credit):
[...]
implementation
uses
dbconsts;
{$R *.dfm}
procedure SetResourceString(ResStringRec: pResStringRec; NewStr: string);
var
OldProtect: DWORD;
begin
if ResStringRec = nil then
Exit;
VirtualProtect(ResStringRec, SizeOf(ResStringRec^),
PAGE_EXECUTE_READWRITE, #OldProtect) ;
ResStringRec^.Identifier := Integer(NewStr) ;
VirtualProtect(ResStringRec, SizeOf(ResStringRec^), OldProtect, #OldProtect) ;
end;
const
TextFalse = '0';
TextTrue = '1';
procedure TForm1.FormCreate(Sender: TObject);
begin
SetResourceString(#STextFalse, TextFalse);
SetResourceString(#STextTrue, TextTrue);
[...]
Personally I use this trick in such situations:
const DigitBool: array[Boolean] of string = ['0', '1'];
//and than
Caption := DigitBool[Query1.Fields[0].Value];
Unfortunately it appears that the only way to do this is to check for every TField:
if (Query1.Fields[i] is TBooleanField) and then use one of the methods provided above.
There is no global TField.AsString hack as far as I'm aware.