Related
Here's what I'm trying to do. Let's say I have this table t:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
2 | 18 | 2012-05-19 | y
3 | 18 | 2012-08-09 | z
4 | 19 | 2009-06-01 | a
5 | 19 | 2011-04-03 | b
6 | 19 | 2011-10-25 | c
7 | 19 | 2012-08-09 | d
For each id, I want to select the row containing the minimum record_date. So I'd get:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
4 | 19 | 2009-06-01 | a
The only solutions I've seen to this problem assume that all record_date entries are distinct, but that is not this case in my data. Using a subquery and an inner join with two conditions would give me duplicate rows for some ids, which I don't want:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
5 | 19 | 2011-04-03 | b
4 | 19 | 2009-06-01 | a
How about something like:
SELECT mt.*
FROM MyTable mt INNER JOIN
(
SELECT id, MIN(record_date) AS MinDate
FROM MyTable
GROUP BY id
) t ON mt.id = t.id AND mt.record_date = t.MinDate
This gets the minimum date per ID, and then gets the values based on those values. The only time you would have duplicates is if there are duplicate minimum record_dates for the same ID.
I could get to your expected result just by doing this in mysql:
SELECT id, min(record_date), other_cols
FROM mytable
GROUP BY id
Does this work for you?
To get the cheapest product in each category, you use the MIN() function in a correlated subquery as follows:
SELECT categoryid,
productid,
productName,
unitprice
FROM products a WHERE unitprice = (
SELECT MIN(unitprice)
FROM products b
WHERE b.categoryid = a.categoryid)
The outer query scans all rows in the products table and returns the products that have unit prices match with the lowest price in each category returned by the correlated subquery.
I would like to add to some of the other answers here, if you don't need the first item but say the second number for example you can use rownumber in a subquery and base your result set off of that.
SELECT * FROM
(
SELECT
ROW_NUM() OVER (PARTITION BY Id ORDER BY record_date, other_cols) as rownum,
*
FROM products P
) INNER
WHERE rownum = 2
This also allows you to order off multiple columns in the subquery which may help if two record_dates have identical values. You can also partition off of multiple columns if needed by delimiting them with a comma
This does it simply:
select t2.id,t2.record_date,t2.other_cols
from (select ROW_NUMBER() over(partition by id order by record_date)as rownum,id,record_date,other_cols from MyTable)t2
where t2.rownum = 1
If record_date has no duplicates within a group:
think of it as of filtering. Simpliy get (WHERE) one (MIN(record_date)) row from the current group:
SELECT * FROM t t1 WHERE record_date = (
select MIN(record_date)
from t t2 where t2.group_id = t1.group_id)
If there could be 2+ min record_date within a group:
filter out non-min rows (see above)
then (AND) pick only one from the 2+ min record_date rows, within the given group_id. E.g. pick the one with the min unique key:
AND key_id = (select MIN(key_id)
from t t3 where t3.record_date = t1.record_date
and t3.group_id = t1.group_id)
so
key_id | group_id | record_date | other_cols
1 | 18 | 2011-04-03 | x
4 | 19 | 2009-06-01 | a
8 | 19 | 2009-06-01 | e
will select key_ids: #1 and #4
SELECT p.* FROM tbl p
INNER JOIN(
SELECT t.id, MIN(record_date) AS MinDate
FROM tbl t
GROUP BY t.id
) t ON p.id = t.id AND p.record_date = t.MinDate
GROUP BY p.id
This code eliminates duplicate record_date in case there are same ids with same record_date.
If you want duplicates, remove the last line GROUP BY p.id.
This a old question, but this can useful for someone
In my case i can't using a sub query because i have a big query and i need using min() on my result, if i use sub query the db need reexecute my big query. i'm using Mysql
select t.*
from (select m.*, #g := 0
from MyTable m --here i have a big query
order by id, record_date) t
where (1 = case when #g = 0 or #g <> id then 1 else 0 end )
and (#g := id) IS NOT NULL
Basically I ordered the result and then put a variable in order to get only the first record in each group.
The below query takes the first date for each work order (in a table of showing all status changes):
SELECT
WORKORDERNUM,
MIN(DATE)
FROM
WORKORDERS
WHERE
DATE >= to_date('2015-01-01','YYYY-MM-DD')
GROUP BY
WORKORDERNUM
select
department,
min_salary,
(select s1.last_name from staff s1 where s1.salary=s3.min_salary ) lastname
from
(select department, min (salary) min_salary from staff s2 group by s2.department) s3
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
I was able to fix my previous query, however the results retrieve contains duplicates. I want to retrieve the latest record.
For example:
id | firstname | lastname | email | date
1 | steven | smith | steven#gmail.com 2013-06-10 04:01:25
2 | Bill | Johnson | bill#gmail.com | 2014-06-10 04:01:25
3 | steven | smith | steven#gmail.com | 2014-10-10 12:01:25
The return result should be the row with IDs 2 and 3
THe ID 1 should be not returned as the date is older than the ID 3 date
How can I add this to the query below ?
SELECT
users.firstname,
users.lastname,
DISTINCT(users.email),
users.pref
FROM (
SELECT
users.firstname,
users.lastname,
users.email,
users.status,
users.active,
CONCAT(
users.preference_1, ',',
users.preference_2, ',',
users.preference_3
) AS pref
FROM users
) AS users
WHERE users.status = 1
AND users.active = 1
AND users.date = (
SELECT MAX(u.date) FROM users AS u WHERE u.email = users.email
)
LIMIT 10000
no need for subselect
add: ORDER BY users.date DESC LIMIT 1 this way you sort the list by date and return only the "1st elemet" which is the one with the "biggest/latest" date
Well, looks like you want to fetch the latest row for each user.
Here's a query that can do it. (It also uses subquery. I don't understand the necessity for using subquery to concat though)
SELECT DISTINCT us.email, us.preference FROM users AS us WHERE us.date = (
SELECT MAX(u.date) FROM users AS u WHERE u.email = us.email
)
This does not fetch all the details you want. But gives you the basic idea about the query you might have to run.
this one been puzzling me for a couple of searching hours.
So I have a campaign table and a vendor Table. The vendor might have several campaigns.
I want to select all campaigns if the vendor has enough credits.
Problem is I don't know how many campaigns are going to be selected from the same vendor which means that the vendor might still have credits for two campaigns but not for the rest of them.
Example
tblvendors
+---------+------------+---------------+
|vendorId | vendorName | vendorCredits |
+---------+------------+---------------+
| 1 | a | 5 |
| 2 | b | 100 |
+---------+------------+---------------+
tblproducts
+-----------+---------------+------------+
| productId | productName | vendorId |
+-----------+---------------+------------+
| 1 | c | 1 |
| 2 | e | 2 |
| 3 | f | 1 |
| 4 | g | 1 |
| 5 | h | 1 |
+-----------+---------------+------------+
tblcampaigns
+------------+---------------+------------+
| campaignId | productId | vendorId |
+------------+---------------+------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 1 |
| 4 | 4 | 1 |
| 5 | 5 | 1 |
+------------+---------------+------------+
Now considering that everytime a row is selected the vendor looses 2 credits since vendor 'a' only has 5 credits left only campaigns 1 2 and 3 should be returned.
My current Query is this:
SET #maxCampaignId = (SELECT MAX(campaignId) FROM tblCampaigns);
SELECT
#maxCampaignId,
t0.campaignId,
t0.productId,
productName,
productDescription,
productImage,
(CASE WHEN campaignId > (SELECT configValue FROM tblconfiguration WHERE configKey = 'lastHomeCampaignId')
THEN campaignId ELSE campaignId + #maxCampaignId END) AS orderField
FROM tblcampaigns AS t0
INNER JOIN tblproducts AS t1 ON t0.productId = t1.productId
INNER JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId
WHERE
campaignType = 'homeFeature' AND
t0.isActive = 1 AND
t2.vendorCredits > (SELECT configValue FROM tblconfiguration WHERE configKey = 'campaignHomeFeatureCost' LIMIT 1)
ORDER BY orderField ASC
LIMIT 4
The problem as you can see is int the line that compares the vendorCredits. Obviously as is the query selects more campaigns than the vendor can afford.
I wanted to avoid doing this in PHP as I think it should be possible to do this straight out of the database.
Check this post, it may help - group by and having clauses. I'll try to do some test later
Using COUNT(*) in the WHERE clause
UPDATE:
select t2.vendorId, vendorCredits from tblcampaigns AS t0 JOIN tblproducts AS t1 ON t0.productId = t1.productId JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId group by t2.vendorId having t2.vendorCredits = count(t2.vendorId)
If I correctly understood the question: This query will select all vendors having more campains than credits.
Ok found it.
Thanks to this post: How do I limit the number of rows per field value in SQL?
What I did was Selecting the rows I wanted in the order I wanted as a subquery and its respective row number so that I could reorder it back in the end.
Then I made a second subquery ordered by the vendorId so that I could count the number of times it turned up and returning the row_count to the main query.
Finally in the main query I reordered it back to the row number in the deepest subquery but now I have the value I wanted to compare which is the value of credits per row * the current row number for a particular vendor.
Anyways maybe the code is cleared and here it goes:
SET #creditsCost = (SELECT configValue FROM tblconfiguration WHERE configKey = 'campaignHomeFeatureCost' LIMIT 1);
SET #maxCampaignId = (SELECT MAX(campaignId) FROM tblCampaigns);
SET #curRow = 0;
SELECT * FROM
(
SELECT *,
#num := if(#first_column = vendorId, #num:= #num + 1, 1) as row_num,
#first_column:=vendorId as c
FROM
(SELECT
#curRow := #curRow + 1 AS row_number,
#maxCampaignId,
t0.campaignId,
t0.productId,
t2.vendorId,
t2.vendorCredits,
productName,
productDescription,
productImage,
(CASE WHEN campaignId > (SELECT configValue FROM tblconfiguration WHERE configKey = 'lastHomeCampaignId')
THEN campaignId ELSE campaignId + #maxCampaignId END) AS orderField
FROM tblcampaigns AS t0
INNER JOIN tblproducts AS t1 ON t0.productId = t1.productId
INNER JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId
WHERE
campaignType = 'homeFeature' AND
t0.isActive = 1
ORDER BY orderField ASC) AS filteredCampaigns
ORDER BY vendorId
) AS creditAllowedCampaigns
WHERE
row_num * #creditsCost <= vendorCredits
ORDER BY row_number
Anyhow I still appreciate Who took the time to answer and try to help, and will be listening to future comments since I think this is not the best way performance wise.
What I would like to do is retrieve all data from a table, and order them by the number of games the user played in a specific category. Is there any way I can use some sort of "COUNT WHERE" sql statement?
here's what i have so far. it will only return the user if they have played a game in the "fps" category, but I want it to show all users in descending order even if they have not played an fps game. please excuse my crappy tables
SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id and played_games.game_cat = 'fps'
GROUP BY user_data.user_id
ORDER BY 'count' DESC;
user_data table
user_id | user
1 | jeff
2 | herb
3 | dug
played_games table
id | user_id | game | game_cat
1 | 2 | kill | fps
2 | 1 | shoot| fps
3 | 2 | COD | fps
4 | 3 | dogs | cas
You need a LEFT OUTER JOIN to get the records even if a corresponding record does not exist in the other table.
SELECT user, coalesce(count(game_cat), 0) as count
FROM user_data LEFT OUTER JOIN played_games
ON user_data.user_id = played_games.user_id AND played_games.game_cat='fps'
GROUP BY user_data.user_id
ORDER BY count desc;
Gives the following result on my screen
+------+-------+
| user | count |
+------+-------+
| herb | 2 |
| jeff | 1 |
| dug | 0 |
+------+-------+
This is how I'd do it. No subquery, no COALESCE, no COUNTIF junk.
SELECT `users`.`user`, COUNT(`played_games`.id) AS `c`
FROM `users`
LEFT OUTER JOIN `played_games` ON
`users`.`user_id` = `played_games`.`user_id`
AND `played_games`.`game_cat` = "fps"
GROUP BY `users`.`user_id`
ORDER BY `c` DESC, `user` ASC
SQLFiddle (not sure if you can link them like this...)
Try this:
SELECT ud.user, coalesce(sum(pg.game_cat = 'fps'), 0) Total
FROM user_data ud
LEFT JOIN played_games pg ON ud.user_id = pg.user_id
GROUP BY ud.user_id
ORDER BY Total DESC
This will show all users and the amount of times they've played a game with category 'fps'.
The coalesce one is promising, but doesn't work for me, sigh~ I just found NULLIF is a good way to solve this problem. Remember to use LEFT JOIN
COUNT( NULLIF(TABLE.ATTR, 1) ) AS total_count
The TABLE.ATTR is some field that can be NULL, here is an example:
SELECT Posts.*, COUNT( NULLIF(Comments.user_email, 1) ) as comment_num
FROM (`Posts`)
LEFT OUTER JOIN `Comments` ON `Comments`.`post_id` = `Posts`.`id`
GROUP BY `Posts`.`id`
LIMIT 5
Got the idea from http://www.bennadel.com/blog/579-SQL-COUNT-NULLIF-Is-Totally-Awesome.htm
Below query the all game category with user id and order by count
select * from (SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id(+) GROUP BY user_data.user_id)
order by count desc