Combining two unrelated SELECT-s with different ORDER-s into one result - mysql

TL;DR: How to combine two differently ordered SELECTs from unrelated tables into one result?
Let's say I have 2 non-related tables - cats and games.
Table "cats":
+----+----------------+-----+
| id | name | age |
+----+----------------+-----+
| 1 | Balthazar | 3 |
| 2 | Milkman | 7 |
| 3 | The Dark Angel | 4 |
+----+----------------+-----+
Table "games"
+----+----------+-------+
| id | name | plays |
+----+----------+-------+
| 1 | Snake | 18 |
| 2 | Lemmings | 234 |
| 3 | Ludo | 33 |
+----+----------+-------+
For each of the tables respecitively I need a SELECT to order them, and get different columns:
SELECT age FROM `cats` ORDER BY age DESC;
and
SELECT plays FROM `games` ORDER BY plays DESC;
Thing is I want to get both results using one query, instead of two. The expected result:
+-----+-------+
| age | plays |
+-----+-------+
| 7 | 234 |
| 4 | 33 |
| 3 | 18 |
+-----+-------+
No point for a JOIN as the tables share no relation. I was thinking of using UNION but I can't figure out how to handle the fact that both queries:
select different columns
order the result by different columns
Any ideas how to achieve this?

One method is to calculate a row number and then join on that value:
select rn, max(age) as age, max(plays) as plays
from ((select c.age, NULL as plays, (#rnc := #rnc + 1) as rn
from cats c cross join (select #rnc := 0) params
) union all
(select NULL as age, g.plays, (#rng := #rng + 1) as rn
from games g cross join (select #rng := 0) params
)
) cg
group by rn;

select age,plays from cats inner join games on cats.id=games.id

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.

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;

MySQL select unique rows in two columns with the highest value in one column

I have a basic table:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 2 | jamie | 2 | 100 |
| 3 | jamie | 1 | 50 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
I tried using the "Left Joining with self, tweaking join conditions and filters" part of this answer: SQL Select only rows with Max Value on a Column but some reason when there are records with a value of 0 it breaks, and it also doesn't return every unique answer for some reason.
When doing the query on this table I'd like to receive the following values:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
Because they are unique on name and cat and have the highest time value.
The query I adapted from the answer above is:
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, id, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON a.id = b.id AND a.time = b.time
The issue here is that ID is unique per row you can't get the unique value when getting the max; you have to join on the grouped values instead.
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON A.Cat = B.cat and A.Name = B.Name AND a.time = b.time
Think about it... So what ID is mySQL returning form the Inline view? It could be 1 or 3 and 2 or 4 for jamie. Hows does the engine know to pick the one with the max ID? it is "free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. " it could pick the wrong one resulting in incorrect results. So you can't use it to join on.
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
If you want to use a self join, you could use this query:
SELECT
d1.*
FROM
date d1 LEFT JOIN date d2
ON d1.name=d2.name
AND d1.cat=d2.cat
AND d1.time<d2.time
WHERE
d2.time IS NULL
It is very simple
SELECT MAX(TIME),name,cat FROM table name group by cat

SQL Query for selecting multiple rows but highest value for each PK

I know that the title sounds horrible but I have no idea how to summarize it better. I'm pretty sure that somebody had the same problem before but I couldn't find anything. RDBMS: MySQL.
Problem:
I have the following (simplified) table:
+------+------------+---------------------------------+
| name | date | score |
+------+------------+---------------------------------+
| A | 01.01.2015 | 1 |
| A | 01.02.2015 | 3 |
| A | 01.03.2015 | 4 |
| B | 01.01.2015 | 3 |
| B | 01.02.2015 | 4 |
| B | 01.03.2015 | 5 |
| C | 01.01.2015 | 1 |
| C | 01.02.2015 | 2 |
| C | 01.03.2015 | 3 |
+------+------------+---------------------------------+
There is no unique constraint or PK defined.
The table represents a highscore of a game. Every day the score of all players are inserted with values that are: name, points, now(),...
The data represent a snapshot of the score of each player at a specific time.
I want the most recent entry for each user only but only for the highest X players. So the result should look like
+------+------------+---------------------------------+
| name | date | score |
+------+------------+---------------------------------+
| A | 01.03.2015 | 4 |
| B | 01.03.2015 | 5 |
+------+------------+---------------------------------+
C doesn't appear since he's not in the top 2 (by score)
A appears with the most recent row (by date)
B appears, like A, with the most recent row (by date) and because he is in the top 2
I hope it becomes clear what I mean.
Thanks in advance!
I understand that what you need is to first select the X players who've gotten the highest score and then get their latest performance. In this case, you should do this:
SELECT *
FROM tablename t
JOIN
(
SELECT t.name, max(t.date) as max_date
FROM tablename t
JOIN
(
SELECT name
FROM
(
SELECT name, max(score) as max_score
FROM table_name
GROUP BY name
) all_highscores
ORDER BY max_score DESC
LIMIT X
) top_scores
ON top_scores.name = t.name
GROUP BY t.name
) top_last
on t.name = top_last.name
and t.date = top_last.date;

I need to get the average for every 3 records in one table and update column in separate table

Table Mytable1
Id | Actual
1 ! 10020
2 | 12203
3 | 12312
4 | 12453
5 | 13211
6 | 12838
7 | 10l29
Using the following syntax:
SELECT AVG(Actual), CEIL((#rank:=#rank+1)/3) AS rank FROM mytable1 Group BY rank;
Produces the following type of result:
| AVG(Actual) | rank |
+-------------+------+
| 12835.5455 | 1 |
| 12523.1818 | 2 |
| 12343.3636 | 3 |
I would like to take AVG(Actual) column and UPDATE a second existing table Mytable2
Id | Predict |
1 | 11133
2 | 12312
3 | 13221
I would like to get the following where the Actual value matches the ID as RANK
Id | Predict | Actual
1 | 11133 | 12835.5455
2 | 12312 | 12523.1818
3 | 13221 | 12343.3636
IMPORTANT REQUIREMENT
I need to set an offset much like the following syntax:
SELECT #rank := #rank + 1 AS Id , Mytable2.Actual FROM Mytable LIMIT 3 OFFSET 4);
PLEASE NOTE THE AVERAGE NUMBER ARE MADE UP IN EXAMPLES
you can join your existing query in the UPDATE statement
UPDATE Table2 T2
JOIN (
SELECT AVG(Actual) as AverageValue,
CEIL((#rank:=#rank+1)/3) AS rank
FROM Table1, (select #rank:=0) t
Group BY rank )T1
on T2.id = T1.rank
SET Actual = T1.AverageValue