SQL Inner Join Group By - mysql

Table - Customers
id FirstName LastName
23 James Smith
24 Tom Raven
25 Bob King
Table - Orders
id CustomerID
30 23
31 24
32 23
33 25
Table - Products
OrderID Product1 Product2 Product3
30 1 0 0
31 2 0 0
32 0 1 1
33 2 1 0
I want to count the total number of Products for each Customer so answers would be:
CustomerID FirstName LastName Total
23 James Smith 3
24 Tom Raven 2
25 Bob King 3
So far I've got for the SQL query:
SELECT Customers.id, Orders.id,
FROM Customers
INNER JOIN Orders ON Customers.id = Orders.CustomerID
INNER JOIN Products ON Orders.id = Products.OrderID
Not sure how to count up the products though.
Any help would be much appreciated.

select c.id as CustomerID
,(sum(p.Product1) + sum(p.Product2) + sum(p.Product3)) as ProductCount
from Customers c
inner join Orders o on c.id = o.CustomerID
inner join Products p on o.id = p.OrderID
group by c.id

You could use
SELECT c.id as CustomerID, c.firstname, c.lastname,
sum(p.Product1 + p.Product2 + p.Product3) as total
FROM Customers c
INNER JOIN Orders o
ON c.id=o.CustomerID
INNER JOIN Products p
ON o.id=p.OrderID
group by c.id,c.firstname, c.lastname;
And as #Thorsten Kettner's comment, you should consider normalizing your table design.

Add a GROUP BY clause! Normally it includes the selected columns that are not arguments to a set function.
SELECT O.CustomerID, C.FirstName, C.LastName, count(*) as Total
FROM Customers C
INNER JOIN Orders O ON C.id = O.CustomerID
INNER JOIN Products P ON O.id = P.OrderID
GROUP BY O.CustomerID, C.FirstName, C.LastName
PS. Now using table aliases, to save some typing.

SELECT Customers.id, Customers.firstname, Customers.lastname,count(*) as total FROM Customers
INNER JOIN Orders
ON Customers.id=Orders.CustomerID
INNER JOIN Products
ON Orders.id=Products.OrderID
group by Customers.id,Customers.firstname, Customers.lastname

A more conventional approach might be as follows:
DROP TABLE IF EXISTS customers;
CREATE TABLE customers
(customer_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,firstname VARCHAR(20) NOT NULL
,lastname VARCHAR(20) NOT NULL
);
INSERT INTO customers VALUES
(23,'James','Smith'),
(24,'Tom','Raven'),
(25,'Bob','King');
DROP TABLE IF EXISTS orders;
CREATE TABLE orders
(order_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,customer_id INT NOT NULL
);
INSERT INTO orders VALUES
(30 ,23),
(31 ,24),
(32 ,23),
(33 ,25);
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail
(order_id INT NOT NULL
,product_id INT NOT NULL
,quantity INT NOT NULL
,PRIMARY KEY(order_id,product_id)
);
INSERT INTO order_detail VALUES
(30 ,1 ,1),
(31 ,1 ,2),
(33 ,1 ,2),
(32 ,2 ,1),
(33 ,2 ,1),
(32 ,3 ,1);
SELECT c.*
, SUM(od.quantity) total
FROM customers c
JOIN orders o
ON o.customer_id = c.customer_id
JOIN order_detail od
ON od.order_id = o.order_id
GROUP
BY c.customer_id;
+-------------+-----------+----------+-------+
| customer_id | firstname | lastname | total |
+-------------+-----------+----------+-------+
| 23 | James | Smith | 3 |
| 24 | Tom | Raven | 2 |
| 25 | Bob | King | 3 |
+-------------+-----------+----------+-------+

SELECT DISTINCT CustomerID, FirstName, LastName, Total
FROM ( SELECT id AS CustomerID, FirstName, LastName FROM Customers ) AS c
NATURAL JOIN
( SELECT id AS OrderID, CustomerID FROM Orders ) AS o
NATURAL JOIN
( SELECT OrderID, SUM( Product1 + Product2 + Product3 ) AS Total
FROM Products
GROUP
BY OrderID ) AS p;

Related

mysql count on multiple columns from another table

I would like to get count of two columns from another table. For example, the source data is as below for the table called employee_master
emp_code dept_id sub_dept_id
1 10 22
2 11 20
3 10 22
4 10 22
5 11 20
6 10 21
Another source table called punching_data_table as shown below which is being joined with employee_master.
emp_code present
1 10:01 AM
2 10:02 AM
3 10:02 AM
4 10:03 AM
5 10:11 AM
6 10:09 AM //biometric punching time shown
The result is suppose to look like below.
dept_id dept_count sub_dept_id sub_dept_count
10 4 22 3
10 4 21 1
11 2 20 2
Tried with following code but it doesn't count seperately for both columns even though applied group by employee_master.dept_id, employee_master.sub_dept_id. Please note that there is only 1 column common between both table that is emp_code.
SELECT p.emp_code
, e.emp_code
, COUNT(e.dept_id) dept_id_count
, COUNT(e.sub_dept_id) sub_dept_id_count
FROM punching_data_table p
LEFT
JOIN employee_master e
ON e.emp_code = p.emp_code
GROUP
BY e.dept_id
, e.sub_dept_id
Any help on the subject with explanation will be highly appreciated.
This query (SQLFiddle) will give you the output that you specify:
SELECT em1.dept_id, em2.dept_count, em1.sub_dept_id, COUNT(em1.sub_dept_id) AS sub_dept_count
FROM employee_master em1
JOIN (SELECT dept_id, COUNT(dept_id) as dept_count
FROM employee_master e
JOIN punching_data_table p
ON e.emp_code = p.emp_code
GROUP BY dept_id) em2
ON em1.dept_id = em2.dept_id
WHERE EXISTS (SELECT * FROM punching_data_table pdt WHERE pdt.emp_code = em1.emp_code)
GROUP BY dept_id, sub_dept_id
Output:
dept_id dept_count sub_dept_id sub_dept_count
10 4 21 1
10 4 22 3
11 2 20 2
If you have a department names table e.g.
CREATE TABLE dept_master (dept_id INT, dept_name VARCHAR(20));
INSERT INTO dept_master VALUES
(10, 'Department 10'),
(11, 'Department 11');
You can join that as well using this query (SQLFiddle):
SELECT d.dept_name, em2.dept_count, em1.sub_dept_id, COUNT(em1.sub_dept_id) AS sub_dept_count
FROM employee_master em1
JOIN (SELECT dept_id, COUNT(dept_id) as dept_count
FROM employee_master e
JOIN punching_data_table p
ON e.emp_code = p.emp_code
GROUP BY dept_id) em2
ON em1.dept_id = em2.dept_id
JOIN dept_master d on d.dept_id = em1.dept_id
WHERE EXISTS (SELECT * FROM punching_data_table pdt WHERE pdt.emp_code = em1.emp_code)
GROUP BY d.dept_id, sub_dept_id
And then get department names in the output instead of ids:
dept_name dept_count sub_dept_id sub_dept_count
Department 10 4 21 1
Department 10 4 22 3
Department 11 2 20 2
Try this query
SELECT emp_code, MAX(dept_id_count) as dept_id_count, MAX(sub_dept_id_count) as sub_dept_id_count
FROM
(SELECT p.emp_code , COUNT(e.dept_id) dept_id_count, 0 as sub_dept_id_count
FROM punching_data_table p
LEFT JOIN employee_master e ON e.emp_code = p.emp_code
GROUP BY e.dept_id
UNION
SELECT p.emp_code, 0 as dept_id_count, COUNT(e.sub_dept_id) sub_dept_id_count
FROM punching_data_table p
LEFT JOIN employee_master e ON e.emp_code = p.emp_code
GROUP BY e.sub_dept_id) as subQuery
GROUP BY emp_code
There are some things which are not clear in your question for example do you want distinct employees or just a occurrences of employees, what happens if an employee does not exist in punching_data. I have made some assumptions in this answer but the crux of it is that you create 2 sub queries and join them on dept id which will force a 1 to many join
so given
drop table if exists employee_master,punching_data_table;
create table employee_master
(emp_code int, dept_id int, sub_dept_id int);
insert into employee_master values
(1 , 10 , 22),
(2 , 11 , 20),
(3 , 10 , 22),
(4 , 10 , 22),
(5 , 11 , 20),
(6 , 10 , 21 ),
(7 , 10 , 21);
create table punching_data_table(emp_code int);
insert into punching_data_table values
(1),
(2),
(3),
(4) ,
(5),
(6);
select a.dept_id,a.empcodes,s.sub_dept_id,s.empcodes from
(
select dept_id ,count(distinct emp_code) empcodes
from employee_master
where emp_code in (select emp_code from punching_data_table)
group by dept_id
) a
join
(select dept_id , sub_dept_id, count(distinct emp_code) empcodes
from employee_master
where emp_code in (select emp_code from punching_data_table)
group by dept_id,sub_dept_id) s
on s.dept_id = a.dept_id
order by a.dept_id,s.sub_dept_id
Result
+---------+----------+-------------+----------+
| dept_id | empcodes | sub_dept_id | empcodes |
+---------+----------+-------------+----------+
| 10 | 4 | 21 | 1 |
| 10 | 4 | 22 | 3 |
| 11 | 2 | 20 | 2 |
+---------+----------+-------------+----------+
3 rows in set (0.00 sec)
This result can be achieved without punching_data_table
SELECT t1.dept_id,
t2.sub_dept_id,
t1.count_dept,
t2.count_sub
FROM (SELECT dept_id,
sub_dept_id,
Count(dept_id) AS count_dept
FROM employee_master
GROUP BY dept_id)t1
LEFT JOIN (SELECT sub_dept_id,
dept_id,
Count(sub_dept_id) AS count_sub
FROM employee_master
GROUP BY sub_dept_id)t2
ON t1.dept_id = t2.dept_id

Mysql query select customers with no orders in each year

i have 2 table's one with customers and one with orders
SELECT customers.customer_name, orders.order_date
FROM customers
Left
Join orders on (customers.customer_id = orders.customer_id)
WHERE not orders.customer_id IN (SELECT customer_id from orders where Year(order_date) = Year(#Parameter1))
and not orders.order_date is null
this works but i want to do this for each year to get somthing like this as result
|Year | customer_id |
|2010 | 1 |
|2010 | 2 |
|2011 | 2 |
|2011 | 3 |
|2012 | 1 |
You want a list showing customers and years that are not present in the orders table. So get a list of all customers combined with all years and then subtract the customers and years that you find in the orders table:
select o.yr, c.customer_id
from customers c
cross join (select distinct year(order_date) as yr from orders) o
where (c.customer_id, o.yr) not in (select customer_id, year(order_date) from orders);
Scorpio is sort of right, you do have to use the year function:
SELECT Year(orders.order_date), customers.customer_id
FROM customers
LEFT JOIN orders
ON (customers.customer_id = orders.customer_id)
WHERE NOT orders.customer_id
IN (
SELECT customer_id
FROM orders
WHERE Year(order_date) = Year(#Parameter1)
)
AND
NOT orders.order_date is NULL
You can use the year method in the SELECT section of your query
Update:
To display all the customers who didn't order in a year, you don't need the join. You can do it with a single subselect:
SELECT #Parameter1 AS Year, customer_id
FROM customers
WHERE customers.customer_id NOT IN (
SELECT customer_id
FROM orders
WHERE Year(order_date) = Year(#Parameter1)
)

Sql Join Three table [duplicate]

This question already has answers here:
Joining three tables using MySQL
(11 answers)
Closed 6 years ago.
Here is my table ..and i want this output:
pid | pname | custname | quantity | total base price | sale price | profit
schema:
create table customers
(
cid int,
cname varchar(1000),
cg varchar(1000)
)
create table prod
(
pid int,
pname varchar(1000),
baseprice int,
saleprice int
)
create table orders
(
oid int,
custid int,
pid int,
quantity int,
odate date
)
How do I write a query for this?
--pid | pname | custname | quantity | total base price | sale price | profit
select o.pid, p.pname, c.cname AS custname, SUM(o.quantity) AS quantity,
SUM(p.baseprice) AS 'total base price', SUM(p.saleprice) AS 'sale price',
SUM(p.baseprice) - SUM(p.saleprice) AS profit -- change this math to what you need
from orders o join prod p
on o.pid = p.pid
join customers c
on o.custid = c.cid
group by o.pid, p.pname, c.cname
select
a.pid ,
a.pname ,
c.cname ,
b.quantity ,
a.baseprice * b.quantity as TotalBasePrice ,
a.saleprice*b.quantity as TotalSalePrice ,
a.saleprice*b.quantity - a.baseprice * b.quantity as profit
from prod as a
inner join orders as b on a.pid = b.pid
inner join customers as c on b.custid = c.cid

MySQL: Update values with sum of two tables

I have a table setup like this
Liabilities:
Id | CustomerId | liabilities
---------------------------
9 90 1000
...
Payments:
Id | CustomerId | Payment
---------------------------
3 90 2500
4 91 1000
...
Customer:
Id | balance
---------------------------
90 1500
91 1000
...
As you can see, the balance for a customer is the sum of all its payments minus the sum of all its liabilities. What is an SQL query to update the balance?
You can do it using an UPDATE statement with LEFT JOIN operations to derived tables containing Payments and Liabilities aggregates:
UPDATE Customer AS c
LEFT JOIN (
SELECT CustomerId, SUM(Payment) AS TotalPayment
FROM Payments
GROUP BY CustomerId
) AS p ON c.Id = p.CustomerId
LEFT JOIN (
SELECT CustomerId, SUM(liabilities) AS TotalLiabilities
FROM Liabilities
GROUP BY CustomerId
) AS l ON c.Id = l.CustomerId
SET balance = COALESCE(TotalPayment, 0) - COALESCE(TotalLiabilities, 0)
Demo here
Alternatively, you can use correlated subqueries in the UPDATE statement:
UPDATE Customer AS c
SET balance = COALESCE((SELECT SUM(Payment)
FROM Payments
WHERE CustomerId = c.Id) , 0)
-
COALESCE((SELECT SUM(liabilities)
FROM Liabilities
WHERE CustomerId = c.Id) , 0)
Demo here

SUMs from multiple JOINed tables MSSQL

I'm trying to generate a report that shows outstanding balances based on dynamic calculations across several tables. Currently this report is being generated using a server side language that runs a query, loops through the query, and runs another query against the result. My goal is to perform all the calculations within MSSQL.
Here are the tables / columns with some example data:
TEST_ORDER Table
orderid customerid serviceamount
1001 2001 75.00
1002 2002 85.00
1003 2001 25.00
1004 2003 10.00
TEST_CUSTOMER Table
customerid customername
2001 'Initech'
2002 'Dunder Mifflin'
2003 'Paper Street Soap Co'
TEST_ORDERITEM Table
orderitemid orderid itemamount
5001 1001 50.00
5002 1001 150.00
5003 1002 15.00
5004 1004 200.00
5005 1004 200.00
TEST_TRANSACTION Table
transactionid orderid amount
9001 1001 75.00
9002 1002 25.00
9003 1002 50.00
9004 1003 55.00
9005 1001 50.00
9006 1001 150.00
The results I'm looking for are:
orderid customername totalorder totalpaid balance
1001 'Initech' 275.00 275.00 0.00
1002 'Dunder Mifflin' 100.00 75.00 -25.00
1003 'Initech' 25.00 55.00 30.00
1004 'Paper Street Soap Co' 410.00 0.00 -410.00
Here's the query I came up with:
SELECT
o.orderid, c.customername, o.serviceamount,
(o.serviceamount + SUM( i.itemamount )) AS totalorder,
SUM( t.amount ) AS totalPaid,
( SUM( t.amount ) - (o.serviceamount + SUM( i.itemamount )) ) AS balance
FROM
test_order o
INNER JOIN test_customer c ON o.customerID = c.customerID
LEFT OUTER JOIN test_transaction t ON o.orderID = t.orderID
LEFT OUTER JOIN test_orderitem i ON o.orderID = i.orderID
WHERE
1=1
GROUP BY
o.orderid, c.customername, o.serviceamount,
o.serviceamount
And here are the results:
orderid customername totalorder totalpaid balance
1001 'Initech' 675.00 550.00 -125.00
1002 'Dunder Mifflin' 115.00 75.00 -40.00
1003 'Initech' null 55.00 null
1004 'Paper Street Soap Co' 410.00 null null
The problems I'm having are:
1. Records in test_orderitem are duplicated, e.g. for order 1002, the serviceamount is $85 and there is only one orderitem for $15, however, th totalorder is calculated for $115. I think the problem has to do with the JOIN iterating twice.
2. 'null' is showing up when no records are returned (for obvious reasons). I haven't started working on this, as I've been testing in MySQL, and I want to figure out the JOIN issue before carrying this back into MSSQL.
Thank you for any assistance you might be able to offer...
For MS-SQL: Here you, just replace table names in final query with your own.
DECLARE #Test_Order TABLE (orderid int, customerid int, serviceamount money)
INSERT INTO #Test_Order(orderid,customerid,serviceamount)
SELECT 1001,2001,75.00 UNION ALL
SELECT 1002,2002,85.00 UNION ALL
SELECT 1003,2001,25.00 UNION ALL
SELECT 1004,2003,10.00
DECLARE #Test_Customer TABLE (customerid int, customername varchar(100))
INSERT INTO #Test_Customer(customerid,customername)
SELECT 2001,'Initech' UNION ALL
SELECT 2002,'Dunder Mifflin' UNION ALL
SELECT 2003,'Paper Street Soap Co'
DECLARE #Test_OrderItem TABLE (orderitemid int, orderid int, itemamount money)
INSERT INTO #Test_OrderItem (orderitemid, orderid, itemamount)
SELECT 5001,1001,50.00 UNION ALL
SELECT 5002,1001,150.00 UNION ALL
SELECT 5003,1002,15.00 UNION ALL
SELECT 5004,1004,200.00 UNION ALL
SELECT 5005,1004,200.00
DECLARE #Test_Transaction TABLE (transactionid int, orderid int, amount money)
INSERT INTO #Test_Transaction (transactionid, orderid, amount)
SELECT 9001,1001,75.00 UNION ALL
SELECT 9002,1002,25.00 UNION ALL
SELECT 9003,1002,50.00 UNION ALL
SELECT 9004,1003,55.00 UNION ALL
SELECT 9005,1001,50.00 UNION ALL
SELECT 9006,1001,150.00
SELECT O.orderid,
C.customername,
SUM(ISNULL(O.serviceamount,0)+ISNULL(I.itemamount,0)) AS totalorder,
SUM(ISNULL(T.amount,0)) AS totalpaid,
(SUM(ISNULL(T.amount,0))) - (SUM(ISNULL(O.serviceamount,0)+ISNULL(I.itemamount,0))) as balance
FROM #Test_Customer C
JOIN #Test_Order O
ON C.customerid=O.customerid
LEFT JOIN (SELECT orderid, SUM(itemamount) AS itemamount FROM #Test_OrderItem GROUP BY orderid) I
ON O.orderid=I.orderid
LEFT JOIN (SELECT orderid, SUM(amount) AS amount FROM #Test_Transaction GROUP BY orderid) T
ON O.orderid=T.orderid
GROUP BY O.orderid, C.customername
ORDER BY O.orderid, C.customername
http://sqlfiddle.com/#!9/ae0be/1
SELECT
o.orderid,
c.customername,
(coalesce(o.serviceamount,0)+coalesce(i.itemsum,0)) totalorder,
t.t_sum totalpaid,
(coalesce(t.t_sum,0)-coalesce(o.serviceamount,0)-coalesce(i.itemsum,0)) balance
FROM TEST_ORDER o
LEFT JOIN (
SELECT orderid, SUM(itemamount) itemsum
FROM TEST_ORDERITEM
GROUP BY orderid
) i
ON o.orderid = i.orderid
LEFT JOIN (
SELECT orderid, SUM(amount) t_sum
FROM TEST_TRANSACTION
GROUP BY orderid
) t
ON o.orderid = t.orderid
LEFT JOIN TEST_CUSTOMER c
ON o.customerid = c.customerid
ORDER BY o.orderid
Give this a try:
SELECT
summary.orderid,
summary.customername,
summary.totalorder,
summary.totalpaid,
summary.totalorder - summary.totalpaid AS balance
FROM
(SELECT
o.orderid,
c.customername,
(SELECT SUM(oi.itemamount) FROM test_orderitem oi WHERE oi.orderid = o.orderid) AS totalorder,
(SELECT IFNULL(SUM(t.amount), 0) FROM test_transaction t WHERE t.orderid = o.orderid) AS totalpaid
FROM
test_order o,
test_customer c
WHERE
o.customerid = c.customerid) summary