I am new here so if its not fixed topic please fix me.
I have this table:
id | uid | month | year
-----------------------
1 | 5 | 12 | 2018
2 | 5 | 12 | 2018
3 | 9 | 12 | 2018
4 | 3 | 01 | 2019
I want to count how much times for each uid the month and year values equal to - month=12 AND year=2018
for example I want to get response like that:
uid=5 - count = 2
uid=9 - count = 1
How its possible?
With group by uid:
select uid, count(*) as counter
from table
where month = 12 and year = 2018
group by uid
You can use conditional aggregation:
select uid,
sum(case when year = 2018 and month = 12 then 1 else 0 end) as cnt
from t
group by uid;
This will include uids with a count of 0. If you just want uids where the count is greater than 0, use a where clause:
select uid, count(*)
from t
where year = 2018 and month = 12
group by uid;
Select uid, count(*) from table where year=2018 and month=12 group by uid
I've the following tables (example):
users:
id | user | photo | joined | country
1 | Igor | abc.jpg | 2015 | Brazil
2 | John | cga.png | 2014 | USA
3 | Lucas| hes.jpg | 2016 | Japan
posts (see that there are two lines with author = Igor and ft = 2 and one line with author = Igor and ft = 3 and Igor have three posts):
id | author | content | date | ft (2 = photos and 3 = videos)
1 | Igor | hi | 2016 | 2
2 | Igor | hello | 2016 | 3
3 | John | hehehe | 2016 | 2
4 | Igor | huhuhuh | 2016 | 2
5 | Lucas | lol | 2016 | 3
friendship (when status = 2 means that they are friends):
id | friend1 | friend2 | status
1 | Igor | Lucas | 2
2 | Lucas | John | 2
3 | John | Igor | 2
And I want to do a COUNT of posts with ft = 2 and a COUNT of friends (status = 2) according to the currently logged user (Igor, in this case).
So, I do (assuming that the current user logged in is Igor):
SELECT photo, joined, country, sum(CASE WHEN ft = 2 THEN 1 ELSE 0 END) AS numPhotos, sum(CASE WHEN ft = 3 THEN 1 ELSE 0 END) AS numVideos
FROM users
LEFT JOIN posts
ON users.user = posts.author
WHERE users.user = 'Igor'
GROUP BY users.user
LIMIT 1
And when I check on a foreach, the data is correct:
numPhotos = 2 and numVideos = 1.
But, I want to select too the number of friends, so, I do:
SELECT photo, joined, country, sum(CASE WHEN ft = 2 THEN 1 ELSE 0 END) AS numPhotos, sum(CASE WHEN ft = 3 THEN 1 ELSE 0 END) AS numVideos, count(friendship.status) AS numFriends
FROM users
LEFT JOIN posts
ON users.user = posts.author
LEFT JOIN friendship
ON (users.user = friend1 OR users.user = friend2) AND friendship.status = 2
WHERE users.user = 'Igor'
GROUP BY users.user
LIMIT 1
But, the output is:
numPhotos = 4, numVideos = 2 and numFriends = 6.
In other words, he is duplicating all results but in numFriends he's taking the total of posts of Igor (3) and duplicating the value too. And if I change count(friendship.status) to sum(friendship.status) the output is:
numPhotos = 4, numVideos = 2 and numFriends = 18 (triples the numFriends).
I tried too with count(distinct friendship.status) and the result is:
numPhotos = 4, numVideos = 2 and numFriends = 1 (duplicates the values again as well as return the wrong value 1 for numFriends that should be 2 knowing he has two friends).
So, how I can do this? (I'm using MySQL)
EDIT:
I changed the count(distinct friendship.status) to count(distinct friendship.id) and it worked to select the number of friends. But the rest of values (numPhotos and numVideos) continue duplicated.
I discovered that the problem is in ON (users.user = friend1 OR users.user = friend2), because if I leave only ON (users.user = friend1) or ON (users.user = friend2) the output isn't duplicated. I tried too with ON 'Igor' IN (friend1, friend2) but the result is the same (numPhotosandnumVideos` continue duplicated).
I think the left join may be joining on a one-to-many relationship, which is causing inflated counts.
Since you are only retrieving the counts for 1 user, I suggest using a subquery to retrieve the friendship counts (for retrieving the counts for multiple users, a derived table may be faster than a subquery):
SELECT
sum(ft = 2) AS numPhotos,
sum(ft = 3) AS numVideos,
(select count(*) from friendships f
where (friend1 = users.user
or friend2 = users.user)
and status = 2) as friendship_count
FROM users
LEFT JOIN posts
ON users.user = posts.author
WHERE users.user = 'Igor'
Note that I removed the group by because users.user is already in the where clause, which means there is only 1 group.
Instead of count(distinct friendship.status), try using count(distinct friendship.id). That should give you the number of unique friends. Counting distinct statuses doesn't work because all the statuses will be 2 by definition, so there is only one distinct value.
I have two tables with given structure :
Table 1:
ST_Id ST_Name
1 xx
2 yy
Table 2 :
AT_Id AT_Amt ST_ID Date
1 500 1 2015-11-17
2 1000 1 2015-11-15
3 300 1 2015-12-1
4 200 2 2015-11-2
I want to get results from mysql order by month. In my php page, I have a table structure for displaying data like shown below
State December November September
xx 300 1500 0
yy 0 200 0
Along with these months, I have a sorting link for sorting the amounts based on month. So when I click the sorting image corresponding to the month November, it should list the table data according to the order of amount in that column. If I clicked for ascending sorting it should list like,
State December November September
yy 0 200 0
xx 300 1500 0
I tried with following query :
SELECT ST.ST_Name,SUM(AT.AT_Amt)
FROM `Table2` AS AT
LEFT JOIN Table1 AS ST ON AT.ST_Id = ST.ST_Id
WHERE AT.Date BETWEEN '2015-04-01' AND '2015-12-31' GROUP BY MONTH( AT.Date) ORDER BY IF(MONTH(AT.Date) = 11 , SUM(AT.AT_Amt) , MONTH( AT.Date)) ASC
This query is returning data like shown below.
ST_Name SUM(AT.AT_Amt)
xx 300
xx 1700
But the expected result is :
ST_Name SUM(AT.AT_Amt)
yy 200
xx 1500
Can anyone please help me fix this? Thanks in advance.
You can do something like this:
SELECT
st_name,
SUM(CASE WHEN MONTH(dt) = 12 THEN at_amt ELSE 0 END) AS December,
SUM(CASE WHEN MONTH(dt) = 11 THEN at_amt ELSE 0 END) AS November,
SUM(CASE WHEN MONTH(dt) = 10 THEN at_amt ELSE 0 END) AS September
FROM table2
INNER JOIN table1 ON table1.st_id = table2.st_id
GROUP BY st_name;
Once you get data into PHP, you could just use JavaScript to do client-side sorting using TableSorter or similar library. That way sorting doesn't incur the expense of re-querying the database.
Result of your data will be:
+---------+----------+----------+-----------+
| st_name | December | November | September |
+---------+----------+----------+-----------+
| xx | 300 | 1500 | 0 |
| yy | 0 | 200 | 0 |
+---------+----------+----------+-----------+
Click the link below for a running demo:
SQLFiddle
EDIT:
Based on your comment, is this something you need?
SELECT
ST.ST_Name,
SUM(AT.AT_Amt) as Amount
FROM `Table2` AS AT
LEFT JOIN Table1 AS ST ON AT.ST_Id = ST.ST_Id
WHERE AT.dt BETWEEN '2015-04-01' AND '2015-12-31'
AND MONTH(AT.dt) = 11
GROUP BY ST.ST_Name
ORDER BY Amount
Example: http://sqlfiddle.com/#!9/51c7c/7 for demo
Result:
| st_name | Amount |
|---------|--------|
| yy | 200 |
| xx | 1500 |
Good day,
I am currently making a sales tracking report where the user pick a date range then the table should display the result.
for now I have this query that counts how many items are sold
select x_transaction_details.xitem,
SUM(x_transaction_details.qty) as totalNumberSold,
count(x_transaction_details.xitem) as occurence,
x_transaction_details.cost,
i_inventory.xitem,
x_transaction_details.date_at as transDate
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurence DESC
this query displays
|itemName| totalNumberSold | occurence | date
|item 1 | 23 pcs | 2 |
|item 2 | 18 pcs | 6 |
|item 3 | 203 pcs | 18 |
etc..
Now I want to know the breakdown of sales per day so I tried
select x_transaction_details.xitem,
SUM(x_transaction_details.qty) as sold,
count(x_transaction_details.xitem) as occurence,
x_transaction_details.cost,
i_inventory.xitem,
x_transaction_details.date_at as transDate
SUM(CASE WHEN date_at = DAYOFWEEK(1) THEN
count(x_transaction_details.xitem) END) as Sunday
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurence DESC
But its generating an error instead. I want to create a more detailed table
|itemName|Mon|Tue|Wed|Thur|Fri|Sat|Sun| totalNumberSold | occurence | date
|item 1 | 10| 0| 0 | 13 | 0 |0 |0 | 23 pcs | 2 |
|item 2 | 1 | 3| 12| 0 | 16|0 |0 | 32 pcs | 6 |
|item 3 | 0 | 6| 1 | 13 | 8 |7 |1 | 36 pcs | 12 |
etc..
Thanks for the tips, I can make this table using php (hard-way) too but I guess its doable using sql query as well. Have a good day ahead.
You are missing a comma and nesting aggregation functions. I think you want:
select i.xid, SUM(td.qty) as sold, count(td.xitem) as occurrence,
avg(td.cost) as avg_cost, i.xitem,
SUM(case when DAYOFWEEK(td.date_at) = 1 then td.qty else 0 end) as Sunday
from i_inventory i join
x_transaction_details td
on td.xitem = i.xid
where td.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12'
GROUP BY i.xid
ORDER BY occurrence DESC;
Notes:
"occurrence" has two r's.
Table aliases make the query easier to write and to read.
You can't nest aggregation functions.
You shouldn't include date_at in the select list because the value is not unique for each row.
The left join is either backwards (inventory should go first) or should be an inner join. In a properly formed database, you should not have items in the transaction table that are not in the inventory table (I don't think).
The actual code (might be helpful for anyone)
select x_transaction_details.xitem, count(x_transaction_details.xitem) as occurrence, i_inventory.xitem,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 1 then x_transaction_details.qty else 0 end) as Sun,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 2 then x_transaction_details.qty else 0 end) as Mom,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 3 then x_transaction_details.qty else 0 end) as tue,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 4 then x_transaction_details.qty else 0 end) as wed,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 5 then x_transaction_details.qty else 0 end) as thur,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 6 then x_transaction_details.qty else 0 end) as fri,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 7 then x_transaction_details.qty else 0 end) as sat,
SUM(x_transaction_details.qty) as totalNumberSold
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurrence DESC
I did not used table aliases though :)
My site allows users to guess the result of a sports match. At the end of the match the guesses should be compared to the actual result. The winner(s) are the members with the closest correct guess
Im looking for a way to return all members who guessed the correct result and score difference IF NO (zero) member guessed correctly return members who guessed closest to the correct result
See MYSQL FIDLE EXAMPLE
I modified the script to change fixed values taking variables as you can see below
if(isset($_POST['resultBtn'])){
foreach($_POST['winner'] as $id =>$winner){
$winScore = $_POST['score'][$id];
:
:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)";
My problem is if I run this script on the following table:
NOTHING gets displayed even if I put result exactly correct:
My variable values are correct in the sql statment so that is not the problem
Any help will be welcomed...
IMPORTANT THE USER WHO SELECTED CLOSEST CORRECT RESULTS, FOR ALL GAME, DURING THE ROUND IS THE WINNER
example: if user A won 4 of the picks and user B won 5 of the picks then user B is the winner of the round
Why don't you want just
SELECT p.*, abs(p.score-'$winScore') as diff
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND p.pick='$winner'
ORDER BY diff ASC
LIMIT 1
This will return the closest member for the event. Remove the LIMIT if you need a few of them.
Also, never put your parameters directly into the SQL query, even trusted ones (not your case) and even if you're sure they will always be integer or non-string type. Use prepared statements.
In this answer I call a "Best" pick any pick that has chosen the correct winner for a particular match, and has the closest score to the actual match score.
These scripts also respect the different "rounds" in the competition, since that is an important complication.
This answer comes in two parts: first a query that is similar to the one in the question that returns all the "Best" picks for a particular match. To make it easier to run in SQL Fiddle, I have used MySQL variables instead of PHP variables.
Schema with test data:
create table Multiple_Picks (
pick_id int,
member_nr int,
event_id int,
pick varchar(100),
score int
);
insert into Multiple_Picks
values
(11,100,1,'Crusaders',15),
(12,100,2,'Waratahs',10),
(13,100,3,'Chiefs',4),
(21,200,1,'Crusaders',15),
(22,200,2,'Waratahs',10),
(23,200,3,'Lions',4),
(31,300,1,'Crusaders',15),
(32,300,2,'Waratahs',12),
(33,300,3,'Lions',6),
(41,100,4,'Crusaders',20),
(42,100,5,'Waratahs',20),
(43,100,6,'Lions',20)
;
Queries to show all picks and then best picks for a particular match:
set #matchId = 2;
set #winner = 'Waratahs';
set #winScore = 8;
-- Show all picks for a particular match
select * from Multiple_Picks
where event_id = #matchId;
-- Show best picks for a particular match
select p.*
from Multiple_Picks p
where p.event_id = #matchId
and p.pick = #winner
and abs(p.score - #winScore) =
(select min(abs(other.score - #winScore))
from Multiple_Picks other
where other.event_id = #matchId
and other.pick = #winner
)
;
SQL Fiddle to show picks for particular match
-- Show all picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
| 32 | 300 | 2 | Waratahs | 12 |
+---------+-----------+----------+----------+-------+
-- Show best picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
+---------+-----------+----------+----------+-------+
Now we need to work towards finding the winner of each round of the competition.
First we have extra test data that contains the actual scores for Matches in rounds 1 and 2.
create table Matches (
event_id int,
winner varchar(100),
score int,
round int
);
insert into Matches
values
(1,'Crusaders',10,1),
(2,'Waratahs',11,1),
(3,'Lions',4,1),
(4,'Crusaders',20,2),
(5,'Waratahs',20,2),
(6,'Chiefs',20,2)
;
Now select the best picks for all Matches. The subselect (aliased as m) calculates best_diff for each match as the minimum difference between the actual score and every guessed score. This subselect is then joined to every pick so that only "Best" picks are returned.
-- Show all best picks for all Matches
select p.*, m.round
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
order by m.round, p.event_id
;
It is then easy to get a count of Best picks for each player for each round by just grouping the previous query by member_nr and round:
-- Show a count of best picks for each player for each round
select p.member_nr, m.round, count(*) as best_count
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
group by p.member_nr, m.round
order by m.round, count(*) desc
;
SQL Fiddle for all best picks and counts for all matches
-- Show all best picks for all Matches
+---------+-----------+----------+-----------+-------+-------+
| pick_id | member_nr | event_id | pick | score | round |
+---------+-----------+----------+-----------+-------+-------+
| 31 | 300 | 1 | Crusaders | 15 | 1 |
| 21 | 200 | 1 | Crusaders | 15 | 1 |
| 11 | 100 | 1 | Crusaders | 15 | 1 |
| 12 | 100 | 2 | Waratahs | 10 | 1 |
| 32 | 300 | 2 | Waratahs | 12 | 1 |
| 22 | 200 | 2 | Waratahs | 10 | 1 |
| 23 | 200 | 3 | Lions | 4 | 1 |
| 41 | 100 | 4 | Crusaders | 20 | 2 |
| 42 | 100 | 5 | Waratahs | 20 | 2 |
+---------+-----------+----------+-----------+-------+-------+
-- Show a count of best picks for each player for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 300 | 1 | 2 |
| 100 | 1 | 2 |
| 100 | 2 | 2 |
+-----------+-------+------------+
The final stage is to select only those players for each round who have the highest number of Best picks. I tried modifying the above queries, but the nesting becomes two confusing, so my solution was to create a few logical views so that the final query can be more easily understood. The views basically encapsulate the logic of the queries I have explained above:
create view MatchesWithBestDiff as
select m.event_id, m.winner, m.score, m.round,
min(abs(m.score-p.score)) as best_diff
from Matches m
join Multiple_Picks p
on p.event_id = m.event_id and p.pick = m.winner
group by m.event_id, m.winner, m.score, m.round
;
create view BestPicks as
select p.*, m.round
from Multiple_Picks p
join MatchesWithBestDiff m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
;
create view BestPickCount as
select member_nr, round, count(*) as best_count
from BestPicks
group by member_nr, round
;
So that the query that shows the winners of each round is simply:
-- Show the players with the highest number of Best Picks for each round
select *
from BestPickCount p
where best_count =
(
select max(other.best_count)
from BestPickCount other
where other.round = p.round
)
order by round
;
SQL Fiddle for players with most Best picks for each round
-- Show the players with the highest number of Best Picks for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 100 | 2 | 2 |
+-----------+-------+------------+
This whole investigation has reminded me how tricky it can be to get SQL to do much manipulation where records need to be selected depending on maximums and sums. Some of these types of queries can be much easier with window functions (the OVER and PARTITION BY clauses), but they are not available in MySQL.
While designing the above queries, I found a few interesting restrictions:
MySQL does not allow joins to subqueries in views definitions.
ANSI SQL does not allow an aggregate in a subquery to reference both a column from the inner query and a column from the outer query. MySQL seems to sometimes allow this, but I couldn't find clear guidance as to when it is allowed, so I chose to code the above queries to avoid this "feature".
scenario 1: NO USERS SELECTED THE CORRECT TEAM
I believe that result in this situation should be empty result because everyone has made a mistake.
SCORE RETURN MEMBERS WHO SELECTED THE CLOSEST TO CORRECT SCORE AND
RESULT
It seems to be already working in your code example except one mistake in select.
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
Instead of constant 1 (one) it should be variable '$winScore'
and to control the number of users you get, you may limit your results so you will get something like this:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id limit '$numberOfMembers'";
SCENARIO 2: SCENARIO 2: MULTIPLE USERS SELECTED CORRECT TEAM BUT
SCORES ARE DIFFERENT RETURN USER(S) WHO GUESSED CLOSEST TO CORRECT
SCORE
Same as in the previous question.
SCENARIO 3: MULTIPLE USERS SELECTED CORRECT TEAM AND SCORE RETURN ALL
USERS WHO SELECTED CORRECT TEAM AND SCORE
You can achieve this using same query just replace the LIMIT with 'rank' function, and also if you will get several closest scores, but you have to limit their number according to their voting order by id, for this purpose I suggest sorting.
So final query will be:
$sql="select * from (SELECT p.*,
abs(p.score-'$winScore') scr_diff,
#rownum := #rownum + 1 rank
FROM Multiple_Picks p,
(SELECT #rownum := 0) rank_gen
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id
) sq
where sq.scr_diff = 0
or sq.rank < '$numberOfMembers'";
Fiddle.
Best guesser for one match
First find the member(s) who picked the winner and had the closest score guess:
SELECT p.*
FROM
( SELECT MIN(ABS(score-'$winScore')) AS closest
FROM Multiple_Picks
WHERE event_id = '$matchId'
AND pick='$winner'
) AS c
JOIN Multiple_Picks p
WHERE p.event_id = '$matchId'
AND p.pick = '$winner'
AND ABS(score-'$winScore') = c.closest
If that return no results, then what should happen? (It would be because no one picked the winner for a particular event.)
But, I think your question is much more complex. However, the above gives a mapping from (event_id, pick) -> list-of-members who "won". Starting over...
Missing info
There is a mystery -- Where do the event results come from? I will assume this table is already populated:
CREATE TABLE Win (
event_id ..., -- which game
winnner ..., -- who won
score ... -- by what score
)
Best guesser overall
So, create a table of BestGuessers(event_id, member). The details of "all game" and "round" are a bit vague. So I will carry this at least one step further.
CREATE TEMPORARY TABLE BestGuessers(
event_id ...,
member_nr ... -- who guessed the best for that event
)
SELECT p.event_id, p.member_nr
FROM
( SELECT w.event_id, w.winner, MIN(ABS(mp.score-w.score)) AS closest
FROM Multiple_Picks AS mp
JOIN Win AS w ON mp.event_id = w.event_id
AND mp.pick = w.winner
GROUP BY w.event_id, w.winner
) AS c
JOIN Multiple_Picks p
ON p.event_id = c.event_id
AND p.pick = c.pick
AND p.score = c.closest
Now, from that, you can pick the best guesser(s).
SELECT y.member_nr
FROM
( SELECT COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
ORDER BY COUNT(*) DESC
LIMIT 1
) AS x -- the max number of correct guesses
STRAIGHT_JOIN
( SELECT member_nr, COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
) AS y -- the users who guessed correctly that many times
USING (ct);
All this is pretty complex; I may have some typos, even logic errors. But maybe I came close.
It seems an additional table to store the actual results would help here.
E.g let's say this is in a table called results with sample values as follows:
event_id winner result
1 Crusaders 16
2 Waratahs 15
3 Chiefs 4
4 Crusaders 17
5 Reds 12
0 Rebels 14
7 Cheetahs 15
8 Crusaders 14
This can then be JOINed on each row and results compared as follows:
SELECT p.*
, CASE WHEN ABS(p.score - r.result)
- CASE WHEN p.pick = r.winner THEN 999999 ELSE 0 END
= (SELECT MIN(ABS(p2.score - r2.result)
- CASE WHEN p2.pick = r2.winner THEN 999999 ELSE 0 END)
FROM picks p2
JOIN results r2
ON p2.event_id = r2.event_id
WHERE p2.event_id = p.event_id)
THEN 1
ELSE 0
END AS win
FROM picks p
JOIN results r
ON p.event_id = r.event_id;
Explanation
The rightmost win column is 1 if the member is calculated to have won or drawn the event, otherwise it is 0. The method used is similar to the one in your post, with the main difference being the team and score are combined. The main thing to be explained here is the 999999, which is subtracted when a correct team is picked - so this can be sure to eclipse the score difference. (Of course, an even bigger value could be picked if needed).
Demo
SQL Fiddle Demo