how left outer join works for table have null values - sql

I have four tables: person and booking, booking_detail,room_pricing where person_id used as foreign key in the booking table and person_id is used as foreign key in the booking_detail table. where room_id is used as foreign_key in booking_detail
Supposing I want to show all the booking_id from booking table and their corresponding total income from rooms on each booking including those who doesn't exist in `` table such as id 1 ,i am using oracle database
person
person_id name
1 xyz
2 abc
3 jkl
booking
booking_id person_id
1 1
2 3
3 1
booking_details
person_id roomid
2 201
3 303
3 303
room_pricing
room_id price
201 $ 100
302 $ 100
303 $ 200
final table should be like this
booking_id total
1 0
2 400$
3 0

Try the following using left join
select
b.booking_id,
coalesce(sum(total), 0) as total
from booking b
left join booking_details bd
on b.person_id = bd.person_id
left join room_pricing rp
on bd.room_id = rp.room_id
group by
b.booking_id

You want a left join with aggregation:
select p.person_id, coalesce(sum(o.total), 0)
from person p left join
order o
on o.person_id = p.person_id
group by p.person_id;

Related

SQL get table1 names with a count of table2 and table3

I have three tables, table1 is connected to table2 and table3, but table2 and table3 are not connected. I need an output count of table2 and table3 for each table1 row. I have to use joins and a group by table1.name
SELECT Tb_Product.Name, count(TB_Offers.Prod_ID) 'Number of Offers', count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.Name
I need to combine these queries:
SELECT Tb_Product.[Name], count(TB_Offers.Prod_ID) 'Number of Offers'
FROM Tb_Product LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY Tb_Product.[Name]
SELECT Tb_Product.[Name], count(Tb_Requests.Prod_ID) 'Number of Requests'
FROM Tb_Product LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
GROUP BY Tb_Product.[Name]
Results:
Name Number of Offers
Airplane 6
Auto 5
Bike 3
Camera 0
Computer 12
Milk 4
Oil 4
Orange 6
Telephone 0
Truck 6
TV 4
Name Number of Requests
Airplane 1
Auto 5
Bike 0
Camera 2
Computer 6
Milk 4
Oil 5
Orange 6
Telephone 0
Truck 1
TV 5
My results for offers and requests are the same value. I am not sure what I am doing wrong with the joins. Do I need to somehow join product to request and separately join product to offers? This needs to be done in one query.
This is for a class. Explanation would also be appreciated.
The simplest way to do this is to count the distinct values of each column:
SELECT
Tb_Product.Name,
count(distinct TB_Offers.Prod_ID) 'Number of Offers',
count(distinct Tb_Requests.Prod_ID) 'Number of Requests'
FROM
Tb_Product
LEFT OUTER JOIN
Tb_Requests ON Tb_Product.Prod_ID = Tb_Requests.Prod_ID
LEFT OUTER JOIN
TB_Offers ON Tb_Product.Prod_ID = TB_Offers.Prod_ID
GROUP BY
Tb_Product.Name
This is necessary because of the way joins work consecutively to produce a rowset that is a combination of all the input relations. COUNT() normally performs a count of non-null values in a column.
You can also do something like this, which aggregates the counts from the child tables independently and then joins them to the base table:
SELECT
p.Name,
o.cnt as Offer_Count,
r.cnt as Request_Count
FROM
TB_Product p
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Offers GROUP BY Prod_ID) o
LEFT OUTER JOIN
(SELECT Prod_ID, COUNT(1) cnt FROM TB_Requests GROUP BY Prod_ID) r
More explanation...
Let's say you have two products:
Prod_ID
Name
1
Widget
2
Gizmo
And two offers, one for each product:
Offer_ID
Prod_ID
100
1
200
2
And two requests for each product:
Request_ID
Prod_ID
1001
1
1002
1
2001
2
2002
2
Now you join Product relation to Offer relation on Prod_ID, you get a result like this:
Prod_ID
Name
Offer_ID
Prod_ID
1
Widget
100
1
2
Gizmo
200
2
Now when you join that relation to Requests on Prod_ID, you get something like this:
Prod_ID
Name
Offer_ID
Prod_ID
Request_ID
Prod_ID
1
Widget
100
1
1001
1
1
Widget
100
1
1002
1
2
Gizmo
200
2
2001
2
2
Gizmo
200
2
2002
2
Now when you count any of these columns you get 4 because each column has 4 values.

SQL help on self Join query

Two tables customers table and payment table
Payment table
paymentid int
paymentdate date
custid int
Customers table
Custid int
name varachar
paymentcustid int
foreign key constraints:
payment.custid refers to customer.custid
payment.paymentcustid also refers to customer.custid
Write a query to fetch the name of the paymentid, paymentdate, name of the customer, name of the payment customer.
Table structure would be like
Payment table
paymentid paymentdate custid
100 07/11/18 1
101 08/11/18 3
102 08/11/18 4
Customer table
Custid name paymentcustid
1 a 4
2 b 3
3 c 5
4 d 5
5 e 1
Looks like you just need :
SELECT
p.paymentid,
p.paymentdate,
c1.name payment_customer,
c2.name customer
FROM
payment p
INNER JOIN customers c1 ON p.cust_id = c1.paymentcustid
INNER JOIN customers c2 ON p.cust_id = c2.custid;

T-SQL (Azure) Only shows 2 results instead of 3

Hi i am extending a query and i have the following problem, i have in a table 3 users and they are connected (joined) with other tables, now in these tables the user can or can not have data, depending on their actions.
SELECT
COALESCE (credit.received, 0) AS CreditReceived,
COALESCE (purchase.used, 0) AS CreditUsed,
COALESCE (purchase.NumberOfPurchase, 0) AS NumberOfPurchase,
COALESCE (credit.received, 0) - COALESCE (purchase.used, 0) AS UserCredit,
dbo.[User].id_user,
dbo.[User].name,
dbo.[User].town,
dbo.[User].country
FROM (SELECT DISTINCT id_user
FROM dbo.UserCredit
UNION
SELECT DISTINCT id_user
FROM dbo.UserPurchase) AS users
INNER JOIN
dbo.[User] ON users.id_user = dbo.[User].id_user
LEFT OUTER JOIN
(SELECT
id_user,
SUM(creditRecieved) AS received
FROM
dbo.UserCredit AS UserCredit_1
GROUP BY id_user) AS credit
ON users.id_user = credit.id_user
LEFT OUTER JOIN
(SELECT
id_user,
SUM(creditUsed) AS used,
COUNT(creditUsed) AS NumberOfPurchase
FROM
dbo.UserPurchase AS UserPurchase_1
GROUP BY id_user) AS purchase
ON users.id_user = purchase.id_user
The information comes from these tables;
TABLE dbo.user
id_user name town country
----------- ----------- -------------- -------------
1 George New York USA
2 Lucas San Diego GB
3 Steven San Fran Germany
TABLE dbo.UserCredit
id id_user creditRecieved PurchasePrice
----------- ----------- -------------- -------------
1 1 150 750
2 1 25 100
3 2 65 15
TABLE dbo.UserPurchase
id id_user creditUsed date
----------- ----------- ----------- -----------
1 1 175 NULL
2 2 3 NULL
3 2 2 NULL
What i get is only the result of the two first user_id's and the third one (steven) is not shown in the results, i guess it is because the id's dont exist in the other two tables, but that i was hoping would be fixed with the COALESCE and the four of them would be set to 0. What am i doing wrong?
You don't need the derived table "users." It doesn't contain all the userid's and is unnecessary. Remove that and left join to your user table instead and it should work.
By replacing the first DISTINCT with the dbo.[User] instead of the dbo.UserCredit it gave me the right results; All users in the overview with credit or no credit, thanks to the COALESCE :)
SELECT
COALESCE (credit.received, 0) AS CreditReceived,
COALESCE (purchase.used, 0) AS CreditUsed,
COALESCE (purchase.NumberOfPurchase, 0) AS NumberOfPurchase,
COALESCE (credit.received, 0) - COALESCE (purchase.used, 0) AS UserCredit,
dbo.[User].id_user,
dbo.[User].name,
dbo.[User].town,
dbo.[User].country
FROM (SELECT DISTINCT id_user
FROM dbo.[User]
UNION
SELECT DISTINCT id_user
FROM dbo.UserPurchase) AS users
INNER JOIN
dbo.[User] ON users.id_user = dbo.[User].id_user
LEFT OUTER JOIN
(SELECT
id_user,
SUM(creditRecieved) AS received
FROM
dbo.UserCredit AS UserCredit_1
GROUP BY id_user) AS credit
ON users.id_user = credit.id_user
LEFT OUTER JOIN
(SELECT
id_user,
SUM(creditUsed) AS used,
COUNT(creditUsed) AS NumberOfPurchase
FROM
dbo.UserPurchase AS UserPurchase_1
GROUP BY id_user) AS purchase
ON users.id_user = purchase.id_user

How to select record in 1 to many relation

I have a contact table and contactphone tables.
Each record in contacts can have many records in contactphone but only 1 can be primary at most (primary is Boolean but can be also NULL)
Contacts:
cid name address
1 Jack A
2 Bill B
3 Mor C
4 Jeff D
ContactsPhones:
cpid cid phone primary
1 1 140 True
2 1 150
3 2 170
I need to write a query that returns all contacts with their details, if they have more than 1 phone then present only primary.
Output:
cid name address phone
1 Jack A 140
2 Bill B 170
3 Mor C
4 Jeff D
What is most easiest way to do it?
Note that primary can have (True,False, Null)
This is a bit tricky. I think the easiest method is distinct on:
SELECT DISTINCT ON (cid) cid, name, address, cp.phone
FROM Contacts c LEFT JOIN
ContactsPhones cp
ON c.cid = cp.cpid
ORDER BY cid, (case when primary then 1 else 2 end);
You can use a LEFT JOIN to a derived table that uses ROW_NUMBER to specify more suitable record:
SELECT cid, name, address, phone
FROM Contacts AS c
LEFT JOIN (
SELECT phone, cpid,
ROW_NUMBER() OVER(PARTITION BY cpid
ORDER BY CASE
WHEN primary THEN 0
ELSE 1
END) AS rn
FROM ContactsPhones) AS cp
ON c.cid = cp.cpid AND cp.rn = 1

Group by with two columns

I am trying to write a query using group by in sub query ,I referred lot of blogs but could not get all the values.
I have three tables and below is the structure of those tables.
Pet_Seller_Master
ps_id ps_name city_id
2 abc 1
3 xyz 2
4 fer 4
5 bbb 1
City_Master
city_id city_name
1 Bangalore
2 COIMBATORE
4 MYSORE
Api_Entry
api_id ps_id otp
1 2 yes
2 3
3 2 yes
4 3 yes
5 4
6 5 yes
7 5 yes
8 5 yes
Query is to get number of sellers, no of pet sellers with zero otp, no of pet sellers with 1 otp, no of pet sellers with 2 otp,no of pet sellers with otp>2 for the particular city and within date range.
Through Below query I am able to get city , psp , and zero otp
select cm.city_name,
count(ps.ps_id) as PSP,
((select count(ps1.ps_id)
FROM ps_master ps1
WHERE ps1.city = cm.city_id)-
(SELECT count(distinct ps1.ps_id)
from ps_master ps1
INNER JOIN api_entry ae ON ps1.ps_id = ae.ps_id and otp!=''
WHERE ps1.city = cm.city_id and date(timestamp) >= curdate() - INTERVAL DAYOFWEEK(curdate())+6 DAY AND date(timestamp) < curdate())) as zero_psp
from ps_master ps INNER JOIN city_master cm ON ps.city = cm.city_id and cm.city_type = 'IN HOUSE PNS'
group by city_id
Please tell me the solution to solve this query.
Thanks in advance
It's not hard to do and you were on a right track. Here is what I would use:
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from Api_Entry a
inner join Pet_Seller_Master p on p.ps_id=a.ps_id
inner join City_Master c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
Now, if you want to get the number of sellers with zero otp, you just apply where clause:
where otp <> 'yes'
If you want to get the number of pet sellers with otp>2, then you just use subquery:
select *
from (
select c.city_name, a.otp, p.ps_name, COUNT(*) nbr
from #tempA a
inner join #tempP p on p.ps_id=a.ps_id
inner join #tempC c on p.city_id=c.city_id
group by c.city_name, a.otp, p.ps_name
) g
where nbr > 2