I have a PLSQL Procedure that receives
FUNCTION VALIDATE_UPDATE_REQUEST (P_Orderno In Ord_Head.Order_No%Type,
P_First_Name In Ord_Address.First_Name%Type,
P_Last_Name In Ord_Address.Last_Name%Type,
P_Address In Ord_Address.Add_1%Type,
P_Mobile In Ord_Address.Phone_No%Type)
First Name, Last Name, Address, Mobile, Order No
The four parameters: First Name, Last Name, Address, Mobile
can be NULL or cannot be NULL
If they are NOT NULL, I need to update the table with whatever is not null in the parameters
How do I do that?
eg:
First Name: Robert
Last Name: NULL
Mobile: NULL
Add1 : First Street, 20A
Based on this, I only need to update First Name and Add1 in the table Ord_Address
Ummmm ... no, you don't have a function. That should be a procedure as functions can't do DML operations.
Other than that, procedure would have something like this:
update your_table set
first_name = nvl(p_first_name, first_name),
last_name = nvl(p_last_name, last_name),
address = nvl(p_address, address),
mobile = nvl(p_mobile, mobile)
where orderno = p_orderno;
Related
(Beginner)
Q1) How to exclude null value from Oracle Inner Join Statement Result. i.e. to display only that row which has all the values (i.e. no null data in any of the attribute)
Q2) How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table
Below is the dummy example in oracle sql
Created 4 Tables i.e.,
name , details , social , address
`create table name
(
id number,
firstname varchar2(20),
lastname varchar2(20)
);
create table details
(
id number,
dob_month varchar2(20),
dob_day varchar2(20),
dob_year varchar2(20)
);
create table social
(
id number,
ssn varchar2(20),
telephone number
);
create table address
(
id number,
address varchar2(20)
);
`
Now insert dummy data into the above tables
`insert into name values (1, 'Will' , 'Smith');
insert into name values (2, 'Barry' , 'White');
insert into name values (3, 'Tom' , 'Jones');
insert into name values (4, 'Rod' , 'Stewart');
insert into name values (5, 'Elvis' , 'Presley');
insert into details values (1,'May',31,null);
insert into details values (2,'August',22,1980);
insert into details values (3,'October',null,1973);
insert into details values (4,'January',30,1980);
insert into details values (5,'March',11,1980);
insert into social values (1,'123-45-6789',null);
insert into social values (2,'222-45-5555',789456123);
insert into social values (3,'333-45-7777',888888888);
insert into social values (4,null,693456741);
insert into social values (5,'999-45-1111',null);
insert into address values (null, null);
insert into address values (2, '12th street');
insert into address values (null, null);
insert into address values (4, '14th Avenue');
insert into address values (5, null);`
Q1 Pictorial Explanation
For Q1,
I tried with below oracle sql query (both returns the same result) but not able to figure out the exact query which will exclude null value from Oracle Inner Join Statement Result and display only that row which has all the values (i.e. not null)
`select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name, details, social, address
where name.id=details.id
and details.id=social.id
and social.id=address.id;
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id;`
For Q2, I am looking for sample Query
i.e. How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table
For the first question, I think you are looking for this:
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name
join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id
where dob_year is not null
and dob_day is not null
and telephone is not null
and ssn is not null
and address is not null;
Or a bit shorter
...
where COALESCE(dob_year, dob_day, telephone, ssn, address) is not null;
For Q2 it would be this
select *
from address
where id is null
or address is null;
Some more notes:
It is a poor design to store date parts and then even localized strings. You should never do that. In your case it should be
create table details
(
id number,
dob_date DATE
);
Then next question, why do you create four tables? Maybe you learned at school about database normalization but you have taken too literally. Can a person have more than one SSN/telephone? If not, then add these columns to name table (and maybe rename it to person) instead of a separate table. The same question applies to table address.
Hello fellow programmers. Im currently working on a webshop database for my studying program. Currently im trying to make a procedure which creates an order in the orders table for a customer when he/she/it is beeing created. I am also thinking of putting this into a constructor but since i want to use this functionality twice once when the order reaches a certain status and after creation i want to bundle this functionality in a procedure. I have spend nearly 8 hours of research and testing on this but since the feedback from oracle db on my code is 0 to nothing i cant figure out what is wrong. When i create the procedure it is not flagged as valid and i cant even see the parameters in the parameters tab when i click on the procedure. I hope the code formatting works this is my first post..
This are the types order and Customer which hold a REF to each other
CREATE TYPE ORDER_TYPE AS OBJECT(
Order_Id NUMBER,
Date_of_Creation DATE,
Items ITEM_LIST,
Status REF STATUS_TYPE,
Customer REF CUSTOMER_TYPE
);
CREATE TYPE CUSTOMER_TYPE AS OBJECT(
Customer_Id NUMBER,
Email VARCHAR2(254),
User_Name VARCHAR2(50),
Password VARCHAR2(20),
First_Name VARCHAR2(50),
Last_Name VARCHAR2(50),
Address ADDRESS_TYPE,
Shopping_Cart REF ORDER_TYPE
);
CREATE TABLE Orders OF ORDER_TYPE(Status SCOPE IS Order_Status NOT NULL, Customer NOT NULL)
NESTED TABLE Items STORE AS ORDER_ITEMS_NT_TAB;
ALTER TABLE Orders ADD CONSTRAINT PK_Orders PRIMARY KEY(Order_Id);
CREATE TABLE Customers OF CUSTOMER_TYPE(Customer_Id PRIMARY KEY,
Email NOT NULL,
User_Name NOT NULL,
Password NOT NULL,
First_Name NOT NULL,
Last_Name NOT NULL,
Address NOT NULL);
This is the procedure code. The input should be the customer created or updated. Then i want to insert a new order, i still have to change the id field to guid or uuid so every order will be unique but for testing purpose i just used 1. The item list should be empty at first and the status of the order should be status 1 which stands for "Shopping_Cart" this means that the order is still beeing created and should be displayed as shopping cart in the browser later on. After the insert i want to return the inserted row with the returning into statement so i cant update the customer and set the ref of his shopping cart to the new inserted order. I cant figure out whats wrong im still working on it but i would be greatful for any help.
CREATE PROCEDURE create_customer_order(customer IN CUSTOMER_TYPE) AS
DECLARE
shopping_c NUMBER;
BEGIN
INSERT INTO ORDERS
VALUES(1,CURRENT_DATE ,NEW ITEM_LIST(),(SELECT REF(os) FROM ORDER_STATUS os WHERE VALUE(os).STATUS_ID = 1),REF(customer))
RETURNING Order_Id INTO shopping_c;
UPDATE CUSTOMERS c
SET c.SHOPPING_CART = (SELECT REF(o) FROM ORDERS o WHERE o.ORDER_ID = shopping_c)
WHERE c.CUSTOMER_ID = customer.CUSTOMER_ID;
END;
Feel free to ask questions if something is not clear. Cheers!
I will include a working example.
But first, you did not provide all types. So i assumed them.
I see you have types that refer to eachother. This is basically not a good idea.
For storing data, you could use object types, but you could also use normal data types like number/varchar2. You would need tables orders/order_items/customers. If you want to make changes to the customer_type and your table is already populated with data, changing the type is difficult (what to do with the old data?).
But to come back at your question, here is a working example.
drop type customer_type force;
drop type order_type force;
drop type address_Type force;
drop type status_type force;
drop type item_list force;
create type STATUS_TYPE as object (
status number
);
create type ADDRESS_TYPE as object (
street varchar2(100)
);
create type ITEM_LIST as object (
itemname varchar2(100)
);
CREATE TYPE ORDER_TYPE AS OBJECT(
Order_Id NUMBER,
Date_of_Creation DATE,
Items ITEM_LIST,
Status REF STATUS_TYPE,
Customer REF CUSTOMER_TYPE
);
CREATE TYPE CUSTOMER_TYPE AS OBJECT(
Customer_Id NUMBER,
Email VARCHAR2(254),
User_Name VARCHAR2(50),
Password VARCHAR2(20),
First_Name VARCHAR2(50),
Last_Name VARCHAR2(50),
Address ADDRESS_TYPE,
Shopping_Cart REF ORDER_TYPE
);
alter type order_type compile;
drop table orders;
CREATE TABLE Orders (id number, Status status_type , Customer CUSTOMER_TYPE);
CREATE OR REPLACE PROCEDURE create_customer_order(p_customer IN CUSTOMER_TYPE) AS
shopping_c NUMBER;
BEGIN
dbms_output.enable(null);
INSERT INTO ORDERS (id, status, customer)
VALUES (1, null, p_customer)
RETURNING id INTO shopping_c;
commit;
dbms_output.put_line('id='||shopping_c);
END;
/
--Test
declare
l_customer customer_type;
begin
l_customer := customer_type (Customer_Id => 1
, email=>'a#b.org'
, user_name=>'test'
, password=>'DoyouReallyWantThis'
, first_name=>'first'
, last_name =>'last'
, address=>null --for simplicity
, shopping_cart=>null --for simplicity
);
create_customer_order(p_customer => l_customer);
end;
/
thanks for your answer.. It did not work for me and you also missed the update part in the procedure but still thank you for the effort. i managed to get the procedure working by running every code piece step by step and watching if it fails. Feels like javascript to me lol. I discovered for some reason i cant explain that you cant declare variables under the declare statement and you cant use defined object types as parameters. i could not find anything about it in the documentation. to everyone still interested how i solved the problem this is the code.
CREATE OR REPLACE PROCEDURE create_customer_order(p_customer_id IN NUMBER)
AS
BEGIN
INSERT INTO ORDERS
VALUES(1, CURRENT_DATE, NEW ITEM_LIST(),(SELECT REF(os) FROM ORDER_STATUS os WHERE os.STATUS_ID = 1),(SELECT REF(c) FROM CUSTOMERS c WHERE c.CUSTOMER_ID = p_customer_id));
UPDATE CUSTOMERS c
SET c.SHOPPING_CART = (SELECT REF(o) FROM ORDERS o WHERE DEREF(o.Customer).Customer_Id = p_customer_id AND DEREF(o.STATUS).Status_Id = 1 )
WHERE c.CUSTOMER_ID = p_customer_id;
END;
I'm getting more and more experienced with oracle pl/sql but this problem seems to be persistent: I have a procedure that merges external data into a table in the database that looks something like this:
PROCEDURE updateTable (ts DATE, val NUMBER, id NUMBER)
BEGIN
IF id NOT IN (15, 16, 23)
THEN
MERGE INTO myTable dest
USING (SELECT ts, val, id FROM Dual) src
ON (src.id = dest.id AND src.ts = dest.ts)
WHEN MATCHED THEN UPDATE SET dest.val = src.val
WHEN NOT MATCHED THEN INSERT (ts, val, id) VALUES (src.ts, src.val, src.id);
END IF;
END;
This works just fine so far. Now the problem is that the list of id's that are excluded is hardcoded and it would be much more dynamic to have those in another table, i.e. in the code above replace the line
IF id NOT IN (15, 16, 23)
with something like
IF id NOT IN (SELECT id FROM excluTable)
which returns the notorious error: PLS_00405: subquery not allowed in this context
If it was only one id, I could simply create a variable and select the id into it. Unfortunately it's quite a long list. I've tried to bulk collect them into an array but then I don't find a way to put that into the conditional clause either. I'm sure there is an elegant solution for this.
Thanks for your help!
There may be many IDs in your exclusion table, but you are only passing one into the procedure. You can see if that single value exists in the table with a count into a local variable, and then check whether the count was zero or non-zero; something like:
PROCEDURE updateTable (ts DATE, val NUMBER, id NUMBER) IS
l_excl_id PLS_INTEGER;
BEGIN
SELECT count(*)
INTO l_excl_id
FROM excluTable
WHERE excluTable.id = updateTable.id;
IF l_excl_id = 0
THEN
MERGE INTO myTable dest
USING (SELECT ts, val, id FROM Dual) src
ON (src.id = dest.id AND src.ts = dest.ts)
WHEN MATCHED THEN UPDATE SET dest.val = src.val
WHEN NOT MATCHED THEN INSERT (ts, val, id) VALUES (src.ts, src.val, src.id);
END IF;
END;
Incidentally, it can get confusing if your procedure argument names are the same as table column names, or other identifiers. For instance, as id is the procedure argument name and the column name in the table I've had to prefix them both:
WHERE excluTable.id = updateTable.id;
one with the table name (or alias if you add one), the other with the procedure name. If you just did
WHERE excluTable.id = id
then the scoping rules would mean it matched every ID in the table with itself, not the argument, so you would be counting all rows - and it might not be immediately obvious why it wasn't behaving as you expected. If the arguments were named as, say, p_ts and p_id then you wouldn't have to account for that ambiguity. That's also why I've prefixed my local flag variable with l_.
I've two table in the database, the first one is Person and the second is Pilot. as following:
Person Table:
CREATE TABLE person(
person_id NUMBER PRIMARY KEY,
last_name VARCHAR2(30) NOT NULL,
first_name VARCHAR2(30) NOT NULL,
hire_date VARCHAR2(30) NOT NULL,
job_type CHAR NOT NULL,
job_status CHAR NOT NULL
);
/
INSERT INTO person VALUES (1000, 'Smith', 'Ryan', '04-MAY-90','F', 'I');
INSERT INTO person VALUES (1170, 'Brown', 'Dean', '01-DEC-92','P', 'A');
INSERT INTO person VALUES (2010, 'Fisher', 'Jane', '12-FEB-95','F', 'I');
INSERT INTO person VALUES (2080, 'Brewster', 'Andre', '28-JUL-98', 'F', 'A');
INSERT INTO person VALUES (3190, 'Clark', 'Dan', '04-APR-01','P', 'A');
INSERT INTO person VALUES (3500, 'Jackson', 'Tyler', '01-NOV-05', 'F', 'A');
INSERT INTO person VALUES (4000, 'Miller', 'Mary', '11-JAN-08', 'F', 'A');
INSERT INTO person VALUES (4100, 'Jackson', 'Peter', '08-AUG-11', 'P','I');
INSERT INTO person VALUES (4200, 'Smith', 'Ryan', '08-DEC-12', 'F','A');
COMMIT;
/
Pilot Table:
CREATE TABLE pilot(
person_id NUMBER PRIMARY KEY,
pilot_type VARCHAR2(100) NOT NULL,
CONSTRAINT fk_person_pilot FOREIGN KEY (person_id)
REFERENCES person(person_id)
);
/
INSERT INTO pilot VALUES (1170, 'Commercial pilot');
INSERT INTO pilot VALUES (2010, 'Airline transport pilot');
INSERT INTO pilot VALUES (3500, 'Airline transport pilot');
COMMIT;
/
I'm asked to write a pl/sql block of code that accepts the last name from the user and return the result as following:
1) if the last name is not in the table, it returns all the rows in the table.
2) if the last name is in the table, it shows all of the employee's information.
So far I'm doing well with the code, but I got stuck in the case that there are two employees with the last name. here is the cursor that I wrote:
cursor person_info is
select last_name, first_name, hire_date, job_type, job_status, nvl(pilot_type, '-----------')
from person
full outer join pilot
on person.person_id = pilot.person_id
where upper(last_name) = upper(v_last_name)
group by last_name, first_name, hire_date, job_type, job_status, pilot_type
order by last_name, first_name, hire_date asc;
Logically, there are three cases to be covered:
the first case, when the entered last name is in the table, I return all the rows in the table and that's done.
The second case when there is only one employee with the entered last name, and this case is done as well. The last case when there are more than one employee having the same last name like for example 'Jackson' or 'Smith' in this case, my program crashes and give me the error that my select into statement returns more than one row.
select person_id
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
open person_info;
fetch person_info into v_last_name, v_first_name, v_hire_date, v_job_type, v_job_status, v_pilot_type;
Can someone help me in guiding me how to fetch the data correctly? I'm not allowed to create any temporary tables or views.
I'm so sorry for making the problem longer than it should, but I was trying to be as clear as possible in explaining the problem.
Thank you in advance.
if the error is
"ORA-01422 exact fetch returns more than requested number of rows" then I think your answer is here https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:981494932508
If you EXPECT the query to return more than one row, you would code:
for x in ( select * from t where ... )
loop
-- process the X record here
end loop;
Your immediate issue is that you're selecting the matching person_id into a variable, and then seeing if that specific ID is 1. You don't have an actual ID 1 anyway so that check would never match; but it is that querying matching multiple rows that gets the error, as you can't put two matching IDs into a single scalar variable.
The way you've structured it looks like you are trying to count how many matching rows there are, rather than looking for a specific ID:
select count(person_id)
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
....
When you do have multiple matches then you will need to use the same mechanism to return all of those as you do when there are no matches and you return all employees. You may find the logic should be in the cursor query rather then in PL/SQL logic. It depends on the details of the assignment though, and how it expects you to return the data in both (or all three) scenarios.
It's also possible you just aren't expected to hit this problem - it isn't clear if the assignment is finding all employees, or only those that are pilots. The issue still exists in general, but with the data you show there aren't any duplicate pilot last names. If you haven't learned about this kind of error yet perhaps you're getting a bit ahead of what your tutor expects.
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);