SQL query to get multiple rows with different timestamps - mysql

I am attempting get some data with their latest timestamps. sets of data have different timestamps which i need to get using their timestamps. for example:
id
testcase_id
user
timestamp
1
2
abbc
2013-01-13 15:00:00
2
2
pbbb
2013-01-13 15:05:00
3
4
dddd
2013-01-13 15:05:00
4
4
abbc
2010-01-10 16:04:00
For abbc need to get row 1 which have latest timestamp(2013-01-13 15:00:00) and i need row 2 with pbbb (2013-01-13 15:05:00) and row 3 dddd (2013-01-13 15:05:00). I have this timestamp data for each user in backend just need a proper query to fetch all those rows using timestamps.
Note: There could be multiple users with same timestamps, ex - if 10 users have same timestamp as abbc i will require all those users.

JPQL is picky about the syntax it allows. You may try using correlated subqueries here:
select e
from your_entity e
where e.timestamp = (select max(f.timestamp)
from your_entity f where f.user = e.user)

Try CTE + analytical function row_number()
with t1 as (
select id, testcase_id, user, timestamp,
row_number() over (partition by user order by timestamp desc) rno
from your_table)
select select id, testcase_id, user, timestamp
from t1
where rno = 1

Related

How to get records based on the previous related ones

I'm trying to get records taking account of the just related one. In this case we have some users and we need subscriptions that their just previous ones where created in 2018.
We have this subscriptions table:
id
user_id
created_at
1
1
2016-01-01
2
1
2017-01-01
3
1
2018-01-01
4
1
2019-01-01
5
1
2020-01-01
6
2
2018-01-01
7
2
2019-01-01
I am using a self-join:
SELECT `subscriptions`.`id`
FROM `subscriptions`
LEFT JOIN subscriptions as previous
ON subscriptions.user_id = previous.user_id AND subscriptions.created_at > previous.created_at
WHERE `previous`.`created_at` BETWEEN '2018-01-01' AND '2018-12-31';
It returns 4,5,7 but I only want the just following ones 4,7
SQLFiddle
On MySQL 8+, we can use the LAG() analytic function here:
WITH cte AS (
SELECT *, LAG(created_at) OVER (PARTITION BY user_id
ORDER BY created_at) lag_created_at
FROM subscriptions
)
SELECT id, user_id, created_at
FROM cte
WHERE YEAR(lag_created_at) = 2018;

Get original RANK() value based on row create date

Using MariaDB and trying to see if I can get pull original rankings for each row of a table based on the create date.
For example, imagine a scores table that has different scores for different users and categories (lower score is better in this case)
id
leaderboardId
userId
score
submittedAt ↓
rankAtSubmit
9
15
555
50.5
2022-01-20 01:00:00
2
8
15
999
58.0
2022-01-19 01:00:00
3
7
15
999
59.1
2022-01-15 01:00:00
3
6
15
123
49.0
2022-01-12 01:00:00
1
5
15
222
51.0
2022-01-10 01:00:00
1
4
14
222
87.0
2022-01-09 01:00:00
1
5
15
555
51.0
2022-01-04 01:00:00
1
The "rankAtSubmit" column is what I'm trying to generate here if possible.
I want to take the best/smallest score of each user+leaderboard and determine what the rank of that score was when it was submitted.
My attempt at this failed because in MySQL you cannot reference outer level columns more than 1 level deep in a subquery resulting in an error trying to reference t.submittedAt in the following query:
SELECT *, (
SELECT ranking FROM (
SELECT id, RANK() OVER (PARTITION BY leaderboardId ORDER BY score ASC) ranking
FROM scores x
WHERE x.submittedAt <= t.submittedAt
GROUP BY userId, leaderboardId
) ranks
WHERE ranks.id = t.id
) rankAtSubmit
FROM scores t
Instead of using RANK(), I was able to accomplish this by with a single subquery that counts the number of users that have a score that is lower than and submitted before the given score.
SELECT id, userId, score, leaderboardId, submittedAt,
(
SELECT COUNT(DISTINCT userId) + 1
FROM scores t2
WHERE t2.userId = t.userId AND
t2.leaderboardId = t.leaderboardId AND
t2.score < t.score AND
t2.submittedAt <= t.submittedAt
) AS rankAtSubmit
FROM scores t
What I understand from your question is you want to know the minimum and maximum rank of each user.
Here is the code
SELECT userId, leaderboardId, score, min(rankAtSubmit),max(rankAtSubmit)
FROM scores
group BY userId,
leaderboardId,
scorescode here

Interpolate Multiseries Data In SQL

I have a system that stores the data only when they are changed. So, the dataset looks like below.
data_type_id
data_value
inserted_at
2
240
2022-01-19 17:20:52
1
30
2022-01-19 17:20:47
2
239
2022-01-19 17:20:42
1
29
2022-01-19 17:20:42
My data frequency is every 5 seconds. So, whether there's any timestamp or not I need to get the result by assuming in this 5th-second data value the same as the previous value.
As I am storing the data that are only changed, indeed the dataset should be like below.
data_type_id
data_value
inserted_at
2
240
2022-01-19 17:20:52
1
30
2022-01-19 17:20:52
2
239
2022-01-19 17:20:47
1
30
2022-01-19 17:20:47
2
239
2022-01-19 17:20:42
1
29
2022-01-19 17:20:42
I don't want to insert into my table, I just want to retrieve the data like this on the SELECT statement.
Is there any way I can create this query?
PS. I have many data_types hence when the OP makes a query, it usually gets around a million rows.
EDIT:
Information about server Server version: 10.3.27-MariaDB-0+deb10u1 Debian 10
The User is going to determine the SELECT DateTime. So, there's no certain between time.
As #Akina mentioned, sometimes there're some gaps between the inserted_at. The difference might be ~4seconds or ~6seconds instead of a certain 5seconds. Since it's not going to happen so frequently, It is okay to generate by ignoring this fact.
With the help of a query that gets you all the combinations of data_type_id and the 5-second moments you need, you can achieve the result you need using a subquery that gets you the closest data_value:
with recursive u as
(select '2022-01-19 17:20:42' as d
union all
select DATE_ADD(d, interval 5 second) from u
where d < '2022-01-19 17:20:52'),
v as
(select * from u cross join (select distinct data_type_id from table_name) t)
select v.data_type_id,
(select data_value from table_name where inserted_at <= d and data_type_id = v.data_type_id
order by inserted_at desc limit 1) as data_value,
d as inserted_at
from v
Fiddle
You can replace the recursive CTE with any query that gets you all the 5-second moments you need.
WITH RECURSIVE
cte1 AS ( SELECT #start_datetime dt
UNION ALL
SELECT dt + INTERVAL 5 SECOND FROM cte1 WHERE dt < #end_datetime),
cte2 AS ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY test.data_type_id, cte1.dt
ORDER BY test.inserted_at DESC) rn
FROM cte1
LEFT JOIN test ON FIND_IN_SET(test.data_type_id, #data_type_ids)
AND cte1.dt >= test.inserted_at )
SELECT *
FROM cte2
WHERE rn = 1
https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=380ad334de0c980a0ddf1b49bb6fa38e

SQL- Get count of rows in a list of date ranges

I have a table that has some data on active users. It displays what version the user was on and the date they were active. Let's call this table active_users:
Version Time
-------------------------
1 '2018-03-12'
1 '2018-03-01'
1 '2018-03-06'
1 '2018-03-09'
2 '2018-01-02'
2 '2018-01-04'
2 '2018-01-05'
2 '2018-01-11'
I also have a table that returns the date a version was released and a week after the release date. Let's call this table release_dates:
Version Release_Date Week_After_Release_Date
------------------------------------------------
1 '2018-03-01' '2018-03-08'
2 '2018-01-02' '2018-01-09'
Now what I want to do in one query is get a count of the number of users who were active between the dates listed in the row from the second table.
The result would look like this:
Version Count
------------------------------------------------
1 2
2 3
as there are two rows in active_users that are version 1 and have dates between 2018-03-01 and 2018-03-08 and three rows that are version 2 and have dates between 2018-01-02 and 2018-01-09.
This would be easy to do with a for-loop because I could just iterate over each row in release_dates, do a query in active_users with those parameters and concatenate all the results at the end. Unfortunately I am working with the restraint of doing a single query, do we know if this is possible? I am using MYSQL as my database.
One way to get the result you want is to combine an aggregate operation with a conditional selection and only count the rows that matches the condition:
select
au.version,
sum(if(au.time between rd.release_date and rd.week_after_release_date,1,0)) as count
from active_users au
join release_dates rd on au.version = rd.version
group by au.version
If you prefer to use the count function the third row could be written as:
count(if(au.time between rd.release_date and rd.week_after_release_date, au.time, null)) as count
If you want it possibly slightly more portable you could use case instead of if
Sample SQL Fiddle
One way is a correlated subquery:
select rd.*,
(select count(*)
from active_users au
where au.time >= rd.release_date and
au.time < Week_After_Release_Date + interval 1 day
)
from release_dates rd;
Note that I do not use between on the dates, just in case the dates have a time component.
First of all, you want to know which users are active during each time interval in the second table by outer join all records in the first table and the second table:
SELECT
au.Version, au.Time
FROM
active_users AS au, release_dates AS rd
WHERE
au.Version = rd.Version AND au.Time >= rd.Release_Date AND au.Time < rd.Week_After_Release_Date
You can get the following result:
Version | Time
---------+------------
1 | 2018-03-01
1 | 2018-03-06
2 | 2018-01-02
2 | 2018-01-04
2 | 2018-01-05
Based on this result, you can group by Version and get your final stats:
SELECT
Version, COUNT(*)
FROM
(
SELECT
au.Version
FROM
active_users AS au, release_dates AS rd
WHERE
au.Version = rd.Version AND au.Time >= rd.Release_Date AND au.Time < rd.Week_After_Release_Date
) v
GROUP BY
Version

How may i group this data in mysql

I Have a table called
CountryVisits
Visitor Date
4 20/12/12 14:27:7
5 20/12/12 15:45:11
6 20/12/12 16:36:51
12 21/12/12 11:17:2
145 21/12/12 12:37:57
445 21/12/12 13:35:15
435 21/12/12 14:34:35
I want to have a count of all visitors per Date not time :
So i expect :
VisitorCnt Date
3 20/12/12
4 21/12/12
I tried :
SELECT COUNT(DATE(Date)), DATE(Date) from CountryVisits GROUP BY Date ;
But i don't have the desired results.
When you write GROUP BY Date it's grouping by the Date column in the table, which includes times. If you want to group by just the day, you need to assign an alias and group by that:
SELECT COUNT(*), DATE(Date) AS VisitedOn
FROM CountryVisits
GROUP BY VisitedOn