Creating a view with sums from multiple tables (Oracle SQL) - sql

I'm trying to create a view that gets the sums of a couple of different rows in various tables. (I'm not sure how to explain this properly)
Here is how my tables are set out:
Visitors:
VISITORID FNAME LNAME PHONE HOTELID
---------- --------------- --------------- --------------- ----------
23 Bella Morgan 0394110625 3
Bookings:
BOOKINGID HOTELID ROOMNO BOOKINGDATE BOOKINGDAYS BEDANDBREA VISITORID
---------- ---------- ---------- ------------------- ----------- ---------- ----------
28 3 509 28-04-2013 00:00:00 3 Yes 23
Rooms:
ROOMNO HOTELID ROOMTYPE PRICE
---------- ---------- ------------------------- ----------
509 3 Double 700
Services:
SERVICEID SERVICENAME COST HOTELID
---------- -------------------------------------------------- ---------- ----------
1-CLTH Cloth Cleaning 14.95 1
2-RMSV Room Service 9.95 2
Booking_services:
SERVICEID BOOKINGID
---------- ----------
2-RMSV 32
1-CLTH 32
I want to create a view called bills that gives me the total of room cost and cost of all services.
To get the room price, the sum is rooms.price*bookings.bookingdays.
For the services, it's the sum of all the rows in the services table that match the SERVICEID in booking_services for the matching bookingID.
Currently there are more rows in all of the tables than I've shown (so it doesn't take up too much space on here) and I have a query but it's only showing 2 of the visitors that i'd like the total for. I know it's because of line 5, but I'm not sure how I can get it to calculate that as well as those who do not have a row in booking_services.
Here is that query:
CREATE VIEW bills AS
SELECT v.fname, SUM((r.price*b.bookingdays)+s.cost) AS total
FROM visitors v, rooms r, bookings b, services s, booking_services bs
WHERE v.visitorid = b.visitorid
AND
s.serviceid in(select bs.serviceid from booking_services where bs.bookingid = b.bookingid)
AND
b.roomno = r.roomno
GROUP BY v.fname;
Any help to get what I'm after (if this makes any sense) would be appreciated.

Here is the SQLFiddel Demo
You can try below query for your view:
Select v.fname, sum((r.price*b.bookingdays)+nvl(bso.cost,0))
From visitors v
Join bookings b
on v.visitorid = b.visitorid
Join rooms r
on b.roomno = r.roomno
left outer join (select bs.BOOKINGID,sum(cost) as cost
from booking_services bs
Join services s
on s.SERVICEID = bs.SERVICEID
group by bs.BOOKINGID) bso
on bso.BOOKINGID = b.BOOKINGID
GROUP BY v.fname;

Related

Display the resortid, name of the resort ,booking count and the total amount collected in each resort

This is the query written by me:
select resort.resortid,
resort.resortname,
(count(booking.bookingid)) as "TOTALBOOKING",
booking.totalcharge as "TOTALAMOUNT"
from resort
join booking on booking.resortid=resort.resortid
group by resort.resortid,resort.resortname,booking.totalcharge
order by resort.resortid;
and output:
RESORTID RESORTNAME TOTALBOOKING TOTALAMOUNT
---------- ------------------------------ ------------ -----------
1001 TREE OF LIFE RESORT 1 30000
1001 TREE OF LIFE RESORT 1 48000
1002 PALETTE RESORTS 1 30000
1003 GRAND GRT 1 32000
1004 GREEN COCONUT 1 30000
Expected Output:
RESORTID RESORTNAME TOTALBOOKING TOTALAMOUNT
---------- ------------------------------ ------------ -----------
1001 TREE OF LIFE RESORT 2 78000
1002 PALETTE RESORTS 1 30000
1003 GRAND GRT 1 32000
1004 GREEN COCONUT 1 30000
How to add the count of totalbookings having same resortid ?
You can use the windowed version of count().
SELECT resort.resortid,
resort.resortname,
count(booking.bookingid) AS "TOTALBOOKING",
booking.totalcharge AS "TOTALAMOUNT",
count(booking.bookingid) OVER (PARTITION BY resort.resortid) AS "totalbookings having same resortid"
FROM resort
INNER JOIN booking
ON booking.resortid = resort.resortid
GROUP BY resort.resortid,
resort.resortname,
booking.totalcharge
ORDER BY resort.resortid;
I think that you want to sum() the totalcharge per resortid and resortname: so you need just the two latter columns in the group by clause (not totalcharge).
select
r.resortid,
r.resortname,
count(*) totalbooking ,
sum(b.totalcharge) as totalamount
from resort r
inner join booking b on b.resortid = r.resortid
group by r.resortid, r.resortname
order by r.resortid;
Note that table aliases make the query easier to write and read.
This gives you one row per resort, with the total number of bookings and the sum of totalcharge.

PostgreSQL JOIN TWO TABLES SYNTAX

I'm trying to find a solution for my problem but hours of thinking and searching didn't help. So I thought maybe i could ask it here.
I have two tables:
Table: Project
projectid | projectName | projectInformation | totalPrice | projectStatus
-------------- ------------ ------------------- ---------- -------------
1 "Education" "Information..." 2000 FALSE
2 "Hospital" "Information..." 3000 TRUE
3 "Water" "Information..." 1000 TRUE
Table: Donations
donationid | donationamount | date | costumerid | projectid
---------- -------------- ---- ---------- ---------
1 10 now() 3 1
2 20 now() 1 2
3 50 now() 2 2
4 15 now() 4 3
I want to archive the following results:
projectid | projectname | projectinformation | totalprice | projectstatus | sum(amount)
1 "Education" "information..." 2000 FALSE 325
2 "Hospital" "information..." 3000 TRUE
3 "Water" "information..." 1000 TRUE 120
So costumers can donate a x amount for a project.
I want to show the collected amount of money for each project. When I do the following query it gives me only the projects with donations only. I want to show all projects as well if there are no donations yet and put as default amount 0 USD for example.
This was my query that gives only project with donations:
select projectid, projectname, projectinformation, totalprice, projectstatus, sum(amount)
from project
natural join donation
group by projectid, projectname, projectinformation, totalprice, projectstatus;
I guess I need to use left outer join but somehow I couldn't figure out how to write it correctly.
You would use a left join:
select p.projectid, p.projectname, p.projectinformation, p.totalprice,
p.projectstatus, coalesce(sum(d.amount), 0)
from project p left join
donation d
on p.project_id = d.project_id
group by p.projectid, p.projectname, p.projectinformation, p.totalprice, p.projectstatus;
I strongly discourage you from using natural join. Leaving out the join keys makes the query hard to follow and very difficult to debug. You can easily make mistakes with the wrong keys used for the join.
I actually consider natural join an abomination, because it depends on the names of columns rather than on formally declared foreign key relationships -- "abomination" is my description for ignoring such intentionally declared relationships. If you want a short-hand, the using clause can be quite useful.

Excluding events within date-range while joining tables in SSMS

SQL newbie here. Using Microsoft SQL management studio. Having trouble excluding records that are outside of a particular date range while joining two tables. Please Help!
I have two tables - Event and DateRange.
Event
CustomerID ---------------- EventDate
1 ------------------------- 1/2/14
1 -------------------------- 2/5/15
1 -------------------------- 6/1/13
DateRange
CustomerID ---------------- StartDate ----------- EndDate
1 -------------------------- 1/1/14 -------------- 1/3/14
1 -------------------------- 2/1/15 -------------- 2/3/15
1 -------------------------- 5/30/13 ------------- 6/2/13
I want to join those two tables using CustomerID but I only want to ouput the date that was outside of the Startdate and EndDate date range (row 2 in Event table - 2/5/15).
I tried the following query but what keeps happening is that it kept giving me all the dates back in multiple because for each event date there were two StartDate and EndDate where it didn't fit into.
SQL Query
SELECT
E.CustomerID
,E.EventDate
,D.StartDate
,D.EndDate
FROM
Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE
(E.EventDate NOT BETWEEN D.StartDate and D.EndDate)
ORDER BY E.CustomerID
The output I got was something like this...
CustomerID ----- EventDate ----- StartDate ----- EndDate
1 -------------- 1/2/14 -------- 2/1/15 -------- 2/3/15
1 -------------- 1/2/14 -------- 5/30/13-------- 6/2/15
1 -------------- 2/5/15 -------- 1/1/14 -------- 1/3/14
1 -------------- 2/5/15 -------- 5/30/13 ------- 6/2/13
1 -------------- 6/1/13 -------- 1/1/14 -------- 1/3/14
1 -------------- 6/1/13 -------- 2/1/15 -------- 2/3/15
It's kind of doing a cross join because all the customerIDs are the same. And I need to keep the customerID because that's the only way to join those two tables. So how can I join these two table to only get the output that I want. Even if I do SELECT DISTINCT just the E.EventDate column by itself it'll still give me all three EventDates.
I pretty much need a count of how many event dates there are that don't fall into the startdate and enddate.
Thanks.
The problem is that you don't have a unique ID on your Event table to exclude your result set from. Since you're joining by CustomerID, it's giving you every row that doesn't meet your criteria. You might want to try something like this.
CREATE TABLE Event (ID INT NOT NULL IDENTITY PRIMARY KEY, CustomerID INT, EventDate DATETIME)
SELECT DISTINCT
E.EventDate
,E.CustomerID
FROM
Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE
E.ID NOT IN (
SELECT E.ID FROM Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE E.EventDate BETWEEN D.StartDate and D.EndDate
)
ORDER BY E.CustomerID
As you can see from above, I've created an Identity ID for the Event table. Now you can use a sub query to exclude the "events" from the result set that have dates that fall within a date range in the DateRange table. Which will give you what you're looking for. Hope that helps.
EventDate ------------- CustomerID
2015-02-05 00:00:00.000 1

Calculate Discount amount Based on Quantity

I have a table
SELECT PRD_CODE,PRD_MAKE,PRD_NAME FROM PRD_MST
SELECT PDI_DISC_QTY,PDI_DISC_PRICE FROM PRD_DISC_INF
select customerid,Productid,quantity from ShoppingCartItem
Records
PRD_MST
PRD_CODE PRD_MAKE PRD_NAME
----------- ---------------- ----------
4 mobile moto e
5 cycle hero
PRD_DISC_INF
PDI_DISC_QTY PDI_DISC_PRICE
----------------- ------------------
2.00 2.10
1.00 2.31
ShoppingCartItem
customerid Productid quantity
----------- ----------- -----------
1 5 5
The Problem is that
if a customer buy 5 mobile and price of single unit is 200 then how do i calculate discount amount base on PDI_DISC_QTY in table 'PRD_DISC_INF'
Need help i am unable to get the logic
Try this DEMO
select PDI_DISC_QTY *
(cast(round(((select quantity from ShoppingCartItem)/PDI_DISC_QTY),0) as int))
from PRD_DISC_INF ;

How to Join three tables

I have 3 tables : Orders, Customers and Suppliers
Structure and Data:
Orders
================================
OrdNo, OrdDt, OrdType, CSID
--------------------------------
1 01/04/2011 C 2
2 01/04/2011 S 1
--------------------------------
Customers
----------
CID, CName
----------
1 John
2 Boby
----------
Suppliers
=========
SID, SName
----------
1 Tony
2 Mohan
----------
If OrdTYpe = "C" then pick data from Customers table for CSID = CID
If OrdTYpe = "S" then pick data from Suppliers table for CSID = SID
How to list the records like
--------------------------------------
OrdNo, OrdDt, OrdType, CSID CSName
--------------------------------------
1 01/04/2011 C 2 Boby
2 01/04/2011 S 1 Tony
--------------------------------------
Does this get what you want?
SELECT ordno, orddt, ordtype, csid,
COALESCE( c.name, s.name ) csname
FROM orders o
LEFT JOIN customer c ON o.ordtype='C' AND c.cid = o.csid
LEFT JOIN suppliers s ON o.ordtype='S' AND c.sid = o.csid
Another possibility, at least in Oracle, would be:
SELECT ordno, orddt, ordtype, csid,
CASE WHEN ordtype='C' THEN (SELECT name FROM customer WHERE cid = csid)
WHEN ordtype='S' THEN (SELECT name FROM suppliers WHERE sid = csid)
END csname
FROM orders;
Martin has a good point. I'm no pro, but I would suggest making an intermediate table that will allow you to keep customers and suppliers separate, yet still pull from a common set of id's
Orders
========================
OrdNo, OrdDt, CSID
------------------------
1 01/04/2011 2
2 01/04/2011 1
-------------------------
ClientIDs
---
ID
---
1
2
3
4
---
Customers
----------
CID, CName
----------
1 John
3 Boby
----------
Suppliers
=========
SID, SName
----------
2 Tony
4 Mohan
----------
That way you end up with something like this:
-------------------------------
OrdNo, OrdDt, CSID CSName
-------------------------------
1 01/04/2011 2 Tony
2 01/04/2011 1 John
-------------------------------
and by back tracking up through the (now) unique ID you will be able to tell if it is a customer, or supplier... also I'm pretty sure your SQL will run faster with this route (don't hold me to it though). If you like this idea, I could look into SQL to back it.
SELECT o.[OrdNo], o.[OrdDt], o.[OrdType], o.[CSID],
CASE WHEN o.[OrdType] = 'C'
THEN c.[CName]
ELSE s.[SName]
END as [CSName]
FROM Orders AS o
LEFT JOIN Customers AS c
ON o.[CSID] = c.[CID]
LEFT JOIN Suppliers AS s
ON o.[CSID] = s.[SID]