SQL Oracle Select, Count, and Inner Join - oracle

Having issues creating a query that displays the customerID, customerFirName, and customerZip from the customers table, based on when the customer has purchased more than one vehicle within the sales table. Here are the table creations:
CREATE TABLE CUSTOMERS
(customerID INT PRIMARY KEY,
customerFirName VARCHAR(20) NOT NULL,
customerLasName VARCHAR(20) NOT NULL,
customerMiName VARCHAR(1) NOT NULL,
customerStreet VARCHAR(40) NOT NULL,
customerState VARCHAR(15) NOT NULL,
customerCity VARCHAR(20) NOT NULL,
customerZip VARCHAR(15) NOT NULL);
CREATE TABLE SALES
(saleID INT PRIMARY KEY,
grossSalePrice DECIMAL(9,2),
vehicleStatus VARCHAR(10) NOT NULL CHECK (lower(vehicleStatus) IN ('available', 'sold', 'pending')),
saleDate DATE,
saleMileage INT,
customerID INT,
salespersonID INT,
vehicleVIN VARCHAR(25),
CONSTRAINT SALES_FK1 FOREIGN KEY (customerID) REFERENCES CUSTOMERS(customerID);
Here is the desired output:
customerID customerFirName customerZip Number_of_Sales
1 Bob 12345 2
2 Jim 94949 3
3 Tom 99330 4
Here is what I've tried....I'm having issues creating a single SELECT statement that has an inner join to combine the SALES.customerID field on the CUSTOMERS.customerID field. Where am I going wrong? Thanks!
SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip, COUNT(SALES.customerID)
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY SALES.customerID
HAVING COUNT(SALES.customerID) > 1;
AND
SELECT COUNT (CUSTOMERS.customerID), customerFullName, customerZip
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY SALES.customerID
HAVING COUNT(SALES.customerID) > 1;

I guess the issus is on group by field.
SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip,
COUNT(SALES.customerID)
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip
HAVING COUNT(SALES.customerID) > 1;

Maybe just do a count in a sub select:
select * from
(SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip, COUNT(*) num_sales
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip)
where num_sales > 1;

Related

How to execute trigger and procedure in oracle

I am working on my Project 'Supermarket Billing Management System' since I am a beginner I m facing a lot of issues while making project. Here I've already created a trigger and a procedure but I don't know how I can execute it, I've created a trigger for a total price of a single Product i.e ProdTotal = ProdPrice * ProdQuantity;.
That means whenever a user enters some data in the Products table, then this trigger must get executed, but I don't know how to execute it, similarly, I've created a procedure to calculate the total price of all the products purchased by a single customer. Just like when you go to the supermarket or any store then after purchasing the items, you get a bill and there is the final total amount. I m not even exactly sure whether my procedure code is right or wrong, though it was created successfully, I m not sure whether it will give me the exact output which I want, So if you can help me then, please let me know, I've researched a lot from different websites and also from many youtube videos but seriously I am not getting how to solve it, so please help me!
Code:
Products table
create table Products
( ProdId number primary key,
ProdNum number not null unique,
ProdName varchar2(15),
ProdPrice int,
ProdQuantity int,
ProdCustId int references Customers,
ProdOrdId int references Orders,
ProdStoreId int references Stores
);
Payments table
create table Payments
( PayId int primary key,
PayDate date,
ProdTotal int,
FinalTotal int,
PayOrdId int references orders,
PayProdId int references Products,
PayCustId int references Customers
);
Trigger code
create trigger PROD_TOTAL
AFTER INSERT ON Products
BEGIN
UPDATE Payments
SET ProdTotal = (SELECT Products.ProdPrice * Products.ProdQuantity FROM Products);
END;
/
Procedure code
create procedure FINAL_TOTAL(C IN NUMBER, T OUT NUMBER)
IS
BEGIN
UPDATE Payments
SET FinalTotal = FinalTotal + ProdTotal
WHERE PayCustId = C;
Commit;
SELECT FinalTotal into T FROM Payments WHERE PayCustId = C;
END;
/
Insert statement in Product table:
insert into Products values(1,1001,'Syrup',30,4,1,1,1);
Insert statements in Payments table:
insert into Payments(PayId, PayDate, PayOrdID, PayProdId, PayCustId)
values(1,date'2020-10-07',1,1,1);
Output:
select * from products;
PRODID PRODNUM PRODNAME PRODPRICE PRODQUANTITY PRODCUSTID
---------- ---------- --------------- ---------- ------------ ----------
PRODORDID PRODSTOREID
---------- -----------
1 1001 Syrup 30 4 1
1 1
select * from Payments;
PAYID PAYDATE PRODTOTAL FINALTOTAL PAYORDID PAYPRODID PAYCUSTID
---------- --------- ---------- ---------- ---------- ---------- ----------
1 07-OCT-20 1 1 1
Now here, as you can see PRODTOTAL and FINALTOTAL column is blank, I know why it is blank because I didn't enter any value. And the reason why I didn't enter any value in these two columns is that I want, the system should automatically calculate that calculation with the help of trigger and procedure and, I can't even remove trigger and procedure because it's mandatory in our project to use both of these concepts. So please help me!!!
As already proposed, first try to get the design right with respect to your requirements. You can implement many constraints just by designing correctly your database schema.
Stay away from triggers and PL/SQL for as long as possible. It will force you to better design in the end and will pay off.
Before using triggers for business logic, try to use views for things that can be selected. That's what the database is for.
When you are "done", test for the performance and if it's suboptimal, improve your schema. If nothing helps, start using triggers for business logic.
I've put together a sample with views I am talking about. I hope it can get you started.
create table Products (
ProdId number generated always as identity primary key
, ProdName varchar2(20) not null
);
create table Stores (
StoreId number generated always as identity primary key
, StoreName varchar2(20) not null
);
create table Customers (
CustomerId number generated always as identity primary key
, CustomerName varchar2(20) not null
);
create table Prices (
PriceId number generated always as identity primary key
, ProdId number not null
, Price number
, ValidFrom date default on null sysdate
, constraint fk_Prices_Product foreign key (ProdId) references Products (ProdId)
);
create unique index uniq_prices_product_price on Prices (ProdId, ValidFrom);
create table Orders (
OrderId number generated always as identity primary key
, CustomerId number not null
, StoreId number not null
, OrderedAt date default on null sysdate
, constraint fk_Orders_Customer foreign key (CustomerId) references Customers (CustomerId)
, constraint fk_Orders_Store foreign key (StoreId) references Stores (StoreId)
);
create table OrderLines (
OrderLineId number generated always as identity primary key
, OrderId number not null
, ProdId number not null
, ProdQuantity number not null
, constraint fk_OrderLines_Order foreign key (OrderId) references Orders (OrderId)
, constraint fk_OrderLines_Prod foreign key (ProdId) references Products (ProdId)
);
create table Payments (
PaymentId number generated always as identity primary key
, OrderId number not null
, PaidAt date default on null sysdate
, PaidAmount number not null
, constraint fk_Payments_Order foreign key (OrderId) references Orders (OrderId)
);
create view Prices_V as
select
p.*
, coalesce(
lead(p.ValidFrom) over (partition by p.ProdId order by p.ValidFrom)
, to_date('9999', 'YYYY')
) ValidTo
from Prices p;
create view Orders_V as
select
o.*
, (
select sum(ol.ProdQuantity * p.Price)
from OrderLines ol
join Prices_V p on (p.ProdId = ol.ProdId and o.OrderedAt between p.ValidFrom and p.ValidTo)
where o.OrderId = ol.OrderId
) Total
, (
select sum(PaidAmount)
from Payments p
where p.OrderId = o.OrderId
) TotalPaid
from Orders o;
insert into Products(ProdName)
select 'Prod A' from dual union all
select 'Prod B' from dual;
insert into Stores(StoreName) values ('Store A');
insert into Customers(CustomerName)
select 'Customer A' from dual union all
select 'Customer B' from dual;
insert into Prices(ProdId, Price, ValidFrom)
select 1, 10, sysdate - 10 from dual union all
select 1, 12, sysdate - 2 from dual union all
select 1, 14, sysdate + 3 from dual union all
select 2, 100, sysdate - 10 from dual union all
select 2, 90, sysdate - 2 from dual union all
select 2, null, sysdate + 5 from dual;
insert into Orders(CustomerId, StoreId, OrderedAt)
select 1 cid, 1 stoid, sysdate - 5 from dual union all
select 2, 1, sysdate - 5 from dual union all
select 2, 1, sysdate - 1 from dual;
insert into OrderLines(OrderId, ProdId, ProdQuantity)
select 1 ordid, 1 prodid, 3 prodquant from dual union all
select 1, 2, 2 from dual union all
select 2, 2, 10 from dual union all
select 3, 2, 10 from dual;
insert into Payments(OrderId, PaidAmount) values (2, 500);
select * from Prices_V order by ProdId, ValidFrom;
select * from OrderLines order by OrderId, ProdId;
select * from Orders_v order by OrderId;
Some of the ideas in there:
Prices are stored in separate table, reference the product and have validity so that product price can change over time. Price view have ValidTo column added so it's easier to work with
There is a unique index on Prices so that we cannot have 2 prices for the same product at the same time
You can have many items in order, so that's why there is Orders and OrderLines tables in 1-to-many relationship
In Order_V the total paid is shown (using a subquery on Payments) and the total order values is shown (using a subquery on OrderLines and Prices, date of order is used to get prices form the correct period)
Based on the schema you will se what things you can represent and which you cannot. It's your job to make it match your requirements :)
And now I've come to the point you say triggers and procedures are mandatory in your project. Hence I have a proposal:
Create a procedure that will allow users to create new price for a product. It should definitely check that the validity does not start in the past. Then implement another one that allows for changing the valid to date (also cannot end in the past). You can than revoke any insert/update privileges on Products table and force users to use your procedures that will contain this business logic.
Create a table PricesLog and trigger on Prices that will insert the PriceId, old.Price, new.Price, sysdate and User to the log on any inserts/updates to the prices table.

Oracle : It give a error in a dynamic view

I have two table one is employee and one is department. I am creating the dynamic view that will rank all departments by salary. The view should pull information from Department and Employee, sum the salary by department, and rank the department by salary.
CREATE TABLE DEPARTMENT
(DEPARTMENT_ID NUMBER PRIMARY KEY,
DEPARTMENT_NAME VARCHAR(30) NOT NULL
);
CREATE TABLE JOBS
(JOB_ID NUMBER PRIMARY KEY,
JOB_TITLE VARCHAR(35) NOT NULL,
MIN_SALARY DECIMAL NOT NULL,
MAX_SALARY DECIMAL NOT NULL
);
CREATE TABLE EMPLOYEES
(EMPLOYEE_ID NUMBER PRIMARY KEY,
FIRST_NAME VARCHAR(20) NOT NULL,
LAST_NAME VARCHAR(25) NOT NULL,
EMAIL VARCHAR(25) NOT NULL,
PHONE_NUMBER VARCHAR(20) NOT NULL,
HIRE_DATE DATE NOT NULL,
JOB_ID NUMBER NOT NULL,
SALARY DECIMAL NOT NULL,
DEPARTMENT_ID NUMBER NOT NULL,
CONSTRAINT emp_job_fk FOREIGN KEY(JOB_ID) REFERENCES JOBS(JOB_ID),
CONSTRAINT emp_department_fk FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(DEPARTMENT_ID)
);
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(1,'IT');
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(2,'Sales');
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (1,'IT Administrator',250000.00,50000.00);
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (2,'Salesman',200000.00,40000.00);
Here is I create so far but it give me a error
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 4 Column: 9
Here is my code
select department_id,department_name,total_salary
from(
select department_id,department_name, SALARY, count(*) as total_salary from(
select dep.department_id , dep.department_name ,emp.SALARY,
DENSE_RANK() OVER (PARTITION BY department_name ORDER BY salary)
from departments dep
inner join employees emp on dep.DEPARTMENT_ID = emp.DEPARTMENT_ID
)
GROUP BY SALARY)
Your query needs to join EMPLOYEES (to get the salaries) to DEPARTMENT (to get the DEPARTMENT_NAME). Calculate the total salary for each department by summing the employee salaries, not counting them. The GROUP BY needs to include the non-aggregated columns.
Then you need to rank the departments by the total salary per department. This query ranks the departments with highest salary = 1. It uses a left join to cater for departments with no employees.
select department_id
, department_name
, total_salary
, rank() over (order by total_salary desc) as dept_rank
from (
select d.department_id
, d.department_name
, sum(e.SALARY) as total_salary
from department d
left join employees e
on e.department_id = d.department_id
group by d.department_id
, d.department_name
)
/

Trying to collect data from multiple tables with logic aritmethics

I am trying to collect data from multiple tables with logic aritmethics like: OR AND NOT UNION INTERSECT.
I have 3 tables called: Customers, Flights, Booking.
My aim is to get all the passportno from the customers table which took a flight more than once.
Thnaks for helpers.
Use group by and having clauses. The having limits those rows that are count>1
Schema
create table bookings
(
id int auto_increment primary key,
passportno int not null,
flightnumber int not null,
`day` date not null,
row varchar(10) not null,
seat varchar(10) not null
);
insert bookings(passportno,flightnumber,`day`,row,seat) values (1,2,'2005-01-01',1,1); -- Joe
insert bookings(passportno,flightnumber,`day`,row,seat) values (1,3,'2005-02-01',1,1); -- Joe
insert bookings(passportno,flightnumber,`day`,row,seat) values (2,3,'2005-02-01',2,2); -- Sally
create table customers
(
passportno int primary key,
name varchar(100) not null,
address varchar(100) not null,
phone varchar(20) not null
);
insert customers(passportno,name,address,phone) values (1,'Joe','addr','ph');
insert customers(passportno,name,address,phone) values (2,'Sally','addr','ph');
Query and results
select c.passportno
from customers c
join bookings b
on c.passportno=b.passportno
group by c.passportno
having count(b.id)>1;
+------------+
| passportno |
+------------+
| 1 |
+------------+
Mysql manual page entitled MySQL Handling of GROUP BY
If I understand the question the query should be something like
SELECT c.PASSPORTNO
FROM CUSTOMERS c
WHERE c.PASSPORTNO IN (SELECT DISTINCT PASSPORTNO
FROM (SELECT PASSPORTNO, FLIGHTNUMBER, COUNT(*)
FROM BOOKINGS
GROUP BY PASSPORTNO, FLIGHTNUMBER
HAVING COUNT(*) > 1)
Best of luck.

Return all null values between two dates

I have the following query and I need it to return all the null values between those two dates.
select cust_first_name
from customers
join orders using(customer_id)
where order_date between (to_date('01-01-2007','DD-MM-YYYY'))
and (to_date('31-12-2008','DD-MM-YYYY'));
Sounds like what you want is customers with no orders within the given date range. The join you are using finds the opposite of that.
You could do this with an outer join, in which case you need to apply the date filter prior to the join. It's probably easier and more readable to use a NOT IN or NOT EXISTS subquery:
select cust_first_name
from customers
WHERE customers.customer_id NOT IN (
SELECT orders.customer_id from orders
where order_date between (to_date('01-01-2007','DD-MM-YYYY'))
and (to_date('31-12-2008','DD-MM-YYYY'))
)
Here is an example of how to do what you want.
The key part is doing a left join on your orders table, and then simply doing a not between date1 and date2
declare #customers table (
id int identity(1,1),
first_name nvarchar(50),
last_name nvarchar(50)
)
declare #orders table (
id int identity(1,1),
customer_id int,
order_date datetime
)
insert into #customers(first_name, last_name) values ('bob', 'gates')
insert into #customers(first_name, last_name) values ('cyril', 'smith')
insert into #customers(first_name, last_name) values ('harry', 'potter')
insert into #orders(customer_id, order_date) values (1, '2007-02-01')
insert into #orders(customer_id, order_date) values (2, '2015-02-15')
insert into #orders(customer_id, order_date) values (3, '2008-02-15')
select
customers.id
,customers.first_name
,customers.last_name
from #customers customers
left join #orders orders on orders.customer_id = customers.id
where orders.id is null
or orders.order_date not between ('2007-01-01') and ('2008-12-31')
group by
customers.id
,customers.first_name
,customers.last_name;

Create View inner join error

I'm creating a view and using inner join and receiving the following error:
ORA-00904: "B"."CUSTOMERNO": invalid identifier
This is the code I'm working with to create view and inner join
CREATE VIEW RentalInfoOct
(branch_no, branch_name, customer_no)
AS
SELECT b.branchNo, b.branchName, b.customerNo, c.customerNo
FROM branch b
INNER JOIN
customer c
ON b.customerNo = c.customerNo
Here are the create table commands as well.
CREATE TABLE Branch
(
branchNo SMALLINT NOT NULL,
branchName VARCHAR(20) NOT NULL,
branchAddress VARCHAR(40) NOT NULL,
PRIMARY KEY (BranchNo)
);
CREATE TABLE Customer
(
customerNo SMALLINT NOT NULL,
customerName VARCHAR(15) NOT NULL,
customerAddress VARCHAR(40) NOT NULL,
customerTel VARCHAR(10),
PRIMARY KEY (CustomerNo)
);
CREATE VIEW RentalInfoOct
(branch_no, branch_name, customer_no)
AS
SELECT b.branchNo, b.branchName, b.customerNo, c.customerNo
You specify 3 columns for the view, but you select 4 columns in SELECT

Resources