Sql Query Difficult with sum - mysql

I have a table Customers ( id | name ), with id and name of fornitori,
and another table Money (id | idcustomers | give | have ).
Give and have are int field, with the money that the customer gave and the sum create the balance.
I would like to create a query that shows for each customer the name and the different
of SUM(GIVE) - SUM(HAVE) where money.idcustomers = customers.id

Just grouping by customer id should do the trick:
SELECT Customers.name, SUM(GIVE) - SUM(HAVE) total
FROM Customers
JOIN Money ON Money.idcustomers = Customers.id
GROUP BY Customers.id
SQL Fiddle demo; http://sqlfiddle.com/#!2/339032/2

SELECT C.name, (SUM(M.give)-SUM(M.Have)) AS total
FROM Customors C, Money M
WHERE C.id = M.idcustomers
ORDER BY C.name
GROUP BY C.name

Related

Joining the tables while manipulating SELECT

I am new to SQL and am wondering how to join two tables based on the user ID while, at the same time, manipulating one of the columns. I have the table of orders per ID, and another table with ID demographics. I want to sum the orders per ID, and then join the demographics information.
Separately, the codes work:
This one sums the orders per id up:
SELECT SUM(order) AS expenses, id
FROM orders
GROUP BY id;
And this one joins another table:
SELECT orders.id, demographics.*
FROM orders
JOIN demographics
ON orders.id = demographics.user;
But how can you do the two simultaneously? So the table becomes like:
id | expenses | demographics.1 | demographics.2 | demographics.3 | etc
SELECT orders.id, sum(order) as expenses, max(demographics.1), max(demographics.2),...
FROM orders
JOIN demographics
ON orders.id = demographics.user
Group by orders.id
select
o.id,
d.demographics.1,
d.demographics.2,
d.demographics.3,
sum (o.order) Expenses
from order o,
demographics d
where 1=1
and.o.id = d.user
group by
o.id

How can I SUM values in a joined table without screwing up totals of values in the first table?

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;

Joining two tables for the result?

I have 2 tables in a database person and order tables.
PERSON table:
PERSON_ID | NAME
ORDER table:
ORDER_ID | ORDER_NO | PERSON_ID
I need to display all the orders + a name of corresponding person if it exists, if not just order details.
So far I got up to query:
SELECT ORDER_ID, ORDER_NO, order.PERSON_ID, NAME
FROM person, order
WHERE person.PERSON_ID = order.PERSON_ID AND
person.FIRST_NAME IS NOT NULL;
Which gives me orders only if the name is available whereas I need to display all the orders despite the fact if name is available or not.
Any suggestions?
Yes, you can use LEFT JOIN for that:
SELECT o.order_id, o.order_no, o.person_id, p.name
FROM `order` o
LEFT JOIN person p
ON p.person_id = o.person_id AND p.FIRST_NAME IS NOT NULL
With LEFT JOIN if the name is null it will still give you the orders.

Inactive customers mysql query optimization

I have a customer table with million of records.
Customer
id | name | .....
I also have an orders table with
id | custID | orderDate | ....
I need to find all the people who have not placed an order for more than 30 days.It should also include people who have never placed the order
select name,customer.id from customer where id in
(select custID from orders where datediff(curdate(),orders.orderDate) > 30 )
union
select name,customer.id from customer left join orders on customer.id = orders.custID where orders.id is null
How can i optimize the query ?
Try
select name,t.id
from customer t where
not exists (
select 1
from orders where
custID=t.id
and
datediff(curdate(),orders.orderDate) <= 30 )
Try this one
Select Customer.Custid,
Customer.name
from Customer
left join orders on
customer.custid = orders.custid and
datediff(getdate(),orders.orderdate)>30)
where
orders.id is null

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