Getting data from multiple tables in sql - mysql

Table 1: Invoices (inv_id, inv_value, cust_id)
Table 2: Customers (cust_id, sales_rep)
Table 3: Members (Member_id, member_cateogry, member_type, cust_id)
Note 1: Each Customer Pays multiple Invoices. (One-to-Many Relationship).
Note 2: Each Customer pays for one-or-more members (so more than one member could be related to one customer).
Note 3: Each Member has a category which could be 1 "represents Individual" OR 2 "represents Group".
Note 4: Each Member has a type which could be 1 "represents new" OR 2 "represents renew".
I want to get the TOTAL of the Invoice_value field for customers who's sales_rep = 1 and their member_category = 10 and their members_type = 123
Ex: What is the total amount of Invoices that customers paid IF the Sales_rep for these customers was 1 and the members they paid for were new and Individual members.
I tried:
SELECT Sum(invoices.inv_value) AS total
FROM invoices,
customers,
members
WHERE invoices.cust_id = customers.cust_id
AND members.custid = customers.cust_id
AND members.category = {$category}
AND members_type = {$type}
AND customers.sales_rep = {$id}";
AND
SELECT Sum(invoices.inv_value) AS total
FROM members
INNER JOIN customers
ON members.custid = customers.cust_id
INNER JOIN invoices
ON customers.cust_id = invoices.cust_id
WHERE customers.sales_rep = {$id}
AND members.category = {$category}
AND members.type = {$type}";
But both return double the Invoice value.
ex.: 1 Invoice for $120 in the Invoices table return $240 using these sql queries.
How can I fix this?

This is your query:
SELECT sum(i.inv_value) as total
FROM members m INNER JOIN
customers c
ON m.custid = c.cust_id INNER JOIN
invoices i
ON c.cust_id = i.cust_id
WHERE c.sales_rep = {$id} AND
m.category = {$category} AND
m.type = {$type}";
(Don't use implicit JOIN syntax using commas. It is archaic and less powerful.)
The problem is probably that two members can have the same customer id. You can check this by running:
select m.cust_id, count(*)
from members m
group by m.cust_id
having count(*) > 1;
It is also possible that customer ids are duplicated in customers.
Assuming the duplicates are only in members, change the query to exists:
SELECT sum(i.inv_value) as total
FROM customers c
ON INNER JOIN
invoices i
ON c.cust_id = i.cust_id
WHERE c.sales_rep = {$id} AND
EXISTS (SELECT 1
FROM members m
WHERE m.custid = c.cust_id AND
m.category = {$category} AND
m.type = {$type}
);

It seems like you are taking inv_value from invoices, which has many to one relationship with customers but customer table and members table have one to many relationship.
Say you have below data
Invoice Table
invoices.cust_id invoices.inv_value
custid1 100
Customer table
customer.cust_id
custid1
Members Table
members.cust_id members.category
custid1 1
custid1 2
On join all three tables
customer.cust_id members.cust_id invoices.inv_value members.category
custid1 custid1 100 1
custid1 custid1 100 2
if you notice as custid1 exist in 2 member_category, invoice value is also duplicated.
To solve this, first you can take the distinct records, then summing those distinct records would help you solve your problem as below
Solution
SELECT Sum(invoices.inv_value) AS total
FROM(
SELECT DISTINCT members.custid, invoices.inv_value inv_value
FROM members
INNER JOIN customers
ON members.custid = customers.cust_id
INNER JOIN invoices
ON customers.cust_id = invoices.cust_id
WHERE customers.sales_rep = {$id}
AND members.category = {$category}
AND members.type = {$type});

Related

How do I display multiple columns from 3 Different tables in SQL?

I have 3 tables:
Reservation, Trip, Customer
I only need to display the trip name, trip type, customer first name, customer last name of the customers who have multiple reservations
Sort of like this
Reservation table
Reservation_ID ... ... .. Customer_Num
16001 101
16002 101
16003 102
16004 103
16005 103
Customer table
Customer_ID ... ... .. Customer_Num
30 101
31 102
32 103
Customer table's primary ID is customer_ID and has a column name of
Customer_Num. this column name is also in the reservation table.
Reservation table's primary id is Reservation_ID
I’ve tried:
SELECT Customer.First_Name, Customer.Last_Name, Trip.Trip_Name, Trip.Type, Reservation.Customer_Num COUNT(Reservation.Customer_Num
FROM Reservation, Customer, Trip
WHERE Reservation.Customer_Num = Customer.Customer_Num
HAVING COUNT(Reservation.Customer_Num) > 1
GROUP BY Customer.First_Name, Customer.Last_Name, Trip.Trip_Name, Trip.Type, Reservation.Customer_Num;
Try this, presuming that Trip table using Customer_num field as foreign key
select A.Customer_id, A.Name, A.Last_name, B.name, B.type from Customer as A
where Customer_id in
(select Customer_Num from Reservation group by Customer_Num having count(Customer_Num) > 1)
left join Trip as B on Trip.Customer_Num = Customer.Customer_Num
You can use EXISTS :
SELECT t.*, c.*
FROM reservation r INNER JOIN
customer c
ON c.Customer_Num = r.Customer_Num INNER JOIN
trip t
ON . . .
WHERE EXISTS (SELECT 1
FROM reservation r1
WHERE r1.customer_no = r.customer_no AND
r1.Reservation_ID <> r.Reservation_ID
);
You need to adjust the ON clause for Trip table as you didn't specified the table information.
Presumming in your TRIP table you have columns as trip name, trip type, You may try below query -
SELECT Customer.First_Name
,Customer.Last_Name
,Trip.Trip_Name
,Trip.Type
,Reservation.Customer_Num
,COUNT(Reservation.Customer_Num) Customer_Num_Cnt
FROM Reservation R
INNER JOIN Customer C ON Reservation.Customer_Num = Customer.Customer_Num
INNER JOIN Trip T ON R.TRIP_ID = T.TRIP_ID
GROUP BY Customer.First_Name
,Customer.Last_Name
,Trip.Trip_Name
,Trip.Type
,Reservation.Customer_Num
-- HAVING COUNT(Reservation.Customer_Num) > 1;
If this doesn't fulfil your requirements, Please share the complete structure of TRIP table and CUSTOMER table.
This displays the results making it so I can pick which names have more than one reservation. In the reservation table it lists the customer num who has more than one reservation but I'm not sure why it wasn't working.
SELECT CUSTOMER.FIRST_NAME, CUSTOMER.LAST_NAME, TRIP.TRIP_NAME, TRIP.TYPE, RESERVATION.CUSTOMER_NUM, COUNT(RESERVATION.CUSTOMER_NUM) CUSTOMER_NUM_CNT
FROM RESERVATION
INNER JOIN CUSTOMER ON CUSTOMER.CUSTOMER_NUM = RESERVATION.CUSTOMER_NUM
INNER JOIN TRIP ON RESERVATION.TRIP_ID = TRIP.TRIP_ID
GROUP BY CUSTOMER.FIRST_NAME, CUSTOMER.LAST_NAME, TRIP.TRIP_NAME, TRIP.TYPE, RESERVATION.CUSTOMER_NUM
commented out--HAVING COUNT(RESERVATION.CUSTOMER_NUM) > 1;

Calculate each customer cash expenditure and card expenditure

I am having a little trouble figuring out solution to a sql query.
The problem is as follows :
1) Transaction table
2) Customer table
3) Creditcard Table
Problem: For each customer find their expenditure through each card and cash payment.
What I have done is :
Applying an inner join over the three tables , I group by on the basis of cust_id and card_no and hence I get a result like below :
Query: select cc.cust_id, name,cc.card_no,cc.card_type,sum(tran_sold_amt) from customer c join creditcard cc on c.cust_id=cc.cust_id join trans t on cc.card_no=t.card_no group by cc.cust_id,cc.card_no;
What I am unable to understand is how do I recognise the cash payment is done by which customer and how do I aggregate them. Is there some discrepancy in the question or am I making a mistake in understanding.
Any help is really appreciated :)
The problem stated exactly is :
Hi thanks for all the suggestions , I was able to solve by self joining the two tables:
Select
b.cust_id,
b.name,
payment,
a.card_no,
SUM(tran_sold_amt) from
(SELECT
c.cust_id as cust_id,
c.name as name,
CASE WHEN Payment_Method = "Cash" THEN "Cash" ELSE t.Card_type END AS payment,
cc.card_no,
tran_id,
tran_sold_amt
FROM
trans t
LEFT JOIN
creditcard cc
ON t.card_no = cc.card_no
LEFT JOIN
customer c
ON cc.cust_id = c.cust_id) a
LEFT JOIN
(SELECT distinct tran_id, cust_id, name from (
SELECT
c.cust_id as cust_id,
tran_id,
name
FROM
trans t
LEFT JOIN
creditcard cc
ON t.card_no = cc.card_no
LEFT JOIN
customer c
ON cc.cust_id = c.cust_id) where cust_id != 'null') b on a.tran_id = b.tran_id where b.cust_id != 'null' group by 1,2,3

MySQL - At least one of something and greater than between 3 tables

I have 3 tables :
I need to retrieve the name of the sellers that have sold during January AT LEAST one product where the total amount of sales of this said product during January is greater than 1000.
I thought about starting like this :
SELECT c.nom, c.prenom
FROM Commerciaux c
LEFT JOIN Ventes v ON c.id_commerciaux = v.id_commerciaux
WHERE EXISTS (SELECT 1
FROM Produits p, Ventes v
WHERE p.id_produits = v.id_produits
AND MONTH(v.date) = 1
GROUP BY p.nom
HAVING SUM(v.montant) > 200)
AND MONTH(v.date) = 1
GROUP BY c.nom, c.prenom
The SELECT in the WHERE EXISTS seems to be working, but when I have to get link the table Sellers, I don't know how to write it.
Any help would be great !
You can use something like this:
select distinct s.name
from Sellers s,
( -- get all those products that qualify (more than 1000 sales)
select product_id, sum(amount) as total
from Sales
where Month(date) = 1
group by product_id
having total > 1000
) vp,
Sales sa
where
s.id = sa.commercial_id and
sa.product_id = vp.id and
Month(sa.date) = 1
#Leo answer would return only those Sellers that have sold more than 100 of the product in January instead of returning all Sellers that have sold any amount of the product that has been sold in an amount greater than 1000 across the board.
You could use a nested query:
SELECT s.name FROM
(Sellers as s JOIN Sales as sp on sp.commercial_id = s.id) JOIN
Product as p on p.id = sp.product_id
WHERE MONTH(sp.date) = 1 AND YEAR(sp.date) = 2017 AND
(SELECT SUM(Amount) FROM Sales as sp2 WHERE sp2.commercial_id = s.id
AND sp2.product_id = p.id
AND MONTH(sp2.date) = 1 AND YEAR(sp2.date) = 2017) > 1000
First, join the three tables on foreign keys, and then run a nested query to compute amount of the selected seller on a specific product to check to pass the amount limitation.
try this
SELECT C.name
FROM Sales A
JOIN Product B ON A.produit_id = B.id
JOIN Seller C ON A.commercial_id = C.id
WHERE MONTH(A.date) = 1
HAVING SUM(A.nAmount) > 100
GROUP BY C.name

Filter on second left join - SQL

I have three tables. One consists of customers, one consists of products they have purchased and the last one of the returns they have done:
Table customer
CustID, Name
1, Tom
2, Lisa
3, Fred
Table product
CustID, Item
1, Toaster
1, Breadbox
2, Toaster
3, Toaster
Table Returns
CustID, Date, Reason
1, 2014, Guarantee
2, 2013, Guarantee
2, 2014, Guarantee
3, 2015, Guarantee
I would like to get all the customers that bought a Toaster, unless they also bought a breadbox, but not if they have returned a product more than once.
So I have tried the following:
SELECT * FROM Customer
LEFT JOIN Product ON Customer.CustID=Product.CustID
LEFT JOIN Returns ON Customer.CustID=Returns.CustID
WHERE Item = 'Toaster'
AND Customer.CustID NOT IN (
Select CustID FROM Product Where Item = 'Breadbox'
)
That gives me the ones that have bought a Toaster but not a breadbox. Hence, Lisa and Fred.
But I suspect Lisa to break the products on purpose, so I do not want to include the ones that have returned a product more than once. Hence, what do I add to the statement to only get Freds information?
How about
SELECT * FROM Customer
LEFT JOIN Product ON Customer.CustID=Product.CustID
WHERE Item = 'Toaster'
AND Customer.CustID NOT IN (
Select CustID FROM Product Where Item = 'Breadbox'
)
AND (SELECT COUNT(*) FROM Returns WHERE Customer.CustId = Returns.CustID) <= 1
The filter condition goes in the ON clause for all but the first table (in a series of LEFT JOIN:
SELECT *
FROM Customer c LEFT JOIN
Product p
ON c.CustID = p.CustID AND p.Item = 'Toaster' LEFT JOIN
Returns r
ON c.CustID = r.CustID
WHERE c.CustID NOT IN (Select p.CustID FROM Product p Where p.Item = 'Breadbox');
Conditions on the first table remain in the WHERE clause.
As a note: A table called Product that contains a CustId seems awkward. The table behaves more likes its name should CustomerProducts.
You use conditional COUNT
SELECT C.CustID, C.Name
FROM Customer C
JOIN ( SELECT CustID
FROM Products
GROUP BY CustID
HAVING COUNT(CASE WHEN Item = 'Toaster' THEN 1 END) > 1
AND COUNT(CASE WHEN item = 'Breadbox' THEN 1 END) = 0
) P -- Get customer with at least one Toaster and not Breadbox
ON C.CustID = P.CustID
JOIN ( SELECT CustID
FROM Returns
HAVING COUNT(*) < 2
) R -- Get only customers with less than 2 returns
ON C.CustID = R.CustID

WHERE value IS NOT IN (subquery)

I've been struggling with this query.
I have two tables. One with coupons and Invoicenumbers. One with Invoicenumbers and customer names.
I need to get the customers who have not used a given coupon.
Here are the tables:
Promotion table:
Promotions
Invoice | Coupon
----------------
1 | couponA
2 | couponB
3 | couponB
Orders Table:
Orders
Invoice | Customer
------------------
1 | Jack
2 | Jack
3 | Jill
So Jack has used coupons A and B. And Jill has only used coupon B.
If my query were select customers who have not used coupon A, I should get Jill.
This works, but it seems clumsy and slow. Is there a better way?
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Customer NOT IN(
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Coupon = couponA)
GROUP BY Customer
Thanks for looking!
edit:
Here's an SQLFiddle schema
http://sqlfiddle.com/#!2/21d31/6
Updated: We should use prefer to use joins for better performance when its easy to do for us. Join vs. sub-query
Sql Fiddle
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
Note: I changed column name customer for t3 because two joined tables must have different column names
Explanation:
Using inner or sub query is expensive when you have big data. use joins instead, lets learn converting subquery to join
With Subquery We had:
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Converting sub-query to join
First step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA')
) t2
on o.customer != t2.changedname;
2nd step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2 where invoice
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
And that's it, much faster for tables having numerous rows
Original answer:
Use not in. Have a look.
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Edit I have added distinct to make query faster
SQL Fiddle
SELECT DISTINCT o2.customer FROM ORDER o2
LEFT JOIN (promotions p1
JOIN Orders o1 ON p1.cuopon = 'CuoponA' AND p1.invoice = o1.invoice ) p3
ON o2.customer = p3.customer
WHERE p3.customer IS NULL
Try this query instead:
SELECT DISTINCT Customer
FROM Orders o1
WHERE NOT EXISTS (
SELECT 1
FROM Orders o2
INNER JOIN Promotions ON Promotions.Invoice = o2.Invoice
WHERE o1.Customer = o2.Customer AND Coupon = 'couponB')
The idea is to get rid of the GROUP BY by removing a join in the top part of the query, and also eliminate the NOT IN by making a coordinated subquery.
Here is a link to sqlfiddle.
Try this with a right join
SELECT Customer, Coupon
FROM Promotions
RIGHT JOIN Orders ON Promotions.Invoice = Orders.Invoice
AND Coupon = 'couponA'
GROUP BY Customer
HAVING Coupon IS NULL