I have this datatype declared and afterwards populated:
CREATE OR REPLACE TYPE CRAMER."T_CREATELINK_PORTLIST" IS TABLE OF o_CreateLink_PORTLIST;
AND
TYPE o_CreateLink_PORTLIST AS OBJECT (
PORTNAME VARCHAR2(50),
PORTID NUMBER,
ISNEWPORT NUMBER,
SHELFNAME VARCHAR2(50),
SLOTNAME VARCHAR2(50),
BANDWIDTHNAME VARCHAR2(50),
ISSELECTED NUMBER );
What I want to do is get some object/s from one table of this type to another table of same type. Something like this:
lsa_all_ports cramer.t_CreateLink_PORTLIST;
INSERT INTO lsa_filter_ports VALUES( SELECT *
FROM TABLE(CAST(lsa_all_ports AS cramer.t_CreateLink_PORTLIST)) new
WHERE new.bandwidthname = 'band2');
I'm pretty sure this syntax is wrong, but is there a simple way to do this??
Other atempts:
SELECT * INTO lsa_filter_ports
FROM TABLE(CAST(lsa_all_ports AS cramer.t_CreateLink_PORTLIST)) new
WHERE new.bandwidthname = 'band2';
ORA-06550: not enough values
lsa_filter_ports cramer.t_CreateLink_PORTLIST := cramer.t_CreateLink_PORTLIST();
INSERT INTO lsa_filter_ports
(SELECT * FROM TABLE(CAST(lsa_all_ports AS cramer.t_CreateLink_PORTLIST))
WHERE bandwidthname = 'band2');
ORA-00942: table or view does not exist
If I understood everything correctly You can do it like here:
declare
lsa_all_ports t_createlink_portlist :=
t_createlink_portlist( o_createlink_portlist ('abc', 1, 'band1'),
o_createlink_portlist ('def', 2, 'band2'));
lsa_filter_ports t_createlink_portlist := t_createlink_portlist();
begin
select cast(multiset(select *
from table(lsa_all_ports)
where bandwidthname = 'band2')
as t_createlink_portlist)
into lsa_filter_ports
from dual;
end;
You can better try to use the SELECT...INTO command to do this.
The SELECT INTO statement retrieves data from one or more database
tables, and assigns the selected values to variables or collections.
And if the table is already created and you want to insert into it then you can simply do like
insert into lsa_filter_ports
select * from o_CreateLink_PORTLIST
WHERE bandwidthname = 'band2'
Please see below the usage.
--Created object
create or replace TYPE o_CreateLink_PORTLIST AS OBJECT (
PORTNAME VARCHAR2(50),
PORTID NUMBER,
ISNEWPORT NUMBER,
SHELFNAME VARCHAR2(50),
SLOTNAME VARCHAR2(50),
BANDWIDTHNAME VARCHAR2(50),
ISSELECTED NUMBER );
---created table same as object
create table lsa_filter_ports (
PORTNAME VARCHAR2(50),
PORTID NUMBER,
ISNEWPORT NUMBER,
SHELFNAME VARCHAR2(50),
SLOTNAME VARCHAR2(50),
BANDWIDTHNAME VARCHAR2(50),
ISSELECTED NUMBER );
--A type of object
CREATE OR REPLACE TYPE T_CREATELINK_PORTLIST IS TABLE OF o_CreateLink_PORTLIST;
---Anonymous Block
declare
lsa_all_ports t_CreateLink_PORTLIST;
begin
INSERT INTO lsa_filter_ports
SELECT *
FROM TABLE(CAST(lsa_all_ports AS t_CreateLink_PORTLIST)) new
WHERE new.bandwidthname = 'band2';
end;
Collection to collection copy
declare
lsa_all_ports t_CreateLink_PORTLIST:= t_createlink_portlist(o_createlink_portlist ('abc', 1, 'band1'),o_createlink_portlist ('def', 2, 'band2'));
lsa_filter_ports t_CreateLink_PORTLIST:= t_createlink_portlist();
cursor cur is
SELECT *
--FROM TABLE(CAST(lsa_all_ports AS t_CreateLink_PORTLIST)) new
FROM TABLE(lsa_all_ports) new
WHERE new.bandwidthname = 'band2';
begin
for i in cur
loop
lsa_filter_ports.extend;
lsa_filter_ports(cur%rowcount) := lsa_all_ports(cur%rowcount);
dbms_output.put_line(lsa_filter_ports(cur%rowcount).PORTNAME);
end loop;
end;
Related
I have the following function, which works,that I would like to convert into a INSERT/UPDATE
trigger for the column hash_pk. I'm struggling with syntax errors trying to convert this to a trigger. Could someone please help me out.
Secondly, would it be more efficient to store the column hash_pk as a RAW(if so how big) instead of a VARCHAR2?
Thanks in advance to all that answer.
CREATE or REPLACE FUNCTION HASH_SHA512 (
psINPUT IN VARCHAR2
) RETURN VARCHAR2 AS
rHash RAW (512);
BEGIN
rHash := DBMS_CRYPTO.HASH (TO_CLOB (psINPUT),
dbms_crypto.HASH_SH512);
RETURN ((RAWTOHEX (rHash)));
END HASH_SHA512;
/
CREATE table t(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
hash_pk VARCHAR2(1000) not NULL PRIMARY KEY,
c CLOB,
create_date DATE DEFAULT SYSDATE
);
/
create or replace
trigger hash_trg
before insert or update on t
for each row
begin
:new.hash_pk := HASH_SHA512(:new.c);
end;
insert into t (c) values (
rpad('z',16,'z')
);
SELECT * from t
SEQ_NUM HASH_PK C CREATE_DATE
1 2C9437F9D8FB13FC959CA2B9D5B81958B5A32556C60E35D66D1DA92227593A14316FD32EE2B3EEE06EECB1484A0CACAE61A4F930E772BB78AC84E75948DAA628 zzzzzzzzzzzzzzzz 12-OCT-21
update t set c='Good Bye';
SELECT * from t;
SEQ_NUM HASH_PK C CREATE_DATE
1 DCBC14FA2F46F1E264BBD52C4A3DF87E32CC511B43FD9AD722EACCFCA6D8CBE398D10E61E83A85625C7CF96E70348F2D33595196577B01C488030E560A7D34F7 Good Bye 12-OCT-21
I got it was missing parenthesis
create or replace
trigger hash_trg
before insert or update on t
for each row
begin
:new.hash_pk := DBMS_CRYPTO.HASH ((:new.c),
dbms_crypto.HASH_SH512);
end;
I have two equal Oracle tables which have same column names.
1) DoctorProfile_New
2) DoctorProfile_Old
Both tables share the same structure as follows.
DOCREGNO NOT NULL VARCHAR2(10)
DOCCATOGARY NOT NULL VARCHAR2(10)
ANAME1 VARCHAR2(50)
ANAME2 VARCHAR2(50)
ANAME3 VARCHAR2(50)
ANAME4 VARCHAR2(50)
ANAME5 VARCHAR2(50)
ANAME6 VARCHAR2(50)
ANAME7 VARCHAR2(50)
ANAME8 VARCHAR2(50)
ANAME9 VARCHAR2(50)
AGENDER VARCHAR2(2)
ARESCTOWN VARCHAR2(100)
DOCRCODE NUMBER
What I need to do is insert all the data from table DoctorProfile_Old to DoctorProfile_New.
The only difference is for the column DOCREGNO, I have to insert new hard coded value.
insert into DoctorProfile_New
(
DOCREGNO,
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
)
values
('000081',
(
select
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
from DoctorProfile_Old WHERE DOCREGNO='T07004'
)
)
I tried the above query and it always gives the Oracle error
ORA-00947: not enough values
You can use insert select to do this;
insert into DoctorProfile_New (
DOCREGNO,
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
)
select
'000081',
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
from DoctorProfile_Old
WHERE DOCREGNO='T07004';
How about this?
insert into DoctorProfile_New
(DOCREGNO,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN)
select '000081' as DOCREGNO, DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN from DoctorProfile_Old WHERE DOCREGNO='T07004';
If you want to insert DOCREGNO automatically instead of hard coated value, you should create trigger and use sequence value.
CREATE OR REPLACE TRIGGER DoctorProfile_New_TRG
BEFORE INSERT OR UPDATE ON DoctorProfile_New
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
if :new.DOCREGNO is null then
select lpad(some_sequence.nextval, 6, '0') into :new.DOCREGNO from dual;
end if;
END;
Insert all data from DoctorProfile_Old
insert into DoctorProfile_New
(DOCREGNO,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN)
select null,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN from DoctorProfile_Old;
This is my query. A Trigger which automatically stores in a separate table called ‘ExcellentSale’ the Sales Agent
name, car model and manufacturer name, each time the agreed price of a
SalesTransaction is more than 20% of the car’s asking price. (Note: You need to create
the ‘ExcellentSale’ table before implementing this trigger. To create the primary key, use a
sequence that starts at 1 and increments by 1). It shows error like this
ERROR at line 23: PL/SQL: ORA-00942: table or view does not exist
1. create or replace trigger filltable
2. after insert on salestransaction
3. for each row
4. declare
The code for the trigger is:
create or replace trigger filltable
after insert on salestransaction
for each row
declare
aname varchar2(25);
modname varchar2(25);
manfname varchar2(25);
askpr number;
agrpr number;
begin
select sa.name,m.name,mf.name
into aname,modname,manfname
from manufacturer mf,model m, car c, salestransaction st, salesagent sa
where mf.manufacturerid = m.manufacturerid and
m.modelno = c.modelno and
c.vin = st.vin and
st.agentid = sa.agentid;
select askingprice,agreedprice
into askpr,agrpr
from car c,salestransaction st
where c.VIN = St.vin;
if(agrpr > askpr*1.2) then
insert into excellentsales values(agent_seq.nextval,aname,modname,manfname);
end if;
end filltable;
/
create table excellentsales
(agentid varchar2(5) not null,
agentname varchar2(25),
carmodel varchar2(25),
mfname varchar2(25),
primary key(agentid))
CREATE SEQUENCE agent_seq START WITH 1 INCREMENT BY 1;
You don't need to query SALESTRANSACTION within a row trigger on SALESTRANSACTION - you should instead be using the :NEW values on the SALESTRANSACTION row for which the trigger was invoked. I suggest rewriting your trigger as:
create table excellentsales
(EXCELLENTSALES_ID NUMBER
CONSTRAINT PK_EXCELLENTSALES
PRIMARY KEY
USING INDEX,
agentid varchar2(5) not null,
agentname varchar2(25),
carmodel varchar2(25),
mfname varchar2(25))
CREATE SEQUENCE EXCELLENTSALES_SEQ
START WITH 1
INCREMENT BY 1;
create or replace trigger filltable
after insert on salestransaction
for each row
declare
aname varchar2(25);
modname varchar2(25);
manfname varchar2(25);
askpr number;
agrpr number;
begin
SELECT m.NAME, mf.NAME, c.ASKINGPRICE
INTO modname, manfname, askpr
FROM CAR c
INNER JOIN MODEL m
ON m.MODELNO = c.MODELNO
INNER JOIN MANUFACTURER mf
ON mf.MANUFACTURERID = m.MANUFACTURERID
WHERE c.VIN = :NEW.VIN;
SELECT sa.NAME
INTO aname
FROM SALESAGENT sa
WHERE sa.AGENTID = :NEW.AGENTID;
agrpr := :NEW.AGREEDPRICE;
if agrpr > askpr * 1.2 then
insert into excellentsales (EXCELLENTSALES_ID, AGENTID, AGENTNAME, CARMODEL, MFNAME)
values(EXCELLENTSALES_SEQ.nextval, :NEW.AGENTID, aname, modname, manfname);
end if;
end filltable;
I also redesigned your EXCELLENTSALES table to include a primary key which is not the AGENTID. As originally designed EXCELLENTSALES did not record the actual AGENTID (which should apparently be coming from SALESTRANSACTION), which in the real world might be needed, for example, for paying commissions.
I first create an address_type object
CREATE TYPE address_type AS OBJECT
( line1 VARCHAR2(100)
, line2 VARCHAR2(100)
, line3 VARCHAR2(100)
, city VARCHAR2(50)
, state VARCHAR2(50)
, country VARCHAR2(50)
, zip VARCHAR2(10)
);
/
I create a nested table type of the above object.
CREATE TYPE address_table AS TABLE OF ADDRESS_TYPE;
/
I then create another object as follows:
CREATE TYPE telephone_number_type AS OBJECT
( country_code VARCHAR2(4)
, area_code VARCHAR2(10)
, phone_number VARCHAR2(10)
, extension VARCHAR2(10)
, number_type VARCHAR2(10)
);
/
And then I create a nested table type as follows:
CREATE TYPE telephone_number_table AS TABLE OF TELEPHONE_NUMBER_TYPE;
/
Now I create a table named person. Many of whose columns are not much useful in this question, except for the telephone_numbers column which is of nested table telephone_number_table type.
CREATE TABLE person
( personid INTEGER PRIMARY KEY
, fname VARCHAR2(50) NOT NULL
, mname VARCHAR2(50)
, lname VARCHAR2(50) NOT NULL
, email VARCHAR2(255) UNIQUE
, password VARCHAR2(255) NOT NULL
, birthdate DATE
, billing_address ADDRESS_TABLE
, delivery_address ADDRESS_TABLE
, telephone_numbers TELEPHONE_NUMBER_TABLE
, display_pic BLOB
, ts_registration TIMESTAMP
, ts_verification TIMESTAMP
, ts_last_updated TIMESTAMP
) NESTED TABLE billing_address STORE AS nt_billing_address
, NESTED TABLE delivery_address STORE AS nt_delivery_address
, NESTED TABLE telephone_numbers STORE AS nt_telephone_numbers
, LOB(display_pic) STORE AS SECUREFILE (
TABLESPACE users
ENABLE STORAGE IN ROW
CHUNK 4096
PCTVERSION 20
NOCACHE
NOLOGGING
COMPRESS HIGH
)
;
I then create a sequence for this:
CREATE SEQUENCE sq_personid;
To insert values into the person table I use an anonymous block as follows:
DECLARE
v_fname person.fname%TYPE := 'Yogeshwar';
v_mname person.mname%TYPE := '';
v_lname person.lname%TYPE := 'Rachcha';
v_email person.email%TYPE := 'yogeshrachcha#gmail.com';
v_password person.password%TYPE := 'mail_123';
v_birthdate person.birthdate%TYPE := TO_DATE('28-03-1987', 'DD-MM-YYYY');
v_telephone_numbers TELEPHONE_NUMBER_TABLE;
v_billing_address ADDRESS_TABLE;
v_delivery_address ADDRESS_TABLE;
BEGIN
v_telephone_numbers := TELEPHONE_NUMBER_TABLE
( TELEPHONE_NUMBER_TYPE('+91','22','123456','','Residence')
, TELEPHONE_NUMBER_TYPE('+91','22','456798','123','Office')
, TELEPHONE_NUMBER_TYPE('+91','','1234567890','','Mobile'));
v_billing_address := ADDRESS_TABLE (ADDRESS_TYPE ( 'Line 1', 'Line 2', 'Line 3', 'Mumbai', 'Maharashtra', 'India', '123456'));
v_delivery_address := ADDRESS_TABLE (ADDRESS_TYPE ( 'Line 1', 'Line 2', 'Line 3', 'Mumbai', 'Maharashtra', 'India', '123456'));
-- billing and delivery addresses are the same. These are not much importance in this question.
INSERT INTO person VALUES
( sq_personid.nextval
, v_fname
, v_mname
, v_lname
, v_email
, v_password
, v_birthdate
, v_billing_address
, v_delivery_address
, v_telephone_numbers
, NULL
, sysdate
, sysdate
, sysdate);
END;
Everything runs absolutely perfect till this point. Then in an anonymous block like the following, I try to create a nested table variable and use it in an SQL query:
DECLARE
TYPE t_country_codes IS TABLE OF VARCHAR2(4);
country_codes T_COUNTRY_CODES := T_COUNTRY_CODES('+1', '+44', '+91');
cc VARCHAR2(4);
BEGIN
FOR i IN (SELECT t.country_code
FROM person p
CROSS JOIN TABLE(p.telephone_numbers) t
WHERE t.country_code IN (SELECT COLUMN_VALUE -- I doubt the problem is with this SELECT statement.
FROM TABLE(country_codes))) LOOP
dbms_output.put_line(i.country_code);
END LOOP;
END;
/
I get this error:
ORA-06550: line 8, column 70:
PLS-00642: local collection types not allowed in SQL statements
ORA-06550: line 8, column 64:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
A nested table type can be declared either in SQL (via the CREATE TYPE statement like you did with the telephone_number_table type) or in PL/SQL (via the TYPE declaration on the DECLARE block). If you declare the type in PL/SQL, you cannot use an instance of the type in SQL. You would need to declare the type in SQL in order to use an instance of the type in SQL.
Please, try to move your "local" (from last block) collection definition to the schema level.
Also, this post can be helpful.
I created the following simple PL/SQL stored procedure example to ask a specific question. This procedure inserts an employee name and id number into a table called employees_???. The ??? is explained below.
PROCEDURE hire_employee (emp_id IN INTEGER, name IN VARCHAR2, country IN VARCHAR2)
AS
BEGIN
INSERT INTO employees_??? VALUES (emp_id, name, 1000);
END hire_employee;
What I need is to set the table name based on the IN variable country. For example,
If country = 'usa', I want the INSERT line to read:
INSERT INTO employees_usa VALUES (emp_id, name, 1000);
If country = 'germany', I want the INSERT line to read:
INSERT INTO employees_germany VALUES (emp_id, name, 1000);
If country = 'france', I want the INSERT line to read:
INSERT INTO employees_france VALUES (emp_id, name, 1000);
etc...
Is there a way to do this in PL/SQL by substituting something in place of employee_??? so only one line of code for INSERT is used? Or is using a case or if/then/else statement the best way?
To answer your question, you have to use execute immediate and create your statement dynamically.
create or replace procedure hire_employee (
emp_id IN INTEGER
, name IN VARCHAR2
, country IN VARCHAR2 ) is
-- maximum length of an object name in Oracle is 30
l_table_name varchar2(30) := 'employees_' || country;
begin
execute immediate 'insert into ' || l_table_name
|| ' values (:1, :2, 1000)'
using emp_id, name;
end hire_employee;
However, this is a massively over-complicated way of storing the data. If you want to select all data you have to union large numbers of tables.
It would be far better to normalise the database properly and add country to an employees table.
Something like the following:
create table employees (
emp_id number(16)
, country varchar2(3) -- ISO codes
, name varchar2(4000) -- maximum who knows what name people might have
, < other_columns >
, constraint pk_employees primary key ( emp_id )
);
Your procedure then becomes a very simple insert statement:
create or replace procedure hire_employee (
emp_id in integer
, name in varchar2
, country in varchar2 ) is
insert into employees
values ( emp_id, country, name, 1000 );
end hire_employee;
You can use dynamic SQL and the EXECUTE IMMEDIATE construct. In this, you construct the query as a string and then execute it. A good example is at http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm