How to Join three tables - sql

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]

Related

sqlserver records need to come horizontly

I have table structure below,
Table Name: Customer
Cust_id Cust_Name
------------------------
101 John
102 Peter
Table Name: Loan_Details
Id LoanName Cust_Id Amt
-----------------------------------------
1 PersonalLoan 101 2L
2 PersonalLoan 101 3L
3 HomeLoan 101 10L
Table Name: Product
Id Cust_Id ProductName Cost
-----------------------------------------
1 101 Product1 1000
2 101 Product1 2000
3 101 Product1 3000
I need result horizontly in below structure, is it possble?
Cust_Id Cust_Name PersonalLoan Amt HomeLoan Amt ProductName Cost
--------------------------------------------------------------------------------
101 John PersonalLoan 2L HomeLoan 10L Product1 1000
101 John PersonalLoan 3L NULL NULL Product1 2000
101 John NULL NULL NULL NULL Product1 3000
You can try this query.
If you want to list "Peter" too, just change INNER JOIN to LEFT JOIN in the CusPrd subquery.
DECLARE #Customer TABLE (Cust_id VARCHAR(10), Cust_Name VARCHAR(20))
INSERT INTO #Customer
VALUES
('101','John'),
('102','Peter')
DECLARE #Loan_Details TABLE (Id INT, LoanName VARCHAR(20), Cust_Id VARCHAR(10), Amt VARCHAR(10))
INSERT INTO #Loan_Details
VALUES
(1,'PersonalLoan','101','2L'),
(2,'PersonalLoan','101','3L'),
(3,'HomeLoan','101','10L')
DECLARE #Product TABLE (Id INT, Cust_Id VARCHAR(10), ProductName VARCHAR(20), Cost INT)
INSERT INTO #Product
VALUES
(1,'101','Product1',1000),
(2,'101','Product1',2000),
(3,'101','Product1',3000)
;WITH Loan AS
(
SELECT *,
RN = ROW_NUMBER() OVER (PARTITION BY LoanName ORDER BY Id)
FROM #Loan_Details
),
CusPrd AS
(
SELECT
C.Cust_id,
C.Cust_Name,
P.ProductName,
P.Cost,
P.Id,
RN = ROW_NUMBER() OVER (PARTITION BY C.Cust_id ORDER BY P.Id)
FROM
#Customer C
INNER JOIN #Product P ON C.Cust_id = P.Cust_Id
)
SELECT C.Cust_id, C.Cust_Name, PL.LoanName [PersonalLoan], PL.Amt, HL.LoanName [HomeLoan], HL.Amt, C.ProductName, C.Cost
FROM
CusPrd C
LEFT JOIN Loan PL ON PL.Cust_Id = C.Cust_id AND PL.LoanName ='PersonalLoan' AND C.RN = PL.RN
LEFT JOIN Loan HL ON HL.Cust_Id = C.Cust_id AND HL.LoanName ='HomeLoan' AND PL.RN = HL.RN AND C.RN = PL.RN
Result:
Cust_id Cust_Name PersonalLoan Amt HomeLoan Amt ProductName Cost
---------- -------------------- -------------------- ---------- -------------------- ---------- -------------------- -----------
101 John PersonalLoan 2L HomeLoan 10L Product1 1000
101 John PersonalLoan 3L NULL NULL Product1 2000
101 John NULL NULL NULL NULL Product1 3000
select * from
(select c.Cust_id,Cust_Name,ld.LoanName as Loan,Amt,ProductName,Cost
from Customer c left join Product p on p.Cust_id=c.Cust_id
left join Loan_Details ld on ld.Cust_id = c.Cust_id) as MTable
PIVOT
(
SUM(Amt) for Loan in ([HomeLoan],[PersonalLoan])
) as PTable
Use this it will give you what you want the result is something like this
Cust_id Cust_Name ProductName Cost HomeLoan PersonalLoan
----------- -------------------- -------------------- ----------- ----------- ------------
101 John Product1 1000 10 5
101 John Product12 2000 10 5
101 John Product2 3000 10 5
102 Peter NULL NULL NULL NULL

get multiple column from both table by joining a table on the basis of other max table column

1) vendor table
--------------------------------------------
VENDid VENDname
--- -----
1 ABC
2 XYZ
3 WXY
2)purchase table
---------------------------------------------
VENDid Purchasedate
------ ------------
1 12-01-2012
1 10-11-2013
2 22-02-2014
2 11-04-2014
3 10-05-2014
3 11-06-2014
1 14-06-2014
output(list all rows of vendor table and only max(purchasedate) from purchase table)
---------------------------------------------
VENDid VENDname PurchaseDate
------- -------- -------------
1 ABC 14-06-2014
2 XYZ 11-04-2014
3 WXY 11-06-2014
i got some queries like to solve previous problem-
SELECT v.VendID, VendName, Max(PurchaseDate)
FROM vendor v
INNER JOIN purchase p
ON v.VendID = p.VendID
Group By v.VendID, VendName
select VENDid, VENDname,
(select top 1 purchaseDate from purchase p
where p.VENDid=v.VENDid order by purchaseDate desc) as 'Purchase date'
from Vendor v
Que. If i will add some more column in purchase table like -
2)purchase table
------------------------------------------
VENDid Purchasedate amount_paid
------ ------------ ------------
1 12-01-2012 10000
1 10-11-2013 20000
2 22-02-2014 15000
2 11-04-2014 30000
3 10-05-2014 80000
3 11-06-2014 17000
1 14-06-2014 28000
and i want amount_paid along with previous output like-
---------------------------------------------
VENDid VENDname PurchaseDate amount_paid
------- -------- ------------- -------------
1 ABC 14-06-2014 28000
2 XYZ 11-04-2014 30000
3 WXY 11-06-2014 17000
then what will be query..
You appear to be using SQL Server. If so, you can use cross apply:
select v.VENDid, v.VENDname, p.PurchaseDate, p.Amount_Paid
from Vendor v cross apply
(select top 1 p.*
from purchase p
where p.VENDid = v.VENDid
order by p.purchaseDate desc
) p ;

Creating a view with sums from multiple tables (Oracle 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;

Concatenating multiple results of a query in one row in Oracle

I have 2 tables with one having a reference to the first by id
first table for example is customer having the fields
id firstname lastname
------ --------- ---------
1 john smith
2 jessica james
the second table for example is product having the fields
id customer_id product descr
------- ----------- --------- ------
1 1 ts Shirt
2 1 ti Tie
3 2 sk skrit
I need a query that will output the following
customer.firstname customer.lastname product_and_desc
------------------ ------------------ ---------------------
john smith ts-Shirt , ti-Tie
jessica james sk-skirt
with the product rows variable for each customer.
I appreciate you help :)
thanks,
You can use list_agg(). In your case:
select c.firstname, c.lastname,
list_agg(p.product||'-'||p.desc, ' , ') within group (order by p.id) as product_and_desc
from customer c join
product p
on c.id = p.customer_id
group by c.firstname, c.lastname;
I would suggest, though, that the second argument to list_agg() be ', ' rather than ' , '. The space before the comma looks a bit unusual.
select first_name,last_name,wm_concat(product||'-'||descr) as product_and_descr
from tbl1 ,tbl2 where tbl1.id=tbl2.customer_id
group by first_name,last_name;

SQL Pivot with String

I have two tables in SQL Server: Customer and Address
Customer Table:
CustomerID FirstName LastName
----------- ---------- ----------
1 Andrew Jackson
2 George Washington
Address Table:
AddressID CustomerID AddressType City
----------- ----------- ----------- ----------
1 1 Home Waxhaw
2 1 Office Nashville
3 2 Home Philadelphia
This is the output that I need:
CustomerID Firstname HomeCity OfficeCity
----------- ---------- ---------- ----------
1 Andrew Waxhaw Nashville
2 George Philadelphia Null
This is my query, but not getting the right result:
SELECT CustomerID, Firstname, HOme as HomeCity, Office as OfficeCity FROM
(SELECT C.CustomerID, C.FirstName, A.AddressID, A.AddressType, A.City
FROM Customer C, Address A
WHERE C.CustomerID = A.CustomerID)as P
PIVOT (MAX(city) FOR AddressType in ([Home],[Office])) as PVT
This is the result that I am getting:
CustomerID Firstname HomeCity OfficeCity
----------- ---------- ---------- ----------
1 Andrew Waxhaw NULL
1 Andrew NULL Nashville
2 George Philadelphia Null
As you can see Customer 1 is showing up twice in the final result. Is it possible to get only one row per customer?
I looked up this example, but didn't help:http://stackoverflow.com/questions/6267660/sql-query-to-convert-rows-into-columns
Thanks
It is giving this row because you have AddressID in the select list for you subquery "P". So even though you don't have AddressID in you top level select this, the PIVOT function is still grouping by it. You need to change this to:
SELECT CustomerID, Firstname, Home as HomeCity, Office as OfficeCity
FROM ( SELECT C.CustomerID, C.FirstName, A.AddressType, A.City
FROM #Customer C, #Address A
WHERE C.CustomerID = A.CustomerID
) AS P
PIVOT
( MAX(city)
FOR AddressType in ([Home],[Office])
) AS PVT
Although I would be inclined to use an explicit INNER JOIN rather than an implicit join between customer and Address.
I would write it like this instead:
SELECT C.CustomerID, C.Firstname,
Home.City as HomeCity,
Office.City as OfficeCity
FROM Customer C
LEFT JOIN Address Home
on Home.CustomerID = C.CustomerID and Home.AddressType = 'Home'
LEFT JOIN Address Office
on Office.CustomerID = C.CustomerID and Office.AddressType = 'Office'