I have a table like this:
+----+---------+------------+
| id | conn_id | read_date |
+----+---------+------------+
| 1 | 1 | 2010-02-21 |
| 2 | 1 | 2011-02-21 |
| 3 | 2 | 2011-02-21 |
| 4 | 2 | 2013-02-21 |
| 5 | 2 | 2014-02-21 |
+----+---------+------------+
I want the second highest read_date for particular 'conn_id's i.e. I want a group by on conn_id. Please help me figure this out.
Here's a solution for a particular conn_id :
select max (read_date) from my_table
where conn_id=1
and read_date<(
select max (read_date) from my_table
where conn_id=1
)
If you want to get it for all conn_id using group by, do this:
select t.conn_id, (select max(i.read_date) from my_table i
where i.conn_id=t.conn_id and i.read_date<max(t.read_date))
from my_table t group by conn_id;
Following answer should work in MSSQL :
select id,conn_id,read_date from (
select *,ROW_NUMBER() over(Partition by conn_id order by read_date desc) as RN
from my_table
)
where RN =2
There is an intresting article on use of rank functions in MySQL here : ROW_NUMBER() in MySQL
If your table design as ID - date matching (ie a big id always a big date), you can group by id, otherwise do the following:
$sql_max = '(select conn_id, max(read_date) max_date from tab group by 1) as tab_max';
$sql_max2 = "(select tab.conn_id,max(tab.read_date) max_date2 from tab, $sql_max
where tab.conn_id = tab_max.conn_id and tab.read_date < tab_max.max_date
group by 1) as tab_max2";
$sql = "select tab.* from tab, $sql_max2
where tab.conn_id = tab_max2.conn_id and tab.read_date = tab_max2.max_date2";
Related
Lets say we have a table that looks like this:
+---------------+----------------+-------------------+
| ID | random_string | time |
+---------------+----------------+-------------------+
| 2 | K2K3KD9AJ |2022-07-21 20:41:15|
| 1 | SJQJ8JD0W |2022-07-17 23:46:13|
| 1 | JSDOAJD8 |2022-07-11 02:52:21|
| 3 | KPWJOFPSS |2022-07-11 02:51:57|
| 1 | DA8HWD8HHD |2022-07-11 02:51:49|
------------------------------------------------------
I want to select the last 3 entries into the table, however they must all have separate ID's.
Expected Result:
+---------------+----------------+-------------------+
| ID | random_string | time |
+---------------+----------------+-------------------+
| 2 | K2K3KD9AJ |2022-07-21 20:41:15|
| 1 | SJQJ8JD0W |2022-07-17 23:46:13|
| 3 | KPWJOFPSS |2022-07-11 02:51:57|
------------------------------------------------------
I have already tried:
SELECT DISTINCT id FROM table ORDER BY time DESC LIMIT 3;
And:
SELECT MIN(id) as id FROM table GROUP BY time DESC LIMIT 3;
If you're not on MySQL 8, then I have two suggestions.
Using EXISTS:
SELECT m1.ID,
m1.random_string,
m1.time
FROM mytable m1
WHERE EXISTS
(SELECT ID
FROM mytable AS m2
GROUP BY ID
HAVING m1.ID=m2.ID
AND m1.time= MAX(time)
)
Using JOIN:
SELECT m1.ID,
m1.random_string,
m1.time
FROM mytable m1
JOIN
(SELECT ID, MAX(time) AS mxtime
FROM mytable
GROUP BY ID) AS m2
ON m1.ID=m2.ID
AND m1.time=m2.mxtime
I've not test in large data so don't know which will perform better (speed) however this should return the same result:
Here's a fiddle
Of course, this is considering that there will be no duplicate of exact same ID and time value; which seems to be very unlikely but still it's possible.
Using MySql 8 an easy solution is to assign a row number using a window:
select Id, random_string, time
from (
select *, Row_Number() over(partition by id order by time desc) rn
from t
)t
where rn = 1
order by time desc
limit 3;
See Demo
I have a table called 'user_text'
| id | user_id | date | text |
|----|---------|---------------------|----------------------|
| 1 | 4 | 07/01/2019 10:04:11 | This is a test |
| 2 | 9 | 19/11/2018 09:43:00 | Here's another test |
| ...| | | |
What I need to do is to select the 5 most recent (field 'date') entries for each user_id
I've searched a lot about it and it seems that somehow I need a subquery but I can't find the right combination.
In MySQL 5.x, one option uses a correlated subquery:
select u.*
from user_text u
where (
select count(*)
from user_text u1
where u1.user_id = u.user_id and u1.date >= u.date
) <= 5
You can use row_number():
select t.*
from (select t.*, row_number() over (partition by user_id order by date desc) as seqnum
from t
) t
where seqnum <= 5;
It's been asked before, but I can't get it to work properly. The selected answer doesn't work with duplicate values. The second answer should be able to handle duplicates according to the poster, but it's not functioning correctly with my data.
What I want to achieve is pretty simple:
I have a database containing all scores of all users. I want to build a highscore table, so I want to select all highscore rows of each user. With highscore row I mean the row for that user where his score is the highest.
Here's a demo I made based on the answer I mentioned at the top:
CREATE TABLE test(
score INTEGER,
user_id INTEGER,
info INTEGER
);
insert into test(score, user_id, info)
values
(1000, 1, 1),
(1000, 1, 2),
(2000, 2, 3),
(2001, 2, 1);
--
SELECT t.*
FROM test t
JOIN (SELECT test.user_id, max(score) as mi FROM test GROUP BY user_id) j ON
t.score = j.mi AND
t.user_id = j.user_id
ORDER BY score DESC, info ASC;
Expected output:
+-------+---------+------+
| score | user_id | info |
+-------+---------+------+
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
+-------+---------+------+
--> every user_id is present with the row where the user had the highest score value.
Real output:
+-------+---------+------+
| score | user_id | info |
+-------+---------+------+
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
| 1000 | 1 | 2 |
+-------+---------+------+
--> when there are duplicate values, user show up multiple times.
Anyone who can point me in the right direction?
I assume when there are duplicate scores you want the lowest info just like your expected output.
With NOT EXISTS:
select t.* from test t
where not exists (
select 1 from test
where user_id = t.user_id and (
score > t.score or (score = t.score and info < t.info)
)
);
See the demo.
For MySql 8.0+ you can use ROW_NUMBER():
select t.score, t.user_id, t.info
from (
select *, row_number() over (partition by user_id order by score desc, info asc) rn
from test
) t
where t.rn = 1
See the demo.
Results:
| score | user_id | info |
| ----- | ------- | ---- |
| 1000 | 1 | 1 |
| 2001 | 2 | 1 |
If the combination of (user_id, info) is UNIQUE and NOT NULL (or PRIMARY KEY), then you can use a LIMIT 1 subquery in the WHERE clause:
SELECT t.*
FROM test t
WHERE (t.score, t.info) = (
SELECT t2.score, t2.info
FROM test t2
WHERE t2.user_id = t.user_id
ORDER BY t2.score DESC, t2.info ASC
LIMIT 1
)
ORDER BY t.score DESC, t.info ASC;
The result will be:
| score | user_id | info |
|-------|---------|------|
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
demo on sqlfiddle
SELECT info FROM test HAVING MAX(score) was used to keep the info field relevant with the row containing the MAX(score).
SELECT MAX(score) score, user_id, (SELECT info FROM test HAVING MAX(score)) AS info FROM test GROUP BY user_id ORDER BY score DESC;
I have a table with this data
+------+-----+————-----+
|Props |Score|Type |
+------+-----+-------- +
|1.EY | 30|Core
|2.FG | 29|Core
|2.YUE | 29|Core
|3.VB. | 28|Elective
|4.RX. | 67|Elective
|5.XE. | 89|Elective
|6.TF. | 60|Elective
|7.HK | 76|Elective
|8.ER | 58|Elective
I want to calculate the overall score by adding all Core scores plus any of the three best Elective scores. I cant seem to find my way around it.
Expected Results is 320: 3 Core Score + 3 best Elective
i.e(30+29+29)+(89+76+67)
Use UNION ALL for the 2 cases of scores and then sum over the returned scores:
select sum(t.score) totalscore
from (
select score from tablename
where type = 'Core'
union all
select t.score from (
select score from tablename
where type = 'Elective'
order by score desc
limit 3
) t
) t
See the demo.
Result:
| totalscore |
| ---------- |
| 320 |
You could rank the records by type within type partitions in an inner query and do a conditional sum in the outer query, like:
SELECT
SUM(CASE
WHEN type = 'Core' THEN score
WHEN type = 'Elective' AND rn <= 3 THEN score
ELSE 0
END) res
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY type ORDER BY score DESC) rn
FROM mytable t
) x
Demo on DB Fiddle with your sample data:
| res |
| --- |
| 320 |
I have a table software (id_software, software_name, category)
What SQL query to show only 2 software for each category ?
For example I want to get :
|id_software | software_name | category|
-+------------+---------------+---------+-
| 1 | Photoshop | 5 |
| 2 | illustrator | 5 |
| 3 | Firefox | 1 |
| 4 | I.E | 1 |
-+--------------------------------------+-
select * from
(select *
from table1 n
where
( select count(*)
from table1 m
where n.categorie = m.categorie
and n.id_software <= m.id_software) <= 2
order by n.id_software, n.id_software desc) as tn
Source: http://mindbuffer.wordpress.com/2013/07/09/mysql-get-the-top-2-rows-for-each-category-with-certain-condition/
You could use the row_number function for this.
SELECT *
FROM
(SELECT
Category,
id_software,
software_name,
[Nth_Software] = ROW_NUMBER() OVER (Partition by Category ORDER BY Id_software)
FROM
table
) T
WHERE
T.Nth_Software <=2
This gives you the first two software entries based on softwareId for every category.