COUNT total of group by mysql - mysql

This mysql query gives me the number of sales of my products (total and total_staff) grouped by days and hours of the day. I want the sum of total and total_staff for each product (not grouped by anything, expect by id). Can I do this in the same query?
SELECT p.name, p.color, DATE(date_create) as jour,HOUR(date_create) AS h,
SUM(CASE WHEN s.staff=0 THEN 1 ELSE 0 END) as total,
SUM(CASE WHEN s.staff=1 THEN 1 ELSE 0 END) as total_staff
FROM manifsSubCategories msc
LEFT JOIN products_subCategories psc ON msc.id=psc.manifSubCategory_id
LEFT JOIN sales s ON psc.product_id=s.product_id
LEFT JOIN products p ON psc.product_id=p.id
LEFT JOIN manifsCategories ON manifCategory_id=manifsCategories.id
WHERE manifCategory_id=1 AND s.category_id=1 GROUP BY jour,h ORDER BY p.id DESC

Just omit the other columns :
SELECT p.name,
SUM(CASE WHEN s.staff=0 THEN 1 ELSE 0 END) as total,
SUM(CASE WHEN s.staff=1 THEN 1 ELSE 0 END) as total_staff
FROM manifsSubCategories msc
LEFT JOIN products_subCategories psc ON msc.id=psc.manifSubCategory_id
LEFT JOIN sales s ON psc.product_id=s.product_id
LEFT JOIN products p ON psc.product_id=p.id
LEFT JOIN manifsCategories ON manifCategory_id=manifsCategories.id
WHERE manifCategory_id=1 AND s.category_id=1
GROUP BY p.name
ORDER BY p.id DESC
BTW - Your WHERE clause is wrong. You are filtering by a table for the LEFT JOIN , this will turn your join into an inner join due to NULL comparison. Conditions on the right table of a left join should only be placed inside the ON clause.

Related

how get records of a table as columns of other table for report

I have 4 tables:
users
id
name
1
a
2
b
products
id
name
1
aC
2
bC
bought
id
user_id
product_id
amount
1
1
1
100
2
2
1
200
sold
id
user_id
product_id
amount
1
1
1
100
2
2
2
200
Report
Now I want to have a query to get below report:
user_id
user_name
bought_aC
bought_bC
sold_aC
sold_bC
1
a
100
2
b
100
200
Description
I want to have list of users with bought and sold amount of products.
Each user has one row in report. each product has two column for one for bought , one for sold.
My products are limited, so I want to have different columns for each product(each product has bought and sold column that show amount)
Is this possible?
I would appreciate if any one help me.
You are aggregating along two dimensions, so you need to aggregate before joining:
select u.id, u.name,
b.bought_aC, b.bought_bC,
s.sold_aC, s.sold_bC
from users u left join
(select b.user_id,
sum(case when pb.name = 'aC' then b.amount end) as bought_aC,
sum(case when pb.name = 'bC' then b.amount end) as bought_bC
from bought b join
products pb
on b.product_id = pb.id
group by b.user_id
) b
on u.id = b.user_id left join
(select s.user_id
sum(case when ps.name = 'aC' then s.amount end) as sold_aC,
sum(case when ps.name = 'bC' then s.amount end) as sold_bC
from sold s join
products ps
on s.product_id = ps.id
group by s.user_id
) s
on u.id = s.user_id
order by u.id
I guess something like this should work. Not sure how limited your products are, but you will have to add case when statement for each product.
select u.id, u.name,
case when pb.name = 'aC' then b.amount end as bought_aC,
case when pb.name = 'bC' then b.amount end as bought_bC,
case when ps.name = 'aC' then s.amount end as sold_aC,
case when ps.name = 'bC' then s.amount end as sold_bC
from users u
left join bought b on u.id = b.user_id
join products pb on b.product_id = pb.id
left join sold s on u.id = s.user_id
join products ps on s.product_id = ps.id
order by u.id
As, you want to have an output table having all the required columns.You have to use all these but make sure to use the bought and sold table perfectly as they carry the most data from which you can get user's info as well as product info.
SELECT U.id, U.name,
B.bought_Ac, B.bought_Bc,
S.sold_Ac, S.sold_Bc
from users U LEFT JOIN
bought B where B.user_id IN (SELECT
B.user_id,sum(
CASE when P.name = 'aC' THEN B.amount end) as bought_aC,
sum(
CASE when P.name = 'bC'THEN B.amount end) as bought_bC
from bought B join
products P
on B.product_id = B.id
group by B.user_id
) B
on U.id = B.user_id left join(SELECT
S.user_id,
sum(CASE when PR.name = 'aC' THEN s.amount end) as sold_aC,
sum(case when PR.name = 'bC' THEN s.amount end) as sold_bC
from sold S join
products PR
on S.product_id = PR.id
group by S.user_id) S
on U.id = S.user_id
order by U.id

Difference between dates with one date column in mysql

i have the below query:
SELECT transaction_date,tanks.name,stations.name,products.name,tank_sales
FROM tanks_product_movement LEFT JOIN stations ON station_id=stations.id
LEFT JOIN products ON product_id=products.id LEFT JOIN tanks ON
tank_id=tanks.id WHERE products.name='AGO'
AND stations.name='CG-Station'
i want to know the difference in sales between two days i.e sales difference between 2019-04-01 and 2019-04-02 ?
enter image description here
Assuming these two dates would occur only once in the respective table, you could use pivoting logic here:
SELECT
MAX(CASE WHEN transaction_date = '2019-04-02' THEN tank_sales END) -
MAX(CASE WHEN transaction_date = '2019-04-01' THEN tank_sales END) AS sales_diff
FROM tanks_product_movement tpm
LEFT JOIN stations s
ON tpm.station_id = s.id
LEFT JOIN products p
ON tpm.products_id = p.id
LEFT JOIN tanks t
ON tank_id = t.id
WHERE
p.name = 'AGO' AND
s.name = 'CG-Station';
To find the difference between today and yesterday's sales use:
SELECT
MAX(CASE WHEN transaction_date = CURDATE() THEN tank_sales END) -
MAX(CASE WHEN transaction_date = SUBDATE(CURDATE(), 1) THEN tank_sales END)

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 multiple avg columns using where in condition and join query

how to use where in condition with this query? like, WHERE institutions.instituteId IN ("1","2","4","15").
SELECT (SELECT avg(rating_score.score) FROM reviews INNER JOIN rating_score ON reviews.reviewsId=rating_score.reviewsId WHERE reviews.instituteId='13' AND ratingItemId='1') as 'Acadamics',
(SELECT avg(rating_score.score) FROM reviews INNER JOIN rating_score ON reviews.reviewsId=rating_score.reviewsId WHERE reviews.instituteId='13' AND ratingItemId='2') as 'Area_of_study',
(SELECT avg(rating_score.score) FROM reviews INNER JOIN rating_score ON reviews.reviewsId=rating_score.reviewsId WHERE reviews.instituteId='13' AND ratingItemId='3') as 'Campus_Facilities',
(SELECT avg(rating_score.score) FROM reviews INNER JOIN rating_score ON reviews.reviewsId=rating_score.reviewsId WHERE reviews.instituteId='13' AND ratingItemId='4') as 'Acommadatoin',
(SELECT avg(rating_score.score) FROM reviews INNER JOIN rating_score ON reviews.reviewsId=rating_score.reviewsId WHERE reviews.instituteId='13' AND ratingItemId='5') as 'Sports_Facilities',
institutions.instituteId, institutions.instituteName, institutions.location, countries.countryName, institutions.siteAddress, institutions.overallRatings FROM institutions INNER JOIN countries ON institutions.countryId=countries.countryId
WHERE institutions.instituteId='13'
This is similar to the solutions in need to return two sets of data with two different where clauses but uses AVG instead of SUM. The main difference is that you don't use ELSE 0 in the CASE, because you don't want to average 0 for all the rows with a different raingItemId. AVG() will just ignore those null rows.
SELECT
AVG(CASE WHEN ratingItemId='1' THEN rs.score END) AS Academics,
AVG(CASE WHEN ratingItemId='2' THEN rs.score END) AS Area_of_study,
AVG(CASE WHEN ratingItemId='3' THEN rs.score END) AS Campus_Facilities,
AVG(CASE WHEN ratingItemId='4' THEN rs.score END) AS Acommadatoin,
AVG(CASE WHEN ratingItemId='5' THEN rs.score END) AS Sports_Facilities,
i.instituteName, i.location, c.countryName, i.siteAddress, i.overallRatings
FROM institutions AS i
INNER JOIN countries AS c ON i.countryId=c.countryId
LEFT JOIN reviews AS r ON r.instituteId = i.instituteId
LEFT JOIN rating_score AS rs ON rs.reviewsId = r.reviewsId
WHERE i.instituteId IN (1, 2, 4, 15)
GROUP BY i.instituteId

LEFT JOIN after GROUP BY?

I have a table of "Songs", "Songs_Tags" (relating songs with tags) and "Songs_Votes" (relating songs with boolean like/dislike).
I need to retrieve the songs with a GROUP_CONCAT() of its tags and also the number of likes (true) and dislikes (false).
My query is something like that:
SELECT
s.*,
GROUP_CONCAT(st.id_tag) AS tags_ids,
COUNT(CASE WHEN v.vote=1 THEN 1 ELSE NULL END) as votesUp,
COUNT(CASE WHEN v.vote=0 THEN 1 ELSE NULL END) as votesDown,
FROM Songs s
LEFT JOIN Songs_Tags st ON (s.id = st.id_song)
LEFT JOIN Votes v ON (s.id=v.id_song)
GROUP BY s.id
ORDER BY id DESC
The problem is that when a Song has more than 1 tag, it gets returned more then once, so when I do the COUNT(), it returns more results.
The best solution I could think is if it would be possible to do the last LEFT JOIN after the GROUP BY (so now there would be only one entry for each song). Then I'd need another GROUP BY m.id.
Is there a way to accomplish that? Do I need to use a subquery?
There've been some good answers so far, but I would adopt a slightly different method quite similar to what you described originally
SELECT
songsWithTags.*,
COALESCE(SUM(v.vote),0) AS votesUp,
COALESCE(SUM(1-v.vote),0) AS votesDown
FROM (
SELECT
s.*,
COLLATE(GROUP_CONCAT(st.id_tag),'') AS tags_ids
FROM Songs s
LEFT JOIN Songs_Tags st
ON st.id_song = s.id
GROUP BY s.id
) AS songsWithTags
LEFT JOIN Votes v
ON songsWithTags.id = v.id_song
GROUP BY songsWithTags.id DESC
In this the subquery is responsible for collating songs with tags into a 1 row per song basis. This is then joined onto Votes afterwards. I also opted to simply sum up the v.votes column as you have indicated it is 1 or 0 and therefore a SUM(v.votes) will add up 1+1+1+0+0 = 3 out of 5 are upvotes, while SUM(1-v.vote) will sum 0+0+0+1+1 = 2 out of 5 are downvotes.
If you had an index on votes with the columns (id_song,vote) then that index would be used for this so it wouldn't even hit the table. Likewise if you had an index on Songs_Tags with (id_song,id_tag) then that table wouldn't be hit by the query.
edit added solution using count
SELECT
songsWithTags.*,
COUNT(CASE WHEN v.vote=1 THEN 1 END) as votesUp,
COUNT(CASE WHEN v.vote=0 THEN 1 END) as votesDown
FROM (
SELECT
s.*,
COLLATE(GROUP_CONCAT(st.id_tag),'') AS tags_ids
FROM Songs s
LEFT JOIN Songs_Tags st
ON st.id_song = s.id
GROUP BY s.id
) AS songsWithTags
LEFT JOIN Votes v
ON songsWithTags.id = v.id_song
GROUP BY songsWithTags.id DESC
Try this:
SELECT
s.*,
GROUP_CONCAT(DISTINCT st.id_tag) AS tags_ids,
COUNT(DISTINCT CASE WHEN v.vote=1 THEN id_vote ELSE NULL END) AS votesUp,
COUNT(DISTINCT CASE WHEN v.vote=0 THEN id_vote ELSE NULL END) AS votesDown
FROM Songs s
LEFT JOIN Songs_Tags st ON (s.id = st.id_song)
LEFT JOIN Votes v ON (s.id=v.id_song)
GROUP BY s.id
ORDER BY id DESC
Your code results in a mini-Cartesian product because you are doing two Joins in 1-to-many relationships and the 1 table is on the same side of both joins.
Convert to 2 subqueries with groupings and then Join:
SELECT
s.*,
COALESCE(st.tags_ids, '') AS tags_ids,
COALESCE(v.votesUp, 0) AS votesUp,
COALESCE(v.votesDown, 0) AS votesDown
FROM
Songs AS s
LEFT JOIN
( SELECT
id_song,
GROUP_CONCAT(id_tag) AS tags_ids
FROM Songs_Tags
GROUP BY id_song
) AS st
ON s.id = st.id_song
LEFT JOIN
( SELECT
id_song,
COUNT(CASE WHEN v.vote=1 THEN id_vote END) AS votesUp,
COUNT(CASE WHEN v.vote=0 THEN id_vote END) AS votesDown
FROM Votes
GROUP BY id_song
) AS v
ON s.id = v.id_song
ORDER BY s.id DESC