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
Related
My tables looks like this
sales
----------------------------------------------------------
id ordernumber quantity category_id price
1 402-9182243-8008368 1 3 22.95
2 406-3666671-8627555 2 3 6.95
3 303-1935495-5532309 1 1 7.95
4 171-5799800-1198702 1 2 159.95
5 403-2398078-4901169 2 2 18.95
category
--------------
id name
1 bikes
2 shoes
3 planes
returns
--------------
id ordernumber quantity costs
1 402-9182243-8008368 1 22.95
2 402-9182243-8008368 5.95 // return shipping fee
And here is my query
SELECT c.name,
SUM(v.quantity) AS sold, # wrong
SUM(s.quantity * s.price) AS turnover, # wrong
SUM(r.costs) AS returncosts,
FROM sales AS s
INNER JOIN categories AS c ON c.id = s.category_id
LEFT JOIN returns AS r ON r.ordernumber = s.ordernumber
GROUP BY c.name
I have some inner joins with aggregate functions.
But I also need "return" with a "Left Join" (I think).
And with Left Join, my aggregate functions dont work anymore.
Left Join adds additional rows. Additional data, for sum().
I need a single query, so every column is sortable later.
I would be happy about any help. Best Regards
It's a semi cartesian product because ordernumber is not unique in returns table.
We can see what's happening if we remove the aggregate functions and return the detail rows.
One possible approach is to pre-aggregate returns in an inline view, so that unique values of ordernumber are returned.
Assuming ordernumber is unique in sales table, then something like this:
SELECT c.name
, SUM(s.quantity) AS sold
, SUM(s.quantity * s.price) AS turnover
, SUM(r.returncosts) AS returncosts
FROM sales s
JOIN categories c
ON c.id = s.category_id
LEFT
JOIN ( SELECT t.ordernumber
, SUM(t.costs) AS returncosts
FROM returns t
GROUP
BY t.ordernumber
) r
ON r.ordernumber = s.ordernumber
GROUP
BY c.name
You can sum the quantity separately from the LEFT JOIN in a sub query as follows:
SELECT t1.name, t1.sold, t1.turnover, SUM(r.costs) AS returncosts
FROM(
SELECT c.name,
SUM(s.quantity) AS sold,
SUM(s.quantity * s.price) AS turnover
FROM sales AS s
INNER JOIN categories AS c ON c.id = s.category_id
GROUP BY name
) t1
LEFT JOIN returns AS r ON r.ordernumber = s.ordernumber
GROUP BY t1.name, t1.sold, t1.turnover
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});
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
I'd like to SELECT a count of the number of customers, the sum of customer order totals, and a count of customers in specific states.
I know how to do this in two queries easily, however the same WHERE constraints will be used, so it seems like it would be better to do it in one query and avoid repetition. I'm eager to improve my SQL but I can't work out how to combine them. Having them as two separate queries feels very clumsy.
Is there a way to combine them? What factors should I consider to determine if combining them is a good idea?
Customers Table
*-------------*-------------*--------------*------------*
| ID_Customer | ID_State | Name | ...etc... |
*-------------*-------------*--------------*------------*
States Table
*-------------*-------------*
| ID_State | Name |
*-------------*-------------*
Orders Table
*----------*-------------*--------------*------------*
| ID_Order | ID_Customer | ...etc... | Total |
*----------*-------------*--------------*------------*
Query 1.1 - Select Count of Customers and Count of Customers in specific states
SELECT
COUNT(*) AS Customers,
SUM(States.Name = 'California') AS California_Customers,
SUM(States.Name = 'New York') AS NewYork_Customers
FROM Customers
INNER JOIN States ON Customers.ID_State = States.ID_State
Query 1.2 - Select Sum of Customer Order Totals
SELECT
SUM(Total) AS SumOfOrderTotals
FROM Orders
INNER JOIN Customers ON Customers.ID_Customer = Orders.ID_Customer
Query 2 - An attempt at combining the queries into one (does not work)
SELECT
COUNT (DISTINCT(Customers.ID_Customer)) AS Customers,
SUM (Orders.Total) AS SumOfOrderTotals,
SUM (States.Name = 'California') AS California_Customers,
SUM (States.Name = 'New York') AS NewYork_Customers
FROM
Customers
INNER JOIN Orders ON Customers.ID_Customer = Orders.ID_Customer
INNER JOIN States ON Customers.ID_State = States.ID_State
Obviously this does not work as it is because the INNER JOIN between Customers and Orders means that States.Names are counted xN (where N is the number of orders a customer has) for each customer, making those totals wrong.
I considered a Subquery, however I'm not sure how to apply one in this case (if that is what I should be doing).
You need to do the aggregation before the join or use subqueries:
SELECT COUNT(DISTINCT(c.ID_Customer)) AS Customers,
o.SumOfOrderTotals,
SUM(s.Name = 'California') AS California_Customers,
SUM(s.Name = 'New York') AS NewYork_Customers
FROM Customers c JOIN
States s
ON c.ID_State = s.ID_State CROSS JOIN
(SELECT SUM(Total) as SumOfOrderTotals
FROM Orders o
) o;
You could also write this as:
SELECT COUNT(DISTINCT(c.ID_Customer)) AS Customers,
(SELECT SUM(Total)
FROM Orders o
) as SumOfOrderTotals,
SUM(s.Name = 'California') AS California_Customers,
SUM(s.Name = 'New York') AS NewYork_Customers
FROM Customers c JOIN
States s
ON c.ID_State = s.ID_State;
You place the subquery where you would have placed any additional field, as another thing in the SELECT clause.
SELECT
COUNT(*) AS Customers,
SUM(States.Name = 'California') AS California_Customers,
SUM(States.Name = 'New York') AS NewYork_Customers,
(SELECT SUM(Total) FROM Orders) AS SumOfOrderTotals
FROM Customers
INNER JOIN States ON Customers.ID_State = States.ID_State;
Noob question for nested query. Having a little issue with this SQL query. Please help. Trying to COUNT 2 fields in the same table and group by month, year. Query returns same results in new and renewal fields.
SELECT MONTH(p.created_at) as Month, YEAR(p.created_at) as Year,
(SELECT COUNT(p.id) FROM payments p
INNER JOIN carts c ON c.payment_id = p.id
INNER JOIN cart_items ci on ci.cart_id = c.id
WHERE ci.item_id = 8) as 'New',
(SELECT COUNT(p.id) FROM payments p
INNER JOIN carts c ON c.payment_id = p.id
INNER JOIN cart_items ci on ci.cart_id = c.id
WHERE ci.item_id = 13) as 'Renewal',
FROM payments p
GROUP BY month, year
Thanks for the help!
Query Expample
Month Year New Renewal
1 2010 1169 556
1 2011 1169 556
1 2012 1169 556
2 2010 1169 556
2 2011 1169 556
From looking at your query I'm guessing you have a items table which holds various possible purchasable items. Of which item_id = 8 corresponds to a "New" product and item_id = 13 corresponds to a "Renewal".
items are associated with the event of a prospective sale in a relation table called cart_items.
But not all carts are sold, but we only want to look at carts which are actually sold and so we're starting with the payments table.
Whether a prospective sale is paid for or not is recorded in the carts table by populating the payment_id.
A payment represents a cart that has been sold, we get the payments.id from payments, we take the payment_id to carts table and get the cart_id which we take to the cart_items table and get the actual items that were sold, and we want to count it separately if the item_id is 8 ('New') / 13 ('Renewal')
The query we're trying to run then is:
SELECT MONTH(p.created_at) as Month,
YEAR(p.create_at) as Year,
sum(if(ci.item_id = 8, 1, 0)) as New,
sum(if(ci.item_id = 13, 1, 0)) as Renewal
FROM payments p,
INNER JOIN carts c on c.payment_id = p.id
INNER JOIN cart_items ci on ci.cart_id = c.id
WHERE ci.item_id in (8,13)
GROUP BY month, year;
We get the entire data set, we group by the fields we want to group by and we total it up by counting the number of times we see item_id = 8 / item_id = 13.
I might have misunderstood the problem domain so please let me know if you need clarification or if I've gotten something wrong.