Counting the number of first place scores with mysql - mysql

Ok, so I have the following database:
CREATE TABLE IF NOT EXISTS `highscores` (
`lid` int(11) NOT NULL,
`username` varchar(15) NOT NULL,
`score` int(16) NOT NULL,
PRIMARY KEY (`lid`,`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
lid being the level id.
lets say I have the following values in the table:
lid, username,score
1,sam,15
1,joe,12
1,sue,6
1,josh,9
2,sam,8
2,joe,16
2,sue,4
3,sam,65
4,josh,87
4,sue,43
5,sam,12
5,sue,28
5,joe,29
and so on.
How would I create a query(or if required a set of queries) to get the following
sam has 3 high scores
joe has 2 high scores
josh has 1 high score
Thanks in advance.

i have not tested it, but try the following query
select
concat(h.username ," has ", count(h.username)," high scores ")
from
highscores h inner join
(select lid, max(score) as maxscore
from highscores group by lid) t on h.lid = t.lid and h.score = t.maxscore
group by h.username

From what you've described this query will produce what you need
SELECT username,COUNT(*) as num_highscores FROM (
SELECT lid,username
FROM highscores h1
WHERE score=(
SELECT MAX(score)
FROM highscores h2
WHERE h2.lid=h1.lid
)
) AS high_scores
GROUP BY username
ORDER BY num_highscores DESC
Although the results I get on your sample data are different:
sam 2
joe 2
josh 1

Related

Wrong data output in SQL request

I have a table named payments
CREATE TABLE payments (
`id` INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
`student_id` INT NOT NULL,
`datetime` DATETIME NOT NULL,
`amount` FLOAT DEFAULT 0,
INDEX `student_id` (`student_id`)
);
It is necessary to create a query that is find all student_id whose sum payment is less than the biggest one. (it can be more than one user with the same biggest amount of payments)
Let assume for instance this is a test data:
== Dumping data for table payments
id-student_id-datetime-amount
|1|4|2015-06-11 00:00:00|2
|2|5|2015-06-01 00:00:00|6
|3|1|2015-06-03 00:00:00|8
|4|2|2015-06-02 00:00:00|9
|5|4|2015-06-09 00:00:00|6
|6|5|2015-06-06 00:00:00|3
|7|2|2015-06-05 00:00:00|6
|8|3|2015-06-09 00:00:00|12
|14|1|2015-06-01 00:00:00|0
|15|1|2015-06-03 00:00:00|7
|16|6|2015-06-02 00:00:00|0
|17|6|2015-06-07 00:00:00|0
|18|6|2015-06-05 00:00:00|0
Next query shows all students with their sum payments
SELECT `student_id`, SUM(amount) as `sumamount`
FROM `payments`
GROUP BY `student_id`
ORDER BY `sumamount` DESC
Here is write output of this query ordered by sumamount
student_id sumamount
1 15
2 15
3 12
5 9
4 8
6 0
BUT the problem is when I try to get the user who paid less than the biggest one it gives me the wrong answer
Here is the query to get the second user:
SELECT `student_id`, SUM(amount) as `sumamount`
FROM `payments`
GROUP BY `student_id`
HAVING `sumamount` < MAX(sumamount)
ORDER BY `sumamount` DESC
Here is the result
student_id sumamount
3 12
4 8
6 0
As we can see student_id = 5 missed and I have no idea why.
You need to calcualate MAX(sumamount) in a subquery, so that MAX is not grouped by student_id.
SELECT `student_id`, SUM(amount) as `sumamount`, maxsum
FROM `payments`
CROSS JOIN (SELECT MAX(sumamount) AS maxsum
FROM (SELECT SUM(amount) AS sumamount
FROM payments
GROUP BY student_id) t1) t2
GROUP BY `student_id`
HAVING `sumamount` < maxsum
ORDER BY `sumamount` DESC
DEMO

MySQL query optimization with group by clause

I want to calculate total and unique clickouts based on country,partner and retailer.
I have achieved the desired result but i think its not a optimal solution and for longer data sets it will take longer time. how can I improve this query?
here is my test table, designed query and expected output:
"country_id","partner","retailer","id_customer","id_clickout"
"1","A","B","100","XX"
"1","A","B","100","XX"
"2","A","B","100","XX"
"2","A","B","100","GG"
"2","A","B","100","XX"
"2","A","B","101","XX"
DROP TABLE IF EXISTS x;
CREATE TEMPORARY TABLE x AS
SELECT test1.country_id, test1.partner,test1.retailer, test1.id_customer,
SUM(CASE WHEN test1.id_clickout IS NULL THEN 0 ELSE 1 END) AS clicks,
CASE WHEN test1.id_clickout IS NULL THEN 0 ELSE 1 END AS unique_clicks
FROM test1
GROUP BY 1,2,3,4
;
SELECT country_id,partner,retailer, SUM(clicks), SUM(unique_clicks)
FROM x
GROUP BY 1,2,3
Output:
"country_id","partner","retailer","SUM(clicks)","SUM(unique_clicks)"
"1","A","B","2","1"
"2","A","B","4","2"
And here is DDL and input data:
CREATE TABLE test (
country_id INT(11) DEFAULT NULL,
partner VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL,
retailer VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL,
id_customer BIGINT(20) DEFAULT NULL,
id_clickout VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO test VALUES(1,'A','B','100','XX'),(1,'A','B','100','XX'),
(2,'A','B','100','XX'),(2,'A','B','100','GG'),
(2,'A','B','100','XX'),(2,'A','B','101','xx')
SELECT
country_id,
partner,
retailer,
COUNT(id_clickout) AS clicks,
COUNT(DISTINCT CASE WHEN id_clickout IS NOT NULL THEN id_customer END) AS unique_clicks
FROM
test1
GROUP BY
1,2,3
;
COUNT(a_field) won't count any NULL values.
So, COUNT(id_clickout) will only count the number of times that it is NOT NULL.
Equally, the CASE WHEN statement in the unique_clicks only returns the id_customer for records where they clicked, otherwise it returns NULL. This means that the COUNT(DISTINCT CASE) only counts distinct customers, and only when they clicked.
EDIT :
I just realised, it's potentially even simpler than that...
SELECT
country_id,
partner,
retailer,
COUNT(*) AS clicks,
COUNT(DISTINCT id_customer) AS unique_clicks
FROM
test1
WHERe
id_clickout IS NOT NULL
GROUP BY
1,2,3
;
The only material difference in the results will be that any country_id, partner, retailed that previously showed up with 0 clicks will now not appear in the results at all.
With an INDEX on country_id, partner, retailed, id_clickout, id_customer or country_id, partner, retailed, id_customer, id_clickout, however, this query should be significantly faster.
I think this is what you are after:
SELECT country_id,partner,retailer,COUNT(retailer) as `sum(clicks)`,count(distinct id_clickout) as `SUM(unique_clicks)`
FROM test1
GROUP BY country_id,partner,retailer
Result:
COUNTRY_ID PARTNER RETAILER SUM(CLICKS) SUM(UNIQUE_CLICKS)
1 A B 2 1
2 A B 4 2
See result in SQL Fiddle.

SELECT multiple values and order by relevance

I have the following structure:
CREATE TABLE IF NOT EXISTS `user_subjects` (
`user_subject_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`subject_id` int(11) NOT NULL,
PRIMARY KEY (`user_subject_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
and I want to get the user_ids that has subject_id 1 or 2, for example, ordered by relevance. (edit: i meant by how many results it matches)
I've tryed this but it doesn't count the relevance. It returns relevance 1.
http://sqlfiddle.com/#!2/944ab8/2
For that example i wanted to get
user_id relevance
1 1
2 2
and the subject_id matched if it's possible.
Thanks
Something like the following will I think do what you want, although I'm not entirely clear what you mean by "relevance"; this simply counts the number of rows that match both the user and have subject_id 1 or 2:
SELECT
user_id,
COUNT(subject_id) AS relevance
FROM
`user_subjects`
WHERE
subject_id IN (1, 2)
GROUP BY
user_id
ORDER BY
relevance DESC
(This is mysql-specific, I believe, because of the use of relevance in ORDER BY.)
Note sure I follow. Do you mean something like this:
SELECT user_id
, SUM(CASE WHEN subject_id IN(1,2) THEN 1 ELSE 0 END)ttl
FROM user_subjects
GROUP
BY user_id;
?
Well, you'll never get a relevance of 2, because (subject_id LIKE 1) + (subject_id LIKE 2) will return 1 at most.
Maybe you should rethink your query.

Mysql Query for Latest High Scores, not the Highest High scores

Edit: SOLVED by Ian & Mark...
The high scores for a game need to be fetched from a MySQL database (using php).
The query I use works fine for stats that always increase in time.
The query however does not work for stats that can go up or down in time.
Some details:
The stats for players are stored in the stats table. Multiple rows per player (all updates), an auto_increment column is Primary key.
CREATE TABLE IF NOT EXISTS `stats` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stamp` bigint(20) NOT NULL,
`username` varchar(255) NOT NULL,
`kill` int(11) NOT NULL,
`death` int(11) NOT NULL,
`kdr` decimal(6,3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=956 ;
Some sample data:
1, 1365175892, user1, 1089, 191, 5.702
2, 1365175892, user2, 1805, 547, 6.709
3, 1365175892, user3, 104397, 2272, 45.949
4, 1365175892, user1, 1163, 200, 5.815
5, 1365175892, user2, 1090, 204, 5.343
The Kills & Deaths increment by time and thus it is simple to create a top 10.
The issue
For the Kill to Death Ratio (KDR), the value can go up or down in time.
Example: user2 in update 5 has a lower KDR that in update 2, but still my high score list shows the highest KDR of update 2, while I actually need the lower KDR from update 5.
So here, for the top 10, I do not need the Highest value (that would be automatically the last value stored for each player) but the LATEST value (that is not nescesarily the highest value).
And I can't get it to work...
One of the queries I tried is this one that works for increasing-only-stats:
SELECT s.*
FROM stats AS s
INNER JOIN
(
SELECT username, MAX(kdr) AS kdr
FROM stats
GROUP BY username
) AS groupedstats
ON s.username = groupedstats.username
AND s.kdr = groupedstats.kdr
ORDER BY kdr DESC
LIMIT 10
Could anyone help me how to get the correct kdr high-score list? I'm lost...
Thanks
Assuming you want the top 10 by kdr for the latest record for each user, try:
SELECT s.*
FROM stats AS s
INNER JOIN
(
SELECT username, MAX(`stamp`) AS `stamp`
FROM stats
GROUP BY username
) AS groupedstats
ON s.username = groupedstats.username
AND s.`stamp` = groupedstats.`stamp`
ORDER BY kdr DESC
LIMIT 10
Something like this ?
select * from stats where id in (
select max(id) as maxid
from stats
group by username
)
order by kdr desc limit 10

Select characters and group together with joins?

I have the following table setup in mysql:
CREATE TABLE `games_characters` (
`game_id` int(11) DEFAULT NULL,
`player_id` int(11) DEFAULT NULL,
`character_id` int(11) DEFAULT NULL,
KEY `game_id_key` (`game_id`),
KEY `character_id_key` (`character_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
My objective is to get a game_id where a list of character_ids are all present in this game_id.
An example set of data:
1, 1
1, 2
1, 3
2, 1
2, 2
3, 1
3, 4
Let's say i want to get the game_id where the character_id has 1, 2, and 3. How would I go about making an efficient query? Best idea I have had so far was joining the table to itself multiple times, but i assume there has to be a better way to do this.
Thanks
EDIT: for anyone curious this was the final solution I used as it proved the best query time:
SELECT game_ID
FROM (
SELECT DISTINCT character_ID, game_ID
FROM games_Characters
) AS T
WHERE character_ID
IN ( 1, 2, 3 )
GROUP BY game_ID
HAVING COUNT( * ) =3
Select game_ID from games_Characters
where character_ID in (1,2,3)
group by game_ID
having count(*) = 3
the above makes two assumptions
1) you know the characters your looking for
2) game_ID and character_ID are unique
I don't assume you can get the #3 for the count I knnow you can since you know the list of people you're looking for.
This ought to do it.
select game_id
from games_characters
where character_id in (1,2,3)
group by game_id
having count(*) = 3
If that's not dynamic enough for you you'll need to add a few more steps.
create temporary table character_ids(id int primary key);
insert into character_ids values (1),(2),(3);
select #count := count(*)
from character_ids;
select gc.game_id
from games_characters as gc
join character_ids as c
on (gc.character_id = c.id)
group by gc.game_id
having count(*) = #count;