How i can put result rows of this cursor, into column ?
CURSOR CUR1 IS SELECT FILMS.FILM_CODE from FILMS where FILMS.ID_FILM = ID_FILM_ FOR UPDATE OF FILM_CODE;
I wanna put this rows into FILM_CODE column of table below
CREATE TABLE SESSIONF(
ID_SESSION INTEGER DEFAULT SESSION_ID_SEQ.NEXTVAL NOT NULL,
FILM_CODE INTEGER NOT NULL,
NAMEOFGENRE VARCHAR2(200) NOT NULL,
HALL_CODE INTEGER NOT NULL,
NUMBEROFFREEPLACES INTEGER NOT NULL,
COST INTEGER NOT NULL,
DATA_OF_SESSION DATE,
CONSTRAINT PK_ID_SESSION PRIMARY KEY (ID_SESSION)
)
TABLESPACE TBS_PERM_KINO;
Sorry if topic with the same issue already exist, couldn't find it.
EDIT
Procedure on update
create or replace procedure UpdateFILM(ID_FILM_ FILMS.ID_FILM%TYPE,FILM_CODE_ IN FILMS.FILM_CODE%TYPE,FILM_ IN FILMS.FILM%TYPE,GENRE_CODE_ FILMS.GENRE_CODE%TYPE,DIR_CODE_ FILMS.DIRECTOR_CODE%TYPE,YEAROFRELEAS_ FILMS.YEAROFRELEAS%TYPE)
is
varible int := 0;
varible2 int := 0;
varible3 int := 0;
varible4 int := 0;
CURSOR CUR1 IS SELECT FILMS.FILM_CODE from FILMS where FILMS.ID_FILM = ID_FILM_ FOR UPDATE OF FILM_CODE;
F_CODE SESSIONF.FILM_CODE%TYPE;
begin
select count(*) INTO VARIBLE from FILMS where FILM_CODE_ = FILMS.FILM_CODE;
select count(*) INTO VARIBLE2 from DIRECTORS where DIR_CODE_ = DIRECTORS.DIRECTOR_CODE;
IF varible != 0
THEN DBMS_OUTPUT.put_line('FILM_CODE_ erro : there is ALREADY THE SAME FILM_CODE');
ELSIF varible2 =0
THEN DBMS_OUTPUT.put_line('DIR_CODE_ ERR : there IS NO SUCH DIRECTOR_CODE');
ELSIF varible3 !=0
THEN DBMS_OUTPUT.put_line('GENRE_CODE_ ERR : there IS NO SUCH GENRE_CODE_');
ELSIF TO_DATE('12/12/1941', 'DD/MM/YYYY') > YEAROFRELEAS_
THEN DBMS_OUTPUT.put_line('YEAROFRELEAS_ erro : DATE IS LESS THEN 1941');
ELSIF TO_DATE('12/12/3000', 'DD/MM/YYYY') < YEAROFRELEAS_
THEN DBMS_OUTPUT.put_line('YEAROFRELEAS_ erro : DATE IS TO HIGH(MORE THEN 3000)');
ELSE
UPDATE FILMS SET
FILMS.ID_FILM = ID_FILM_,
FILMS.FILM_CODE = FILM_CODE_,
FILMS.FILM = FILM_,
FILMS.GENRE_CODE = GENRE_CODE_,
FILMS.DIRECTOR_CODE = DIR_CODE_,
FILMS.YEAROFRELEAS = YEAROFRELEAS_
WHERE FILMS.ID_FILM = ID_FILM_;
OPEN CUR1;
LOOP
FETCH CUR1 INTO F_CODE;
UPDATE SESSIONF SET SESSIONF.FILM_CODE=FILM_CODE_ WHERE F_CODE = SESSIONF.FILM_CODE;
DBMS_OUTPUT.put_line(F_CODE);
EXIT WHEN CUR1%NOTFOUND;
END LOOP;
CLOSE CUR1;
DBMS_OUTPUT.put_line('films update successful');
END IF;
exception
when others
then DBMS_OUTPUT.put_line(sqlerrm);
end;
CREATE sequence FILM_ID_SEQ;
CREATE TABLE FILMS (
ID_FILM INTEGER DEFAULT FILM_ID_SEQ.NEXTVAL NOT NULL,
FILM_CODE INTEGER NOT NULL,
FILM VARCHAR2(200) NOT NULL UNIQUE,
GENRE_CODE INTEGER NOT NULL,
DIRECTOR_CODE INTEGER NOT NULL,
YEAROFRELEAS DATE,
CONSTRAINT PK_ID_FILM PRIMARY KEY (ID_FILM)
)
TABLESPACE TBS_PERM_KINO;
alter table SESSIONF add constraint FK_FILM_CODE_REF_ID_FILM foreign key (FILM_CODE) references FILMS (ID_FILM) ON DELETE CASCADE;
The way you put it, you can't do that.
Why? Because table contains numerous NOT NULL columns, while cursor selects only one column: FILM_CODE. You could store that value, but what will you put into other mandatory columns?
Therefore, you'll have to either fix cursor's select statement so that it fetches additional columns, or modify table and remove not null constraints (or, possibly, set default values for those columns).
On the other hand, why would you use a cursor? Cursor is usually slow (as you'll probably use it in a loop which works row-by-row). Consider
insert into sessionf (filmcode, nameofgenre, ...)
select f.filmcode, ...
from films f
join ... on ...
Related
i have this table:
CREATE TABLE "ALMAT"."PRODUCT"
( "ID" NUMBER(*,0) NOT NULL ENABLE,
"NAME" VARCHAR2(50 BYTE),
"PRICE" NUMBER(*,0),
"DESCRIPTION" VARCHAR2(180 BYTE),
"CREATE_DATE" DATE,
"UPDATE_DATE" DATE,
CONSTRAINT "PRODUCT_PK" PRIMARY KEY ("ID"))
i want to update data in this table, this is my stored procedure:
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(prod_id int, prod_name varchar2 default null, prod_price int default null) AS
BEGIN
update product
set
name = prod_name,
price = prod_price,
update_date = sysdate
where id = prod_id;
commit;
END UPDATEPRODUCT;
im using optional parameters, how can i update only 1 column? for example: only "NAME" or "PRICE".
Use COALESCE (or NVL) to keep the current value when a NULL value is passed in (or the default is used):
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(
prod_id PRODUCT.ID%TYPE,
prod_name PRODUCT.NAME%TYPE DEFAULT NULL,
prod_price PRODUCT.PRICE%TYPE DEFAULT NULL
)
AS
BEGIN
UPDATE product
SET name = COALESCE(prod_name, name),
price = COALESCE(prod_price, price),
update_date = SYSDATE
WHERE id = prod_id;
END UPDATEPRODUCT;
Also, do not COMMIT in a stored procedure as it prevents you from chaining multiple procedures together in a single transaction and rolling them all back as a block. Instead, COMMIT from the PL/SQL block that calls the procedure.
You can use NVL function here. So your updated procedure would look alike -
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(prod_id int,
prod_name varchar2 default null,
prod_price int default null) AS
BEGIN
UPDATE product
SET name = NVL(prod_name, name),
price = NVL(prod_price, price),
update_date = sysdate
WHERE id = prod_id;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END UPDATEPRODUCT;
I'm getting this error when I try to create this trigger. I tried everything but I don't know what seems to be the problem.
Here is the code:
CREATE OR REPLACE TRIGGER after_price_update
AFTER UPDATE
ON Item
FOR EACH ROW
DECLARE new_totalprice INT;
BEGIN
IF :OLD.price <> :new.price THEN
new_totalprice := :old.Quantity * :new.price;
INSERT INTO OrderRecord(OrderRecord_Id, Item_Id, Employee_Id, Reservation_Id, Order_Time, Quantity, TotalPrice)
VALUES(old.OrderRecord_Id, old.Item_Id, old.Employee_Id, old.Reservation_Id, old.Order_Time, old.Quantity, new_totalprice);
END IF;
END;
And the error is:
4/22 PLS-00049: bad bind variable 'OLD.QUANTITY'
The tables look like this:
CREATE TABLE Item (
Item_Id int PRIMARY KEY,
Menu_Id int,
Name varchar2(20),
Description varchar2(120),
Price int,
FOREIGN KEY(Menu_Id) REFERENCES Menu(Menu_Id)
);
CREATE TABLE OrderRecord (
OrderRecord_Id int PRIMARY KEY,
Item_Id int,
Employee_Id int,
Reservation_Id int,
Order_Time date,
Quantity int,
TotalPrice int,
FOREIGN KEY(Item_Id)References Item(Item_Id),
FOREIGN KEY(Employee_Id)References Employee(Employee_Id),
FOREIGN KEY(Reservation_Id)References Reservation(Reservation_Id)
);
Table OrderRecord is not the table being updated: you cannot reference an old value for this table; you need to read this value with some SELECT statement.
IF :OLD.price <> :new.price THEN
new_totalprice := <Quantity> * :new.price;
There is no column Quantity in the table ITEM on which you are applying trigger.
If I understand what you are wanting to do correctly, it looks like you want to update the calculated total price of all existing OrderRecord entries when an Item entry has a price change.
CREATE OR REPLACE TRIGGER after_price_update
AFTER UPDATE
ON Item
FOR EACH ROW
BEGIN
IF NVL(:OLD.Price, 0) <> NVL(:new.Price, 0) THEN
-- Update the child table "OrderRecord" for this item using new price
UPDATE OrderRecord SET TotalPrice = Quantity * :new.Price WHERE Item_Id = :new.Item_id;
END IF;
END;
Note that this trigger is on the parent table, Item, and references two values from the modified record: price and Item_Id, to update the child OrderRecord table.
Also note the use of NVL to watch for nulls because the comparison will not succeed if a null is on either side. Using zero for null is debatable; one could use a value that will never occur such as a negative number.
Question:
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).
I am using these tables
Manufacturer(manufacturerID, name, region)
Model(modelNo, name, type, previousModel, manufacturerID)
Car(VIN, dateAcquired, yearBuilt, purchasedPrice, askingPrice,
currentMileage, modelNo)
SalesAgent(agentID, name, DOB)
SalesTransaction(VIN, custID, agentID, dateOfSale, agreedPrice)
Here is my attempt
create sequence generateKey
start with 1
increment by 1;
CREATE TABLE ExcellentSale(
recordNo NUMBER,
agentName VARCHAR2(20) NOT NULL,
modelName VARCHAR2(20) NOT NULL,
manufacturerName VARCHAR2(20) NOT NULL,
PRIMARY KEY(recordNo));
create or replace trigger AutoStore
before insert on SalesTransaction
for each row
declare
SAname varchar2(50);
carModel varchar2(50);
manufacturerName varchar2(50);
askingprice number;
agreedprice number;
begin
select sa.name, mo.name, mu.name, c.askingprice, st.agreedprice
into SAname, CarModel, manufacturerName, askingprice, agreedprice
from manufacturer MU, Model MO, Car C, SalesAgent SA, SalesTransaction ST
where mu.manufacturerid = mo.manufacturerid
and st.vin = c.vin
AND c.vin = :new.vin
AND sa.agentID = :new.agentID;
IF :new.agreedPrice > (1.2 * askingPrice) THEN
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, agentName, modelName, manufacturerName);
END IF;
end AutoStore;
/
and the error I am getting is
PL/SQL: ORA -00984: Column not allowed here
please help, thank you in advanced.
In the VALUES clause of the insert statement, you have go use the variables that hold the values you need instead of the column names.
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, SAname, CarModel, manufacturerName);
I'm writing a procedure to fill up a child table from a parent table. The child table however has more fields than the parent table ( as it should be ). I've conjured a cursor which point to a selection, which is essentially a join of multiple tables.
Here's the code I got so far :
CREATE OR REPLACE PROCEDURE Pop_occ_lezione
AS
x Lezione%rowtype;
CURSOR cc IS
WITH y as(
SELECT Codice_corso,
nome_modulo,
Data_inizio_ed_modulo diem,
Giorno_lezione,
ora_inizio_lezione o_i,
ora_fine_lezione o_f,
anno,
id_cdl,
nome_sede,
locazione_modulo loc
FROM lezione
join ( select id_cdl, anno, codice_corso from corso ) using (codice_corso)
join ( select codice_corso, locazione_modulo from modulo ) using (codice_corso)
join ( select nome_sede, id_cdl from cdl ) using (id_cdl)
WHERE
case
when extract (month from Data_inizio_ed_modulo) < 9 then extract (year from Data_inizio_ed_modulo) - 1
else extract (year from Data_inizio_ed_modulo)
end = extract (year from sysdate+365)
)
SELECT *
FROM y
WHERE sem_check(y.diem,sysdate+365) = 1;
--
BEGIN
FETCH cc into x;
EXIT when cc%NOTFOUND;
INSERT INTO Occr_lezione
VALUES (
x.Codice_corso,
x.Nome_modulo,
x.diem,x.giorno_lezione,
x.Ora_inizio_lezione,
to_date(to_char(next_day(sysdate,x.Giorno_lezione),'DD-MM-YYYY') || to_char(x.Ora_inizio_lezione,' hh24:mi'),'dd-mm-yyyy hh24:mi'),
to_date(to_char(next_day(sysdate,x.Giorno_lezione),'DD-MM-YYYY') || to_char(x.Ora_fine_lezione,' hh24:mi'),'dd-mm-yyyy hh24:mi'),
x.nome_sede,
0,
x.loc
);
END LOOP;
END;
/
But of course it won't work, because the variable x has the type of my initial table row, which has less columns then my selection. Unfortunately As far as I know a rowtype variable is needed to cycle trough a cursor, in order to fetch data from it. Can you see the contradiction? How can I change the code? Is there a certain type of variable which can be crafted to reflect a row from my query result? Or maybe a way to cycle trough the data in the cursor without using a support variable? Or maybe something entirely different? Please let me know.
Ok, so as suggested I tried something like this:
INSERT INTO Occr_lezione(
Codice_corso,
Nome_modulo,
Data_inizio_ed_modulo,
Giorno_lezione,
Ora_inizio_lezione,
Ora_fine_lezione,
Anno,
Id_cdl,
Nome_sede,
Locazione_modulo
)
WITH y as(
SELECT Codice_corso,
Nome_modulo,
Data_inizio_ed_modulo,
Giorno_lezione,
Ora_inizio_lezione,
Ora_fine_lezione,
Anno,
Id_cdl,
Nome_sede,
Locazione_modulo
FROM Lezione
join ( select Id_cdl, Anno, Codice_corso from Corso ) using (codice_corso)
join ( select Codice_corso, Locazione_modulo from Modulo ) using (Codice_corso)
join ( select Nome_sede, Id_cdl from Cdl ) using (id_cdl)
WHERE
case
when extract (month from Data_inizio_ed_modulo) < 9 then extract (year from Data_inizio_ed_modulo) - 1
else extract (year from Data_inizio_ed_modulo)
end = extract (year from sysdate+365)
)
SELECT *
FROM y
WHERE sem_check(y.Data_inizio_ed_modulo,sysdate+365) = 1;
END;
/
But it says PL/SQL: ORA-00904: "LOCAZIONE_MODULO": invalid identifier
which isn't true, because the query return a table in which such column is present... am I missing something?
The code is compiled with no errors, it occurs when I try to fire the procedure.
In the table Occr_lezione as you can see:
CREATE TABLE Occr_lezione (
Codice_corso varchar2(20) NOT NULL,
Nome_modulo varchar2(50) NOT NULL,
Data_inizio_ed_modulo date NOT NULL,
Giorno_lezione number(1) NOT NULL,
Ora_inizio_lezione date NOT NULL,
Data_inizio_occr_lezione date,
Data_fine_occr_lezione date NOT NULL,
Nome_sede varchar2(30) NOT NULL,
Num_aula varchar2(3) NOT NULL,
Tipo_aula varchar2(20) NOT NULL,
--
CONSTRAINT fk_Occr_lezione_lezione FOREIGN KEY (Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione) REFERENCES Lezione(Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione) ON DELETE CASCADE,
CONSTRAINT fk_Occr_lezione_aula FOREIGN KEY (Nome_sede,Num_aula,Tipo_aula) REFERENCES Aula(Nome_sede,Num_aula,Tipo_aula) ON DELETE SET NULL,
CONSTRAINT pk_Occr_lezione PRIMARY KEY (Codice_corso,Nome_modulo,Data_inizio_ed_modulo,Giorno_lezione,Ora_inizio_lezione,Data_inizio_occr_lezione),
CHECK ( trunc(Data_inizio_occr_lezione) = trunc(Data_fine_occr_lezione) ), -- data inizio = data fine // prenotazione giornaliera
CHECK ( Data_inizio_occr_lezione < Data_fine_occr_lezione ) -- ora inizio < ora fine // coerenza temporale
there is not a column named Locazione_modulo, however the last column Tipo_aula as the same type and size of Locazione modulo :
CREATE TABLE Modulo (
Codice_corso varchar2(20) NOT NULL,
Nome_modulo varchar2(50),
Locazione_modulo varchar2(20) NOT NULL,
--
CONSTRAINT fk_Modulo_Corso FOREIGN KEY(Codice_corso) REFERENCES Corso(Codice_corso) ON DELETE CASCADE,
CONSTRAINT pk_Modulo PRIMARY KEY(Codice_corso,Nome_modulo),
CHECK (Locazione_modulo IN ('Aula','Laboratorio','Conferenze'))
);
So it should be irrelevant, right?
If you really want to use explicit cursors, you can declare x to be of type cc%rowtype
CREATE OR REPLACE PROCEDURE Pop_occ_lezione
AS
CURSOR cc IS ...
x cc%rowtype;
...
Unless you are using explicit cursors because you want to be able to explicitly fetch the data into local collections that you can leverage later on in your procedure, code using implicit cursors tends to be preferrable. That eliminates the need to FETCH and CLOSE the cursor or to write an EXIT condition and it implicitly does a bulk fetch to minimize context shifts.
BEGIN
FOR x IN cc
LOOP
INSERT INTO Occr_lezione ...
END LOOP;
END;
Of course, in either case, I would hope that you'd choose more meaningful names for your local variables-- x and cc don't tell you anything about what the variables are doing.
If all you are doing is taking data from one set of tables and inserting it into another table, it would be more efficient to write a single INSERT statement rather than coding a PL/SQL loop.
INSERT INTO Occr_lezione( <<column list>> )
SELECT <<column list>>
FROM <<tables you are joining together in the cursor definition>>
WHERE <<conditions from your cursor definition>>
I have a table called film:
CREATE TABLE film (
film_id NUMBER(5) NOT NULL,
title varchar2(255),
description varchar2(255),
release_year NUMBER(4) DEFAULT NULL,
language_id NUMBER(3) NOT NULL,
original_language_id NUMBER(3) DEFAULT NULL,
rental_duration NUMBER(3) DEFAULT 3 NOT NULL,
rental_rate NUMBER(4,2) DEFAULT '4.99',
length NUMBER(5) DEFAULT NULL,
replacement_cost NUMBER(5,2) DEFAULT '19.99' NOT NULL,
rating varchar2(8) DEFAULT 'G',
special_features varchar2(255) DEFAULT NULL
);
And I tried to make a trigger that increased rental rates of films depending on the special feature it has (can only have one). And I came up with the following trigger (for oracle and it compiles with no error)
CREATE OR REPLACE TRIGGER INCREASE_RENTAL_RATE
BEFORE INSERT ON FILM
FOR EACH ROW
DECLARE
change number(4,2);
BEGIN
DBMS_OUTPUT.put_line('mytrigger STARTING');
IF (:NEW.SPECIAL_FEATURES = 'Trailers') THEN
change := 0.1;
DBMS_OUTPUT.put_line('Trailer Read');
END IF;
IF (:NEW.SPECIAL_FEATURES = 'Commentaries') THEN
change := 0.5;
DBMS_OUTPUT.put_line('Commentary read');
END IF;
IF (:NEW.SPECIAL_FEATURES = 'Deleted Scenes') THEN
change := 0.2;
DBMS_OUTPUT.put_line('DS read');
END IF;
IF (:NEW.SPECIAL_FEATURES = 'Behind the Scenes') THEN
change := 0.2;
DBMS_OUTPUT.put_line('BS read');
END IF;
DBMS_OUTPUT.put_line('new=' || :NEW.rental_rate);
UPDATE FILM
SET FILM.RENTAL_RATE = FILM.RENTAL_RATE + change
WHERE FILM.FILM_ID = :NEW.FILM_ID;
DBMS_OUTPUT.put_line('new=' || :NEW.rental_rate);
DBMS_OUTPUT.put_line('mytrigger FINISHED');
END;
Since it compiles with no errors I don't know where to look for problems, but it's not working, I used a dummy example:
INSERT INTO FILM VALUES(20001, 'SUPREMO BORROWERS', 'An exasdafaf', 1978, 1, NULL, 5, '2.99', 52, '11.99', 'PG-13', 'Trailers');
In it, the rental rate is 2.99 and it continues to be 2.99 when I select the film with film_id = 20001...
Here is the output received:
"mytrigger STARTING
Trailer Read
new=2.99
new=2.99
mytrigger FINISHED
1 row(s) inserted."
Which means it knows it had a trailer but the update isn't working
Can anybody help me understand why?
Thanks :)
This is a before trigger, and you are attempting to directly update the row which is about to be inserted / updated in the table (which might not exist yet, and if it did, would likely be overwritten again by the actual update). Instead of the table update, simply change the value of the new pseudo row:
:new.RENTAL_RATE := :new.RENTAL_RATE + change;
Edit
As per below comments, the above didn't work for the OP in this case. However, the following did:
SELECT :new.RENTAL_RATE + change INTO :new.RENTAL_RATE FROM DUAL;