Using mysql GROUP_CONCAT and WHERE - mysql

I have the following tables in my DB
EVENT
ID, TITLE, ...
VOTES
ID, TYPE, ID_EVENT
COMMENTS
ID, COMMENT, ID_EVENT
DATES
ID, DATE, ID_EVENT
One EVENT has many actions, has many comments and has many dates.
I'm using following query to retrieve info from EVENTS table, and for each event retrieve the number of votes, the number of comments and each one of the dates. For events with one of their date = tomorrow (2015-04-03)
SELECT events.id,
events.title,
GROUP_CONCAT(dates.date) AS dates,
COUNT(distinct votes.id) AS votes,
COUNT(distinct comments.id) AS comments
FROM events
LEFT JOIN dates on dates.post_id = events.id
LEFT JOIN votes on votes.post_id = events.id AND votes.type = 1
LEFT JOIN comments on comments.votes_id = votes.id
WHERE dates.date = CURDATE() + INTERVAL 1 DAY
GROUP BY events.id
Result looks like this
id title dates votes comment
33 Event33 2015-04-03,2015-04-03,2015-04-03 4 0
39 Event39 2015-04-03 9 1
Why the dates column repeats the same date (tomorrow)??? The dates of Event33 should be 2015-04-01, 2015-04-02, 2015-04-03.
What is wrong?

You want the events that have one of their dates tomorrow. Your query does that but it also cuts off all other dates.
You need an extra join or an EXISTS subquery.
You also need a DISTINCT on the GROUP_CONCAT(), the same way you used it at the COUNT() aggregate:
SELECT events.id,
events.title,
GROUP_CONCAT(DISTINCT dates.date) AS dates,
COUNT(DISTINCT votes.id) AS votes,
COUNT(DISTINCT comments.id) AS comments
FROM events
LEFT JOIN dates ON dates.post_id = events.id
LEFT JOIN votes ON votes.post_id = events.id AND votes.type = 1
LEFT JOIN comments ON comments.votes_id = votes.id
WHERE EXISTS
( SELECT 1
FROM dates AS dd
WHERE dd.date = CURDATE() + INTERVAL 1 DAY
AND dd.post_id = events.id
)
GROUP BY events.id ;
Another way would be using inline subqueries. No need for GROUP BY or DISTINCT in this. A minor disadvantage in your case, is that the join to comments is through votes, so one subquery has an extra join:
SELECT e.id,
e.title,
( SELECT GROUP_CONCAT(d.date)
FROM dates AS d
WHERE d.post_id = e.id
) AS dates,
( SELECT COUNT(v.id)
FROM votes AS v
WHERE v.post_id = e.id AND v.type = 1
) AS votes,
( SELECT COUNT(c.id)
FROM comments AS c
JOIN votes AS v ON c.votes_id = v.id
WHERE v.post_id = e.id AND v.type = 1
) AS comments
FROM events AS e
WHERE EXISTS
( SELECT 1
FROM dates AS dd
WHERE dd.date = CURDATE() + INTERVAL 1 DAY
AND dd.post_id = events.id
) ;

Related

MySQL subquery in select

I have the following scenario. i have three tables (users, sales, sales_details) Users to Sales is a 1 to 1 relationship and sales to sales_details is 1 to many.
I am running a query where I get all the sales for each user by joining all 3 tables without any issue.
Query looks something like this
SELECT s.month as month,u.name as name, s.year as year, s.date as date,sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,sum(sd.stock) as stock,s.currency as currency,s.user as user
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
group by s.Id
What I want to do now is add an extra field in my query which will be a subquery.
SELECT SUM(total) AS total_yearly
FROM (
SELECT sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month))))
and User = **ID OF USER** ) as sub
This query on its own gives me the sales for the user for the past 12 months while the original query does it per month. I know that the result will be the same for each user but i need it for other calculations.
My problem is how I will join the 2 queries so that the subquery will read the user id from the original one.
Thanks in advance!
Group the second query by user, and then join it with the original query.
SELECT s.month as month,u.name as name, s.year as year, s.date as date,
sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,
sum(sd.stock) as stock,s.currency as currency,s.user as user,
us.total
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
inner join (
SELECT User, sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month)))))
GROUP BY User) AS us ON s.user = us.user
group by s.Id

Is it necessary to nest a double COUNT here?

So, I have three tables for a service that sells tickets for events:
Event(id)
Ticket(id, venue_id, seller_id)
Venue(id, event_id)
Seller(id)
And I would like to detect, for every event that I have, how many sellers (of type 1) there are that have only only ticket for sale (and for that ticket to be of the corresponding event). Like
Event_id; First_time_sellers
2814;3
3092;24
124;1
...
I have attempted this with two queries, both failing getting wrong results
SELECT event_id,
COUNT(DISTINCT rookie_seller)
FROM
(SELECT event_id,
rookie_seller,
tickets
FROM
(SELECT DISTINCT event.id AS event_id,
seller.id AS rookie_seller,
COUNT(DISTINCT ticket.id) AS tickets
FROM ticket
JOIN venue ON ticket.venue_id = venue.id
JOIN event ON venue.event_id = event.id
JOIN seller ON ticket.seller_id = seller.id
WHERE seller.type = 1
AND ticket.isforsale = 1
GROUP BY rookie_seller) a
WHERE tickets = 1) b
GROUP BY event_id
and
SELECT event.id,
a.seller_id,
a.tickets
FROM ticket
JOIN (
SELECT seller.id AS seller_id,
COUNT(DISTINCT ticket.id) AS tickets
FROM ticket
JOIN seller ON ticket.seller_id = seller.id
WHERE seller.type = 1
AND ticket.isforsale = 1
GROUP BY seller_id) a ON ticket.seller_id = a.seller_id
JOIN venue ON ticket.venue_id = venue.id
JOIN event ON venue.event_id = event.id
WHERE a.tickets = 1
I am really not sure what the problem is. the joins should be fine, and the conditions on the count as well, right?
Thanks for any piece of advice :)
You do need a subquery for this. But you can do it more simply:
SELECT event_id, COUNT(*)
FROM (SELECT s.id, v.event_id, COUNT(*) as numTickets
FROM ticket t JOIN
seller s
ON t.seller_id = s.id JOIN
venue v
ON t.venue_id = v.id
WHERE s.type = 1 AND t.isforsale = 1
GROUP BY s.id
HAVING COUNT(*) = 1
) s
GROUP BY event_id;

Merge 2 SQL Queries/Tables

I spent so much time googling today but i don't even know which keywords to use. So …
The project is an evaluation of a betting game (Football). I have 2 SQL Queries:
SELECT players.username, players.userid, matchdays.userid, matchdays.points, SUM(points) AS gesamt
FROM players INNER JOIN matchdays ON players.userid = matchdays.userid AND matchdays.season_id=5
GROUP BY players.username
ORDER BY gesamt DESC
And my second query:
SELECT max(matchday) as lastmd, points, players.username from players INNER JOIN matchdays ON players.userid = matchdays.userid WHERE matchdays.season_id=5 AND matchday=
(select max(matchday) from matchdays)group by players.username ORDER BY points DESC
The first one adds up the points of every matchday and shows the sum.
The second shows the points of the last gameday.
My Goal is to merge those 2 queries/tables so that the output is a table like
Rank | Username | Points last gameday | Overall points |
I don't even know where to start or what to look for. Any help would be appreciated ;)
use both query with join....use inner join if each userid have value in 2nd query also.also add userid in 2nd query also for join
SET #rank = 0;
SELECT #rank := rank + 1,
t1.username,
t2.points,
t1.gesamt
FROM (
SELECT players.username, players.userid puserid, matchdays.userid muserid, matchdays.points, SUM(points) AS gesamt
FROM players INNER JOIN matchdays ON players.userid = matchdays.userid AND matchdays.season_id=5
GROUP BY players.username
)t1
INNER JOIN
(
SELECT players.userid, max(matchday) as lastmd, points, players.username
from players INNER JOIN matchdays ON players.userid = matchdays.userid
WHERE matchdays.season_id=5 AND matchday=
(select max(matchday) from matchdays)group by players.username
)t2
ON t1.puserid = t2.userid
ORDER BY t1.gesamt
You can use conditional aggregation, i.e. sum the points only when the day is the last day:
SELECT
p.username,
SUM(case when m.matchday = (select max(matchday) from matchdays) then m.points end)
AS last_day_points,
SUM(m.points) AS total_points
FROM players p
INNER JOIN matchdays m ON p.userid = m.userid AND m.season_id = 5
GROUP BY p.userid
ORDER BY total_points DESC;
Or with a join instead of a non-correlated subquery (MySQL should come to the same execution plan):
SELECT
p.username,
SUM(case when m.matchday = last_day.matchday then m.points end) AS last_day_points,
SUM(m.points) AS total_points
FROM players p
INNER JOIN matchdays m ON p.userid = m.userid AND m.season_id = 5
CROSS JOIN
(
select max(matchday) as matchday
from matchdays
) last_day
GROUP BY p.userid
ORDER BY total_points DESC;

Mysql : get count and percent for all entries of a table with given conditions

Let's say we have the following model :
A table of exercises (id, name)
A table of users (id, name, email)
A tables of exams (id, user_id, day)
A join table between exams and exercises (exam_id, exercise_id)
Each user can make one exam each day.
Each exam is made of many exercises (has and belongs to many).
For each user (when logged), I would like to display a table displaying all the exercises and for each the count and percent of exams the user made during a given period of time (using the day attribute of the exams).
At this time I can display all the exercises the user made and their percent during a given period with the following query.
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
COUNT(*) AS nb_exams,
(COUNT(*) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
GROUP BY exercise_id,exam_id
) AS o
GROUP BY exercise_id;
But I also want to display the exercises he did not make yet with a value of 0.
Is it possible to do this with one query of mysql ?
EDIT-
Here is the sqlfiddle for the current query. You will notice that only the 3 exercises which are used in exams are returned. I would like to list all the exercises with the count and percent (even the fourth with 0)
Firstly, you should remove the where condition to the join clause in the inner query to see a 0 result when an exercise wasn't performed. Getting a user_id corresponding to the 0 result doesn't make sense. This is the closest solution i can think of.
Fiddle
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
sum(case when exam_id is null then 0 else 1 end) AS nb_exams,
(sum(case when exam_id is null then 0 else 1 end) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
where exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id ,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
and exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
LEFT JOIN users ON exams.user_id = users.id
) AS o
GROUP BY exercise_id,exercise_name

MySQL SELECT subquery

I have a calendar and user_result table and I need to join these two queries.
calendar query
SELECT `week`, `date`, `time`, COUNT(*) as count
FROM `calendar`
WHERE `week` = 1
GROUP BY `date`
ORDER BY `date` DESC
and the result is
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12"}
user_result query
SELECT `date`, SUM(`point`) as score
FROM `user_result`
WHERE `user_id` = 1
AND `date` = '2014-08-20'
and the result is just score 3
My goal is to always show calendar even if the user isn't present in the user_result table, but if he is, SUM his points for that day where calendar.date = user_result.date. Result should be:
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4","score":"3"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12","score":"0"}
I have tried this query below, but the result is just one row and unexpected count
SELECT c.`week`, c.`date`, c.`time`, COUNT(*) as count, SUM(p.`point`) as score
FROM `calendar` c
INNER JOIN `user_result` p ON c.`date` = p.`date`
WHERE c.`week` = 1
AND p.`user_id` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
{"week":"1","date":"2014-08-20","time":"17:30:00","count":"4","score":"9"}
SQL Fiddle
ow sorry, i was edited, and i was try at your sqlfiddle, if you want to show all date from calendar you can use LEFT JOIN, but if you want to show just the same date between calendar and result you can use INNER JOIN, note: in this case INNER JOIN just show 1 result, and LEFT JOIN show 2 results
SELECT c.`week`, p.user_id, c.`date`, c.`time`, COUNT(*) as count, p.score
FROM `calendar` c
LEFT JOIN
(
SELECT `date`, SUM(`point`) score, user_id
FROM `result`
group by `date`
) p ON c.`date` = p.`date`
WHERE c.`week` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
I put a pre-aggreate query / group by date as a select for the one person you were interested in... then did a left-join to it. Also, your column names of week, date and time (IMO) are poor choice column names as they can appear to be too close to reserved keywords in MySQL. They are not, but could be confusing..
SELECT
c.week,
c.date,
c.time,
coalesce( OnePerson.PointEntries, 0 ) as count,
coalesce( OnePerson.totPoints, 0 ) as score
FROM
calendar c
LEFT JOIN ( select
r.week,
r.date,
COUNT(*) as PointEntries,
SUM( r.point ) as totPoints
from
result r
where
r.week = 1
AND r.user_id = 1
group by
r.week,
r.date ) OnePerson
ON c.week = OnePerson.week
AND c.date = OnePerson.date
WHERE
c.week = 1
GROUP BY
c.date
ORDER BY
c.date DESC
Posted code to SQLFiddle