MySQL union all columns - mysql

I have a mysql table for cases. It has product name for each product there can be multiple records with different statuses.
MySQL Fiddler : SQL Fiddler Link
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product
union all
SELECT
product , count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product
It is giving me result as
But I want result as
What exactly I am missing in query. Any help will be appreciated.

Try the following code via using Inner Join:
Select a.product, totalopen , totalclosed
from (
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product ) a
inner join (
SELECT
product ,count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product ) b
on a.product = b.product.
Updated:-
For the products that has only one record, so its status has only CLOSED or OPEN use Full Outer Join instead of inner join as next:-
Select isnull(a.product,b.product) product, isnull(totalopen,0) totalopen , isnull(totalclosed,0) totalclosed
from (
SELECT
product , count(*) as totalopen
FROM cases
where status='OPEN'
group by product ) a
full outer join (
SELECT
product ,count(*) as totalclosed
FROM cases
where status='CLOSED'
group by product ) b
on a.product = b.product

Use case expressions to conditional aggregation:
SELECT product,
count(case when status='OPEN' then 1 end) as totalopen,
count(case when status='CLOSED' then 1 end) as totalclosed
FROM cases
where status in ('OPEN', 'CLOSED')
group by product
If you want to include products with only other status (e.g. pending), remove the WHERE clause.

Use case expression and count the relevant ones.
SELECT
product ,
count(case when status = 'OPEN' then 1 end) as totalopen,
count(case when status = 'CLOSED' then 1 end) as totalclosed
FROM cases
group by product;
It uses the fact that
COUNT(1) = 1
COUNT(NULL) = 0
and
case when 1 = 1 then 1 end -- return 1
case when 1 = 2 then 1 end -- return null

Related

Select customers with no invoices after date

I have Client and Invoice tables. They have one-to-many relationship, where Client.id = Invoice.client_id.
Client columns:
id
Invoice columns:
id,
client_id,
invoice_date
Of course the example is simplified to relevant data.
I am trying to select customers who did NOT have invoices after '2010-01-01'.
I can't figure out any working way to do this. Some routes I took look like this (there many other variations, but no point displaying the here):
SELECT c.id, COUNT(i.invoice_date > "2010-01-01") AS cnt
FROM Client AS c LEFT JOIN Invoice i ON i.client_id = c.id
GROUP BY c.id HAVING cnt = 0
and
SELECT client_id, COUNT(invoice_date > '2010-01-01') as cnt
FROM Invoice
GROUP BY client_id HAVING cnt = 0
You can use a sub-query with NOT EXISTS like this:
SELECT *
FROM Client
WHERE NOT EXISTS (
SELECT 1
FROM Invoice
WHERE Invoice.invoice_date > '2010-01-01' AND Invoice.client_id = Client.id
)
You can also use SUM with CASE or IF:
-- CASE
SELECT c.id, SUM(CASE WHEN i.invoice_date > '2010-01-01' THEN 1 ELSE 0 END) AS cnt
FROM Client AS c LEFT JOIN Invoice i ON i.client_id = c.id
GROUP BY c.id
HAVING cnt = 0
-- IF
SELECT c.id, SUM(IF(i.invoice_date > '2010-01-01', 1, 0)) AS cnt
FROM Client AS c LEFT JOIN Invoice i ON i.client_id = c.id
GROUP BY c.id
HAVING cnt = 0
You can also use COUNT, but with CASE or IF:
-- CASE
SELECT client_id, COUNT(CASE WHEN invoice_date > '2010-01-01' THEN 1 ELSE NULL END) as cnt
FROM Invoice
GROUP BY client_id HAVING cnt = 0
-- IF
SELECT client_id, COUNT(IF(invoice_date > '2010-01-01', 1, NULL)) as cnt
FROM Invoice
GROUP BY client_id HAVING cnt = 0
demo on dbfiddle.uk
You can also use NOT IN with a sub-query
SELECT * FROM client
WHERE id NOT IN (
SELECT client_id
FROM invoice
WHERE invoice_date>'2020-01-01'
);
Try this :-
Select Client.* from Client Client left join Invoice Invoice on
client.id = Invoice.client_id and Invoice.invoice_date > "2010-01-01"
where Invoice.client_id is null;
Basically you include only the subset of the data after "2010-01-01" from the invoice table.

LEFT JOIN with multiple nested select

The following statement surprisingly works, but I'm not sure joining the same table 3 times is efficient. I had to disable ONLY_FULL_GROUP_BY in order for it to work.
There are 2 tables in play. One is the main table with Distributor information, the second is a table of purchases that contains the amount, date, and id of the associated Distributor in the main table (assoc).
There are 3 things I needed. Year to date sales, which SUMS the amount of a certain Distributor's sales from the current year. Last year sales, which does the same for the previous year. Then finally just get the latest purchase date and amount.
The user needs to be able to filter by these values (lys, ytd, etc...) so joining them as variables seems like the way to go. The DB size is about 7,000 records.
SELECT
d.*,
ytd_total,
lys_total,
last_amount,
last_purchase
FROM Distributor as d
LEFT JOIN (
SELECT
assoc, SUM(amount) ytd_total
FROM purchases
WHERE db = 1 AND purchase_date >= '{$year}-01-01'
GROUP BY assoc
) AS ytd
ON ytd.assoc = d.id
LEFT JOIN (
SELECT
assoc, SUM(amount) lys_total
FROM purchases
WHERE db = 1 AND purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31'
GROUP BY assoc
) AS lys
ON lys.assoc = d.id
LEFT JOIN (
SELECT
assoc, amount last_amount, purchase_date last_purchase
FROM purchases
WHERE db = 1
GROUP BY assoc
) AS lst
ON lst.assoc = d.id
WHERE ........
You can do more work in each aggregation query. I think this is more whatyou want:
select d.*, pa.ytd_total, pa.lys_total, pa.last_purchase_date, p.amount
from distributor d left join
(select p.assoc,
sum(case when p.purchase_date >= '{$year}-01-01' then p.amount end) as ytd_total,
sum(case when p.purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31' then p.amount end) as lys_total,
max(p.purchase_date) as last_purchase_date
from purchases p
where p.db = 1
group by p.assoc
) pa left join
purchases p
on pa.assoc = p.assoc and pa.last_purchase_date = p.purchase_date;

mysql query to count no of rows in joining three tables and count rows of one table

I have a problem in MYSQL query.
I have three tables one is voucher table other clients and third is voucher_client.
In voucher_client table I have voucher id column that relate to voucher table and I want to count related rows from client table.
Like if voucher table has id 2 and voucher clients are 2 then query will check from client table age_group column where age_group is adult ,child or infant
here some pictures of tables for more detail.
Please help me out
Voucher table
Client table
Voucher client table
I am trying to do like this
SELECT `v`.*, `a`.`name` as `agent_name`, COUNT(CASE WHEN c.age_group = 'Adult' THEN c.id END) AS t_adult, COUNT(CASE WHEN c.age_group = 'Child' THEN c.id END) AS t_child, COUNT(CASE WHEN c.age_group = 'Infant' THEN c.id END) AS t_infant, COUNT(c.id) as total FROM `voucher` `v` JOIN `voucher_client` `vc` ON `vc`.`voucher_id`=`v`.`id` JOIN `client` `c` ON `vc`.`client_id`=`c`.`id` JOIN `tbl_users` `a` ON `a`.`userId`=`v`.`agent_id` LEFT JOIN `voucher_hotel` `vh` ON `vh`.`voucher_id`=`v`.`id` WHERE `v`.`isDeleted` =0 GROUP BY `v`.`id` ORDER BY `v`.`id` DESC
expected output like this
voucher_id t_adult t_child t_infant
1 2 0 0
2 1 0 0
If only want to show v.id in the result, then replace v.* by v.id in the query.
(Btw, most databases wouldn't even allow a * when there's group by. MySql deviates from the ANSI SQL standard in that aspect.)
And if you need to join to an extra table with an 1-N relationship? Then you can count the distinct values. So that the totals only reflect the unique clientid's.
SELECT
v.id AS voucher_id,
COUNT(DISTINCT CASE WHEN c.age_group = 'Adult' THEN c.id END) AS t_adult,
COUNT(DISTINCT CASE WHEN c.age_group = 'Child' THEN c.id END) AS t_child,
COUNT(DISTINCT CASE WHEN c.age_group = 'Infant' THEN c.id END) AS t_infant
-- , COUNT(*) as total
-- , COUNT(c.id) as total_clientid -- count on value doesn't count NULL's
-- , COUNT(DISTINCT c.id) as total_unique_clientid
FROM voucher v
JOIN voucher_client vc ON vc.voucher_id = v.id
JOIN client c ON c.id = vc.client_id
-- LEFT JOIN voucher_hotel vh ON vh.voucher_id = v.id
WHERE v.isDeleted = 0
-- AND c.age_group = 'Adult' -- uncomment this to only count the adults
GROUP BY v.id
ORDER BY v.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 Use HAVING in a Subquery as Part of a Select Statement

I'm working on a query to return how many activists have volunteered this year by team more than once; I got it to work as a standalone query:
SELECT Activists.team, COUNT(id) AS Activists
FROM
(
SELECT e.id, v.team,
COUNT(CASE WHEN (e.status="Completed" AND right(e.date,2)="15") THEN e.id END) AS count
FROM actiontable e
LEFT JOIN persontable v ON v.id = e.id
GROUP BY e.id, v.team
HAVING (count > 1)
) AS Activists
GROUP BY Activists.team;
But I can't quite figure out how to get it to work in a longer SELECT statement. The problem I'm running into is that I have many other (more simple) parts of the query to return things by team also in the SELECT statement like:
COUNT(DISTINCT CASE WHEN v.WillCanvass = "X" THEN v.id END)
So obviously I can't have the HAVING (count > 1) part of the query for the activists because then it would affect all the other parts of my SELECT statement -- so I need the subquery above to only affect the sole part where I'm working on.
I made a SQL Fiddle with sample schema to help with the above query that works -- but the ideal would be to get an output that looks similar to this, where the Activists subquery doesn't affect the WillCanvass column (even though I made up the numbers below):
Team Activists WillCanvass
Team A 2 2
Team B 8 5
Team C 7 3
Hope that makes sense -- thanks!
EDIT
My best shot at what I want -- though the query gives me errors -- looks like this:
SELECT a.team as team,
COUNT(v.*) as activists,
SUM(CASE WHEN v.WillCanvass = "X" THEN 1 ELSE 0 END) as WillCanvass
FROM
persontable v
left join
(
SELECT e.id,
v.team,
v.WillCanvass,
COUNT(*) as count
FROM actiontable e
LEFT JOIN persontable v ON v.id = e.id
WHERE e.status="Completed" AND right(e.date,2)="15"
GROUP BY e.id
HAVING (count > 1)) as a
GROUP BY team;
There's an updated SQL Fiddle of it here.
Not sure exactly what you try to achieve. I first get the list of activists with the right criteria and then GROUP BY team.
SELECT a.team as team,
COUNT(*) as activists,
SUM(CASE WHEN a.WillCanvass = "X" THEN 1 ELSE 0 END) as WillCanvass
FROM (
SELECT e.id,
v.team,
v.WillCanvass,
COUNT(*) as count
FROM actiontable e
LEFT JOIN persontable v ON v.id = e.id
WHERE e.status="Completed" AND right(e.date,2)="15"
GROUP BY e.id
HAVING (count > 1)) as a
GROUP BY team
try this way:
http://sqlfiddle.com/#!2/e186da/5
SELECT v.team as team,
SUM(CASE WHEN (e.status="Completed" AND YEAR(e.date)="2015") THEN 1 ELSE 0 END) AS count
FROM actiontable e
LEFT JOIN persontable v ON v.id = e.id
GROUP BY v.team
HAVING (count > 1);
SELECT v.team as team,
SUM(CASE WHEN (e.status="Completed" AND YEAR(e.date)="2015") THEN 1 ELSE 0 END) AS count,
SUM(CASE WHEN (v.WillCanvass='X') THEN 1 ELSE 0 END) AS WillCanvass
FROM actiontable e
LEFT JOIN persontable v ON v.id = e.id
GROUP BY v.team
I got it -- thanks for helping me get there, but it's pretty different than anything above. I had to take the HAVING clause out completely in order have the first part of the SELECT statement (WillCanvass) be completely independent from the actiontable part of it:
SELECT a.team as team,
COUNT(case when a.X > 1 then a.id else null end) as activists,
SUM(CASE WHEN a.WillCanvass = "X" THEN 1 ELSE 0 END) as WillCanvass
FROM (
SELECT v.id,
v.team,
v.WillCanvass,
COUNT(case when e.status="Completed" AND right(e.date,2)="15" then e.id else null end) as X
FROM actiontable e
RIGHT JOIN persontable v ON v.id = e.id
GROUP BY v.id, v.team, v.WillCanvass
) as a
GROUP BY team;
And here's a new SQL Fiddle to see how it works.