Join Tables - include in result rows without data in other table - mysql

I have this sql:
SELECT
c.id,
c.name,
SUM(CASE WHEN o.status = 1 THEN 1 ELSE 0 END)
FROM orders o, customers c
WHERE o.customer_id = c.id
GROUP BY c.id;
Take all customers from table customer.
Count how much rows have column status=1 in table orders, grouped by customer_id.
My sql works perfect if customer have at least one order in table orders(any status, even if all have status different by 1).
But if there is an customer which have no order(does not depend status) in table order, it will not appear in result.
Let's say we have those 2 customers:
100 - C1 - 15 orders(7 have status 1)
101 - C2 - No orders
The Result will be:
100 | C1 | 7
How I can include C2?
100 | C1 | 7
101 | C2 | 0

Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
And, you want a LEFT JOIN:
SELECT c.id, c.name,
COUNT(o.customer_id)
FROM customers c LEFT JOIN
orders o
ON o.customer_id = c.id AND o.status = 1
GROUP BY c.id;

You need a left join:
SELECT id, name, COALESCE(sum_orders, 0)
FROM customer c
LEFT JOIN (SELECT customer_id, SUM(CASE WHEN o.status = 1 THEN 1 ELSE 0 END) AS sum_orders
FROM orders
GROUP BY customer_id) o ON c.id = o.customer_id

Related

Select count of total products as well as out of stock products from table

I have to 3 tables: product, product_to_store, store
product table
id quantity status
1 1 1
2 0 1
3 0 1
4 23 1
product_to_store table
store_id product_id
1 1
2 2
1 3
2 4
store table
id name
1 store1
2 store2
To find total products I can run query to fetch all products in table product where status of product is enabled.
select count(*) from product where status=1
total name
2 Store 1
2 store 2
To find total out of stock products I can run below query after joining all 3 tables and using group by store_id:
Select count(*) as outofproducts from product where quantity=0;
Result come like this:
outofproducts name
1 Store 1
1 store 2
But I want combination of above 2 results in single query like below:
outofproducts total name
1 2 Store 1
1 2 store 2
You'd use conditional aggregatiopn, i.e. sum/count over conditions:
select
s.name,
sum(p.quantity > 0) as in_stock,
sum(p.quantity = 0) as out_of_stock,
count(*) as total
from store s
join product_to_store ps on ps.store_id = s.id
join product p on p.id = ps.product_id
group by s.name
order by s.name;
This makes use of MySQL's true = 1, false = 0. If you don't like it, replace sum(p.quantity = 0) with sum(case when p.quantity = 0 then 1 else 0 end) or count(case when p.quantity = 0 then 1 end).
You can start query from store table so that we will get total rows as store table data.
Then use nested query for each store to get out of product and total product count
select
(Select count(*) as outofproducts from product_to_store ps inner join product p on p.id = ps.product_id where quantity=0 and ps.store_id = s.id ) as outofproducts ,
(Select count(*) as count from product_to_store ps inner join product p on p.id = ps.product_id where ps.store_id = s.id ) as totalCount,
s.name
from store s
You could join the related subquery for count
select t1.name, t1.outofproducts, t2.total
from(
select b.id, b.name , count(*) outofproducts
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
where a.quantity = 0
group by b.id, b.name
) t1
inner join (
select b.id, b.name , count(*) total
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
group by b.id, b.name
) t2 on t1.id = t2.id

MySQL Select different data in same row into same Query

I am learning MySQL now. And I want to know more about OUTER JOIN with subqueries. So it means i dont want to use CASE and IF
So now I have a two tables that looks like this.
customer table with c_id and c_name, order table with order_id, c_id, order_type. There are only three kind values for order_type, 'a','b','c'
Now I want to make a summary that shows the order detail for the customer.
The table should looks like this
c_name a b c
person1 1 0 2
person2 0 1 2
person3 0 0 0
This is what i have so far
SELECT c.c_id, COUNT(A), COUNT(B) , COUNT(C)
FROM customer as c
LEFT OUTER JOIN (
SELECT c_id, order_type as A FROM order
WHERE order_type = 'a')
AS first
ON first.c_id = c.c_id
LEFT OUTER JOIN (
SELECT c_id, order_type as B FROM order
WHERE order_type = 'b')
AS second
ON second.c_id = c.c_id
LEFT OUTER JOIN (
SELECT c_id, order_type as C FROM order
WHERE order_type = 'c')
AS third
ON third.c_id = c.c_id
group by c_name
You are looking for a pivot query here:
SELECT
c.c_name,
SUM(CASE WHEN o.order_type = 'a' THEN 1 ELSE 0 END) AS a,
SUM(CASE WHEN o.order_type = 'b' THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN o.order_type = 'c' THEN 1 ELSE 0 END) AS c
FROM customer c
LEFT JOIN order o
ON c.c_id = o.c_id
GROUP BY
c.c_id;
The idea here is to join your two tables together and then aggregate by customer, generating the totals for each type of order using CASE expressions.

How to get all potential orders from a mysql table

I have a mysql query and can't quite figure out how to pull up all potential customers for a certain product_id from a given country_id. I want a result that has just one record per customer.
My tables are orders and customers.
---customers--
customer_id
country_id
name
---orders---
order_id
product_id
customer_id
My current query below gives too many results. Namely records from other product_id's that are not = 1. Here is my query:
SELECT o.order_id
, c.name
, c.customer_id
, c.country_id
, o.product_id
FROM customers c
LEFT
JOIN orders o
ON o.customer_id = c.customer_id
WHERE o.product_id = 1
OR c.country_id = 1
Some sample results might look like this:
order_id name customer_id country_id product_id
1 Joe Smith 1 1 1
2 Joe Smith 1 1 2
3 John Doe 2 1 1
4 Kirk Smith 3 1 1
NULL Ron Rhoden 6 1 1
NULL Sam Smith 7 1 1
For my purposes you can assume that a given customer will only order a given product once. Notice how I get a result for Joe Smith with product_id=2. I don't want that result in my list. The Ron Rhoden and Sam Smith results are desirable for my purposes. How do I filter the product_id<>1 records but still include all the country_id=1 records?
Thanks.
This is my attempt:
SELECT o.order_id
, c.name
, c.customer_id
, c.country_id
, o.product_id
FROM customers c
LEFT
JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IN (SELECT order_id FROM orders WHERE product_id = 1)
OR c.country_id IN (SELECT customers.country_id FROM customers WHERE product_id = 1)
);
If I understand correctly, you want all customers from country "1" that have ordered product "1". I would suggest using exists:
select c.*
from customers c
where c.country_id = 1 and
exists (select 1
from orders o
where o.customer_id = c.customer_id and
o.product_id = 1
);
Ok, I think this works. A bit complicated but I first asked for all the product_id=1 records from orders then did a UNION query and then asked for everyone in customers with country_id=1 but NOT IN the previous product_id=1 results.
SELECT o.order_id, c.name, c.customer_id, c.country_id, o.product_id
FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.product_id = 1 UNION SELECT NULL AS order_id, c.name, c.customer_id, c.country_id, NULL AS product_id
FROM customers c
WHERE c.country_id = 1 AND c.customer_id NOT IN (SELECT c2.customer_id FROM customers c2 INNER JOIN orders o2 ON c2.customer_id=o2.customer_id WHERE o2.product_id=1)

SELECT all rows by WHERE clause

I have the following query which works (without the "WHERE stats.dt" part). I get all users with their data.
My problem is that this query of course results in rows ONLY with users that have stats.dt > $timestampnow-$maxdays_data). But I need ALL users but their values of SUM(upload) or SUM(download) need only to be fetched when stats.dt is larger than tstamp-maxdays. The other rows with values of upload and download where stats.dt is smaller than what I need, can be ignored.
An example would be that the user with nodeid 2 would not be selected because his dt is too small. I do want the user to be selected but just not with data or upload values (they can be 0).
The stats table looks like this
nodeid | dt | upload | download
----------------------------------------
1 | 1381699533 | 345345 | 42324234
1 | 1382899152 | 7575 | 574234
1 | 1380699533 | 764534 | 7235232
2 | 1372899152 | 71455 | 124123
I don't know where to start looking how to solve this so maybe somebody out there can point me in the right direction. Thanks!
SELECT b.id, b.lastname, b.name, c.balance, a.maxdebt, b.warndata, b.warndownload, b.warnupload, b.warndebt, b.cutoffdata, b.cutoffdownload, b.cutoffupload, b.cutoffdebt, b.data, b.download, b.upload, b.warning, b.access, b.cutoffstop
FROM (
SELECT customers.id AS id, SUM(tariffs.value) AS maxdebt
FROM tariffs
LEFT JOIN assignments ON tariffs.id = assignments.tariffid
RIGHT JOIN customers ON assignments.customerid = customers.id
GROUP BY id
) a
JOIN (
SELECT customers.id AS id, UPPER(lastname) AS lastname, customers.name AS name, SUM(stats.upload+stats.download) AS data, SUM(stats.download) AS download, SUM(stats.upload) AS upload, customers.cutoffstop, warndata, warndownload, warnupload, warndebt, cutoffdata, cutoffdownload, cutoffupload, cutoffdebt, nodes.warning, nodes.access
FROM customers
LEFT JOIN nodes ON customers.id = nodes.ownerid
LEFT JOIN stats ON nodes.id = stats.nodeid
LEFT JOIN customerwarnings ON customers.id = customerwarnings.id
WHERE stats.dt > ($timestampnow-$maxdays_data)
GROUP BY id
) b ON a.id = b.id
JOIN (
SELECT customerid, SUM(cash.value) AS balance
FROM cash
GROUP BY customerid
) c ON b.id = c.customerid
Here's a brute force way of doing it. It can almost certainly be simplified, but without knowing more about the table and foreign key structures it's hard to be sure.
What I've done is replace sum(stats.download) with sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.download end) and similarly for upload. I've also changed the join on b to be an outer join:
Select
b.id,
b.lastname,
b.name,
c.balance,
a.maxdebt,
b.warndata,
b.warndownload,
b.warnupload,
b.warndebt,
b.cutoffdata,
b.cutoffdownload,
b.cutoffupload,
b.cutoffdebt,
b.data,
b.download,
b.upload,
b.warning,
b.access,
b.cutoffstop
From (
Select
c.id,
sum(t.value) as maxdebt
From
tariffs t
left join
assignments a
on t.id = a.tariffid
right join
customers
on a.customerid = c.id
Group by
c.id
) a left outer join (
Select
c.id,
upper(lastname) as lastname,
c.name,
sum(s.upload + s.download) as data,
sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.download end) as download,
sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.upload end) as upload,
c.cutoffstop,
warndata,
warndownload,
warnupload,
warndebt,
cutoffdata,
cutoffdownload,
cutoffupload,
cutoffdebt,
n.warning,
n.access
From
customers c
left join
nodes n
on c.id = n.ownerid
left join
stats s
on n.id = s.nodeid
left join
customerwarnings w
on c.id = w.id
Group By
c.id
) b
On a.id = b.id
inner join (
Select
customerid,
sum(cash.value) as balance
From
cash
Group By
customerid
) c
on a.id = c.customerid

Subquery select as filed

I tried to make a subselect in a MySQL selct statement e.g. like the following one:
SELECT
c.id AS id,
c.name AS name,
(SELECT count(*) FROM orders o WHERE o.user_id = c.id) AS order_count
FROM
customers c
Any ideas why this does not work in MySQL?
Is it possible to do something like that in MySQL?
Tried it in Version 3.23.58 and 5.1.60.
Thanks in advance!
You missed , after name, try this:
SELECT
c.id AS id,
c.name AS name,
(SELECT count(*) FROM orders o WHERE o.user_id = c.id) AS order_count
FROM
customers c
In order to avoid these sorts of errors (like missing commas), I like to write queries out like this...
SELECT c.id
, c.name
, COALESCE(COUNT(o.user_id),0) order_count
FROM customers c
LEFT
JOIN orders o
ON o.user_id = c.id
GROUP
BY c.id;
SELECT
c.id AS id ,
c.name AS name ,
(SELECT count(*)
FROM orders o
WHERE o.user_id = c.id) AS order_count
FROM
customers c
Use ',' after every column
Another method using joins
SQLFIDDLE DEMO
Sample data:
ID NAME
1 john
2 jack
3 kelly
ID USERID
10 1
11 2
12 1
13 3
14 2
15 3
16 2
17 4
18 1
Query:
SELECT count(o.userid), c.id, c.name
FROM cusomter c
LEFT JOIN orders o
ON c.id = o.userid
GROUP BY c.id
;
You may add case when to represent null as a 0 if a customer doesen't have any orders. You choice.
Results:
COUNT(O.USERID) ID NAME
3 1 john
3 2 jack
2 3 kelly