Mysql count of a subquery column with having - mysql

I have a table called giveaways and each giveaway can have contestants. I am trying to get the number of giveaways without winners. The contestants table has a field called winner that is 1 or 0.
My data looks like:
Giveaway Table
| id | name |
|----|------------|
| 1 | Giveaway 1 |
| 2 | Giveaway 2 |
Contestant Table
| id | giveaway_id|winner|
|----|------------|------|
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 3 | 2 | 0 |
This is my query :
SELECT (SELECT COUNT(id) FROM contestants c
WHERE c.giveaway_id = g.id AND winner = 1) as winners
FROM giveaways g
having winners = 0
Right now this will return multiple rows, I want to get the count of rows. I tried wrapping a count() around the winners subquery but that did not work.
In the example above this would be returned:
Results
| winners |
|---------|
| 0 |
| 0 |
I want just the count of rows which would be 2.
What's a better approach? Thx

I am trying to get the number of giveaways without winners.
Use NOT EXISTS with a correlated subquery :
SELECT
COUNT(*)
FROM
giveaways g
WHERE NOT EXISTS (
SELECT 1 FROM contestants WHERE giveaway_id = g.id AND winner = 1
)
The subquery ensures that there is no winning contestant for each giveaway to count.

Related

How can I not take in consideration values join without record on the db

I'm in front of a "minor" problem taht looks easy but I didn't suceed to resolve it.
I have three tables in my Database :
Table gp
____________
id | name |
____________
1 | Le Mans|
2 | Toulon |
3 | Rennes |
Table player
____________
id | name |
____________
1 | Thibaut|
2 | Fred |
3 | Samir |
Table Records
_____________________________
id | gp_id | player_id | time
_____________________________
1 | 1 | 1 | 17860
2 | 2 | 1 | 11311
3 | 3 | 1 | 33133
4 | 3 | 2 | 11113
5 | 2 | 2 | 44444
6 | 1 | 2 | 13131
7 | 1 | 3 | 11111
8 | 3 | 3 | 21112
I want to get a sum of time for players that have a record on every gp ( so in my case, just players Thibaut and Fred have a record on the 3 gp ( Samir has just a record on two gp ) ).
I have no idea how I can get that, of course this SQL query is retrieving a sum but from this query I want to escape the guys that don't have a record on every GPs, but I'm blocked at that point ...
SELECT p.name, sum(time)
from records r
join gp g on r.gp_id = g.id
join player p on r.player_id = p.id
group by r.player_id
Thanks in advance guys !
You could use having count to exclude the records that don't have a record on every GPs.
Try:
select p.name,
sum(`time`) as tot_sum
from records r
inner join player p on r.player_id=p.id
inner join gp g on g.id=r.gp_id
group by p.name
having count(distinct gp_id) = (select count(distinct id) from gp)
https://dbfiddle.uk/t8QwSFDY
having count(distinct gp_id) = (select count(distinct id) from gp) will match only the records in the record table that have a record on every gp.

Return 0 if the condition doesn't match

I have a soccer game where people can predict matches. They can give a match a 1 if they think the home team wins, a 2 if the away club wins and a 3 for a draw. If they predict a match correctly, they get a point. The tables in my database look like this:
Table matches
| id | home | away | result | round_id
| --- | ---------| -------- -| -------|----------|
| 1 | id club 1| id club 2 | 1 | id round 1
| 2 | id club 5| id club 4 | 3 | id round 1
| 3 | id club 8| id club 5 | 1 | id round 2
Table predictions
| prediction | user_id | match_id |
| -------- | -------------- | -------- |
| 1 | id user 1 | id match 1
| 3 | id user 1 | id match 2
| 2 | id user 1 | id match 3
Initially I wanted to do the calculation of the score in PHP, but I think this should also be possible via MySQL only. So I tried something and came up with the following query:
SELECT Count(*) AS points,
username,
round_id
FROM predictions
LEFT JOIN matches
ON predictions.match_id = matches.id
INNER JOIN users
ON predictions.user_id = users.id
WHERE predictions.prediction = matches.result
GROUP BY username,
round_id
ORDER BY points DESC,
username ASC
The query calculates the score per round per user correctly, the only problem is that if a participant has nothing right in a game round, it will not appear in the list at all. Does anyone have any idea what to do to also get a participant in the list if they have 0 points? Translated to the tables mentioned above, round 2 will not return the query because the only match in it was mispredicted. I do want this, however, and so that round 2 is returned with a score of 0.
Result I want:
| points | username | round_id|
| -------- | -------------- | --------|
| 2 | John | 1
| 0 | John | 2
Result I have now:
| points | username | round_id|
| -------- | -------------- | --------|
| 2 | John | 1
The WHERE clause:
WHERE predictions.prediction = matches.result
filters out any wrong predictions, but even if you remove it, the aggregate function COUNT(*) would count the wrong predictions also.
Join and group like this:
SELECT SUM(p.prediction = m.result) AS points,
u.username,
m.round_id
FROM users u
INNER JOIN predictions p ON p.user_id = u.id
INNER JOIN matches m ON m.id = p.match_id
GROUP BY u.id, u.username, m.round_id
ORDER BY points DESC, u.username ASC;
The aggregate function SUM() will sum the boolean expressions prediction = result which are evaluated as 1 for true and 0 for false.
See the demo.

MySQL - Count number of rows in each group

I have a SQL table user_game which contains the games that a user owns:
| id | user_id | game_id |
|----|---------|---------|
| 83 | 1 | 1 |
| 84 | 1 | 2 |
| 85 | 1 | 3 |
| 86 | 2 | 2 |
| 87 | 2 | 3 |
| 88 | 2 | 4 |
| 89 | 3 | 2 |
I am trying to count the number of users which have 1 game, 2 games, 3 games.. etc.
User 1 has 3 games, User 2 has 3 games, and User 3 has 1 game. Therefore these are the results I want to achieve:
| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1 | 1 |
| 2 | 0 |
| 3 | 2 |
COUNT(no_of_games) is the number of users that have that number of games.
I can individually get the number of users for each no_of_games with this query:
-- Select no. of users with 1 game
SELECT no_of_games, COUNT(no_of_games)
FROM
(
-- Select no. of games each user has
SELECT user_id, COUNT(1) as no_of_games
FROM user_game
GROUP BY user_id
) as A
WHERE no_of_games = 1;
which gives the results:
| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1 | 1 |
However I have to change the no_of_games = 1 to 2, 3, 4... manually and UNION them with this solution and I can't do it for ~60 cases.
Is there a simpler way to achieve this?
Your problem is a bit tricky, because groups of games which do not appear in your data with a certain frequency (e.g. 2) will not appear in the result set just using your original table. In the query below, I use a second table called nums which simply contains the sequence 1 through 10 representing counts of number of games. By using a LEFT JOIN we can retain each game count in the final result set.
SELECT t1.no_of_games,
COALESCE(t2.no_of_games_count, 0) AS no_of_games_count
FROM nums t1
LEFT JOIN
(
SELECT t.no_of_games, COUNT(*) AS no_of_games_count
FROM
(
SELECT COUNT(*) AS no_of_games
FROM user_game
GROUP BY user_id
) t
GROUP BY t.no_of_games
) t2
ON t1.no_of_games = t2.no_of_games
ORDER BY t1.no_of_games
And here is the definition I used for nums:
CREATE TABLE nums (`no_of_games` int);
INSERT INTO nums (`no_of_games`)
VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
Demo here:
SQLFiddle
You can find count of games for each user and then find count of users for each count of games.
select cnt no_of_games, count(*) cnt_no_of_games
from(
select user_id, count(*) cnt
from your_table
group by user_id
) t group by cnt;

Querying MySQL tables for a item a user hasnt 'scored' yet

Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1

Sql SUM over products of grouped elements

I have the following data structure:
Table 1(groups):
ID | Group
=============
1 | Sample
2 | Data
Table 2(items):
ID | GroupID | Cost | Amount
==============================
1 | 1 | 1 | 12
2 | 1 | 7 | 15
3 | 1 | 3 | 8
4 | 2 | 2 | 12
And would like the following (query) results:
groups.ID | groups.Name | total
1 | Sample | 141
2 | Data | 24
total is the sum over the products of cost and amount of all items in the group i.e. for group 1: 1*12+7*15+3*8=141
Im guessing I have to something with
SELECT g.ID, g.Group, SUM(Products)
FROM groups AS g, items AS i
WHERE g.ID=i.GroupID
GROUP BY i.GroupID
But don't know what exactly.
Doing iit in clientsoftware with loops is no problem, but I am curious (and certain) that this can be done in (my)Sql
SELECT g.ID as ID, g.Group as Name, SUM(i.Cost * i.Amount) as total
FROM groups g
INNER JOIN items i ON i.GroupID = g.ID
GROUP BY g.Group, g.ID
Having a field named "Group" is quite a bad idea in SQL (reserved keyword)