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;
Related
I have a table like this
http://sqlfiddle.com/#!9/052381/1
I need to create a request that will find VIN codes that meet the following conditions:
VIN starts with XTA%
I have registration history: date_reg_last values: 1306440000,1506715200,1555963200. You need to select only those VIN codes that have exactly these values. If there are more or less records - VIN does not match
I have an owner_type that matches the values 1306440000,1506715200,1555963200: 2, 2, 2. Ie. for record 1306440000 owner_type must be 2, for record 1506715200 also 2, etc. The type can be different for each entry.
Similarly to the third point, I have regions: УЛЬЯНОВСК Г.,УЛЬЯНОВСК Г.,С РУНГА
I have a year, it should be in all records.
I tried making a request like this
SELECT *
FROM `ac_gibdd_shortinfo`
WHERE `vin` LIKE 'XTA%'
AND `model` LIKE '%1119%'
AND `date_reg_first` IN (0,1506715200,1555963200)
AND `date_reg_last` IN (1306440000,1506715200,1555963200)
AND `year` LIKE '2011'
AND `location` IN ('УЛЬЯНОВСК Г.','С РУНГА')
But it finds records that have a different number of registration records. There is only one thought: get all the matching records and then filter them by number with an additional request.
Test this:
SELECT *
FROM `ac_gibdd_shortinfo` t0
WHERE `vin` LIKE 'XTA%'
AND `model` LIKE '%1119%'
AND `date_reg_first` IN (0,1506715200,1555963200)
AND `date_reg_last` IN (1306440000,1506715200,1555963200)
AND `year` LIKE '2011'
AND `location` IN ('УЛЬЯНОВСК Г.','С РУНГА')
AND NOT EXISTS ( SELECT NULL
FROM ac_gibdd_shortinfo t1
WHERE t0.vin = t1.vin
AND t1.date_reg_first NOT IN (0,1506715200,1555963200) )
AND NOT EXISTS ( SELECT NULL
FROM ac_gibdd_shortinfo t2
WHERE t0.vin = t2.vin
AND t2.date_reg_last NOT IN (1306440000,1506715200,1555963200) )
AND NOT EXISTS ( SELECT NULL
FROM ac_gibdd_shortinfo t3
WHERE t0.vin = t3.vin
AND t3.location NOT IN ('УЛЬЯНОВСК Г.','С РУНГА') )
PS. According indices will improve.
and have count (1306440000,1506715200,1555963200) - 3 records in total by VIN – blood73
SELECT vin, model, date_reg_first, date_reg_last, `year`, location
FROM `ac_gibdd_shortinfo` t0
WHERE `vin` LIKE 'XTA%'
AND `model` LIKE '%1119%'
AND `date_reg_first` IN (0,1506715200,1555963200)
AND `date_reg_last` IN (1306440000,1506715200,1555963200)
AND `year` LIKE '2011'
AND `location` IN ('УЛЬЯНОВСК Г.','С РУНГА')
AND 3 = ( SELECT COUNT(*)
FROM ac_gibdd_shortinfo t1
WHERE t0.vin = t1.vin );
I ran into a problem trying to pull one action per user with the least priority, the priority is based on other columns content and is an integer,
This is the initial query :
SELECT
CASE
...
END AS dummy_priority,
id,
user_id
FROM
actions
Result :
id user_id priority
1 2345 1
2 2345 3
3 2999 5
4 2999 2
5 3000 10
Desired result :
id user_id priority
1 2345 1
4 2999 2
5 3000 10
Following what i want i tried
SELECT x.id, x.user_id, MIN(x.priority)
FROM (
SELECT
CASE
...
END AS priority,
id,
user_id
FROM
actions
) x
GROUP BY x.user_id
Which didn't work
Error Code: 1055. Expression #1 of SELECT list is not in GROUP BY
clause and contains nonaggregated column 'x.id' which is not
functionally dependent on columns in GROUP BY clause;
Most examples of this I found were extracting just the user_id and priority and then doing an inner join with both of them to get the row, but I can't do that since (priority, user_id) isn't unique
A simple verifiable example would be
CREATE TABLE `actions` (
`id` int(11) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`priority` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `actions` (`id`, `user_id`, `priority`) VALUES
(1, 2345, 1),
(2, 2345, 3),
(3, 2999, 5),
(4, 2999, 2),
(5, 3000, 10);
how to extract the desired result (please hold in mind that this table is a subquery)?
The proper way to do this would involve a subquery of some sort . . . and that would require repeating the case definition.
Here is another method, using the substring_index()/group_concat() trick:
SELECT SUBSTRING_INDEX(GROUP_CONCAT(x.id ORDER BY x.priority), ',', 1) as id,
x.user_id, MIN(x.priority)
FROM (SELECT (CASE ...
END) AS priority,
id, user_id
FROM actions a
) x
GROUP BY x.user_id;
And that proper way in full...
SELECT x...
, CASE...x... priority
FROM my_table x
JOIN
( SELECT user_id
, MIN(CASE...) priority
FROM my_table
GROUP
BY user_id
) y
ON y.user_id = x.user_id
AND y.priority = CASE...x...;
This should work ...
SELECT id , user_id, priority FROM actions act
INNER JOIN
(SELECT
user_id, MIN(priority) AS priority
FROM
actions
GROUP BY user_id) pri
ON act.user_id = pri.user_id AND act.priority = pri.prority
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.
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
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