Until today i thought i know something about MySQL.
OK lets say we have one table like this:
id | some_name |some_number
-----------------------------
1 | test | 33
2 | test | 34
3 | test | 35
3 | test2 | 36
3 | test2 | 37
and i want to write query to return something like this:
test 33
test 34
test2 36
test2 37
test3 12
test3 34
.
.
.
and so on. I want to return only 2 result per same name. It is easy to use limit and return only one result per name but I'm stuck to return multiple results per same name in this case 2 but might be an n results. Work around is to make some script that will do:
select some_name, some_number from tbl_name limit 2;
and to repeat it for every distinct some_name i have in table.
Is there any elegant solution for MySQL? I would be grateful if you share that with me.
You can use a user variable to add a counter of each row of a name, then just select the rows where the counter is less than or equal to 2 (untested):-
SELECT some_name, some_number
FROM
(
SELECT some_name, some_number, #cnt=(#some_name = some_name, #cnt + 1, 1) AS cnt, #some_name:=some_name
FROM
(
SELECT some_name, some_number
FROM tbl_name
ORDER BY some_name, some_number
) sub0
CROSS JOIN
(
SELECT #some_name:='', #cnt:=0
)
sub1
) sub2
WHERE cnt <= 2
you could try this,
select some_name,some_number from yourTable t
where
(select count(*) from yourTable
where
some_number<=t.some_number and some_name=t.some_name)<3
This partially solved my problem:
set #num := 0, #name := '';
select distinct number, name
from (
select number, name,
#num := if(#name = name, #num +1, 1) as row_number,
#name := name as dummy
from karian2
) as x where x.row_number <= 2;
It fails some time to return 2 results because sub query is not returning distinct values. Example here:
http://sqlfiddle.com/#!2/952ca/23
Related
I'm always "irk" by unnecessary join. But in this case, I wonder if it's possible to not use join.
This is an example of the table I have:
id | team | score
1 | 1 | 300
2 | 1 | 257
3 | 2 | 127
4 | 2 | 533
5 | 3 | 459
This is what I want:
team | score | id
1 | 300 | 1
2 | 533 | 4
3 | 459 | 5
Doing a query looking like this:
(basically: who's the best player of each team)
SELECT team, MAX(score) AS score, id
FROM my_table
GROUP BY team
But I get something like that:
team | score | id
1 | 300 | 1
2 | 533 | 3
3 | 459 | 5
But it's not the third player that got 533 points, so the result have no consistency.
Is it possible to get truthworthy results without joining the table with itself? How to achieve that?
You can do it without joins by using subquery like this:
SELECT id, team, score
FROM table1 a
WHERE score = (SELECT MAX(score) FROM table1 b WHERE a.team = b.team);
However in big tables this can be very slow as you have to run the whole subquery for every row in your table.
However there's nothing wrong with using join to filter results like this:
SELECT id, team, score FROM table1 a
INNER JOIN (
SELECT MAX(score) score, team
FROM table1
GROUP BY team
) b ON a.score = b.score AND a.team = b.team
Although joining itself is quite expensive, this way you only have to run two actual queries regardless how many rows are in your tables. So in big tables this method can still be hundreds, if not thousands of times faster than the first method with subquery.
You can use variables:
SELECT id, team, score
FROM (
SELECT id, team, score,
#seq := IF(#t = team, #seq,
IF(#t := team, #seq + 1, #seq + 1)) AS seq,
#grp := IF(#t2 = team, #grp + 1,
IF(#t2 := team, 1, 1)) AS grp
FROM mytable
CROSS JOIN (SELECT #seq := 0, #t := 0, #grp := 0, #t2 := 0) AS vars
ORDER BY score DESC) AS t
WHERE seq <= 3 AND grp = 1
Variable #seq is incremented each time a new team is met as the records are being processed in descending score order. Variable #grp is used to enumerate records within each team partition. Records with #grp = 1 are the ones having the greatest score value within the team slice.
Demo here
Unfortantly , MySQL doesn't support window functions like ROW_NUMBER() which could have solved this easily.
There are several ways on doing that:
NOT EXISTS() :
SELECT * FROM YourTable t
WHERE NOT EXISTS(SELECT 1 FROM YourTable s
WHERE t.team = s.team AND s.score > t.score)
NOT IN() :
SELECT * FROM YourTable t
WHERE (t.team,t.score) IN(SELECT s.team,MAX(s.score)
FROM YourTable s
GROUP BY s.team)
A correlated query:
SELECT distinct t.id,t.team,
(SELECT s.score FROM YourTable s
WHERE s.team = t.team
ORDER BY s.score DESC
LIMIT 1)
FROM YourTable t
Or a join which I understand you already have.
EDIT : I take my words back, you can do it with a variable like #GiorgosBetsos solution.
You could do something like this:
SELECT team, score, id
FROM (SELECT *
,RANK() OVER
(PARTITION BY team ORDER BY score DESC) AS Rank
FROM my_table) ranked_result
WHERE Rank = 1;
Some info on Rank functionality: Clicketyclickclick
I try to get all the years in a table with 174 rows. There is two differents years 2016 and 2017. And add to the query's response and Id which correspond at the "row count" of the response.
Here is the query :
SET #n=0;
SELECT YEAR (`Date`) AS Libelle, #n := #n + 1 AS id FROM Datas GROUP by Libelle
The response is :
|Libelle| Id|
| 2016| 1|
| 2017|174|
What's the way to get :
|Libelle| Id|
| 2016| 1|
| 2017| 2|
174 corresponds to the last record in my table. And I understand that's #n is incremented with all other rows.
Have you an idea to do this ?
try this. so the counter works only on the result:
SELECT #n := #n + 1 AS id , tmp.*
FROM (
SELECT DISTINCT YEAR (`Date`) AS Libelle FROM Datas GROUP by Libelle ) AS tmp,
( SELECT #n := 0) as parameter;
Something like this perhaps?
SELECT
t1.Libelle,
#n := #n + 1 AS id
FROM (
SELECT DISTINCT YEAR(`Date`) AS Libelle
FROM Datas
GROUP BY Libelle
) t1,
(
SELECT #n := 0
) t2;
Related: https://stackoverflow.com/a/4474389/4152813
I have a table department_courses with following structure :
id department_id name
-- ------------- -----
1 11 Abcd
2 11 Bghg
3 11 Lopps
4 13 Abvgf
So from this table I need to count the position of the subquery. I mean to say , The position of the name Lopps for department_id is 3 . How to get this in mysql query?
If you only need to do this for one row, then a single query is simpler:
select count(*)
from department_courses dc
where dc.id <= (select dc2.id
from department_courses dc2
where dc2.name = 'Lopps'
);
If you want to assign a row number to all rows, then variables are probably a better method.
Try:
select row_num
from (
select t.*, #r := #r + 1 row_num
from department_courses t,
(select #r := 0) r
) x
where x.name = 'Lopps'
x.department_id = 3
I need to pull the name of the students who stood second positions from grade 1 to grade 12. each grade has separate databases with similar table structure
I have the following data:
Set 1
uid marks
1 10
2 20
3 17
4 17
5 20
6 20
Set 2
uid marks
1 10
2 20
3 17
4 17
5 20
6 17
7 20
I need a query which can say uid 3,4 are second in set 1 and 3,4,6 are second in set 2.
i need it in a single query because there are several set of databases
what could be the possible way?
I tried:
SELECT * FROM TBL WHERE marks ! = SELECT MAX(marks) from tbl
but it fetched all marks except the highest
Try this out:
SELECT uid, marks FROM (
SELECT uid, marks, #rank := #rank + (#prevMarks != marks) rank, #prevMarks := marks
FROM t, (SELECT #rank := 0, #prevMarks := 0) init
ORDER BY marks
) s
WHERE rank = 2
Fiddle here.
Another alternative without User Defined Variables:
SELECT t.uid, t.marks FROM t
JOIN (
SELECT DISTINCT marks FROM t
ORDER BY marks
LIMIT 1, 1
) s
ON t.marks = s.marks
Output:
| UID | MARKS |
|-----|-------|
| 3 | 17 |
| 4 | 17 |
Use LIMIT and ORDER BY
SELECT * FROM TBL ORDER BY marks DESC LIMIT 1,1
There you ordered all students by marks fro hi to low. And then limit return from second (0 is first record) and return only one record.
If need all students with second mark, the use subquery
SELECT * FROM TBL WHERE marks = (
SELECT marks FROM TBL ORDER BY marks DESC GROUP BY marks LIMIT 1,1
)
SELECT *
FROM table
WHERE mark = (
SELECT MAX(mark)
FROM table
WHERE mark <
(
SELECT MAX(mark)
FROM table
)
)
Try this
SELECT t.marks, t.uid, (
SELECT COUNT( marks ) +1
FROM tbl t1
WHERE t.marks < t1.marks
) AS rank
FROM tbl t
LIMIT 0 , 30
now you can use rank column with bit modification below
SELECT * from (
SELECT t.marks, t.uid, (
SELECT COUNT( marks ) +1
FROM tbl t1
WHERE t.marks < t1.marks
) AS rank
FROM tbl t
) alias where rank=n (2 here)
I would like data rows numbered upon selecting from a table. Problem is that, I need not sequential numbering, but it should be numbered from 1 to 3 and so to the end, like below:
1 | first row
2 | second row
3 | third row
1 | fourth row
2 | and
3 | ....
1
2
3
I'm trying this query, but it does not work correctly:
mysql -> SET #n = 0;
-> SELECT
CASE
WHEN nnn = 3 THEN #n := 0
ELSE nnn
END
FROM (
SELECT #n := #n + 1 AS nnn FROM mytable
) AS t;
How to make it working correctly?
Adapted from this answer: With MySQL, how can I generate a column containing the record index in a table?
SELECT MOD(#curRow := #curRow + 1, 3) AS row_number
FROM mytable m
JOIN (SELECT #curRow := 0) r;
If you only need to select and assuming that there is column, say "id", that is sequential, then following query should help:
SELECT IF ((id%3) = 0, 3 , (id%3)) AS new_id, <other columns> FROM <table_name>;
Hope it helps...