Translate Lead/Lag function to MySQL - mysql

How do I translated the below codes so that it runs in MySQL. I am new to MySQL and finding it difficult to understand few things.
CODE 1:
SELECT t1.user_id,
t1.visit_month LEAD (t1.visit_month, 1) OVER (partition BY t1.user_id ORDER BY t1.user_id, t1.visit_month)
FROM (SELECT
user_id,
month(date_time) as visit_month
FROM
tbl_activity
group by 1, 2
ORDER BY 1 , 2) t1;
Desired Output for Code 1
CODE 2:
SELECT user_id,
visit_month,
lead,
lead — visit_month AS time_diff
FROM table3
CODE 3:
SELECT user_id,
Visit_month,
lag(visit_month, 1) over (partition BY user_id ORDER BY user_id, visit_month)
FROM table
CODE 4:
SELECT user_id,
visit_month,
lag,
visit_month — lag AS time_diff
FROM table2

You could express the lead() and lag() function via subquery
select user_id, month(date_time) as visit_month,
month(date_time)-(select month(date_time) from tbl_activity
where user_id = a.user_id and
month(date_time) < month(a.date_time)
order by month(date_time) desc LIMIT 1) as time_diff -- This could be re-express via lag() function
from tbl_activity a
group by user_id, month(date_time);
In above you would need to just specify < / > to express the lead() and lag() functionality in subquery and don't forget to use order by clause
EDIT (Lead) :
select user_id, month(date_time) as visit_month,
(select month(date_time) from tbl_activity
where user_id = a.user_id and
month(date_time) > month(a.date_time)
order by month(date_time) LIMIT 1) as lead -- This could be re-express via lead() function
from tbl_activity a
group by user_id, month(date_time);

Related

MYSQL row sequence with SUM() and ORDER BY

I try to order my rows by user's total point.
SUM and ORDER BY working correctly. But also i want to add sequence numbers of rows.
When I try to use #row_number I get some numbers but sequence is incorrect.
correct num column order should be 1,2,3,4 because I use order by total_point of sum of user's points.
How can I get correct sequence for num column?
SELECT
users.user_id,
users.user_hash,
(#row_number:=#row_number + 1) AS num,
sum(total_point) as total_point
FROM (SELECT #row_number:=0) AS t,user_stats
LEFT JOIN users on users.user_id = user_stats.stats_user_id
WHERE create_date BETWEEN "2020-04-01 00:00:00" AND "2020-04-30 23:59:59"
GROUP BY stats_user_id
ORDER BY total_point DESC
v: mysql 5.7
You must use the with total sorted rows and give them a number
SELECT
user_id,
user_hash,
users.user_nick,
(#row_number:=#row_number + 1) AS num,
total_point
FROM
(SELECT
users.user_id,
users.user_hash,
users.user_nick,
SUM(total_point) AS total_point
FROM
user_stats
LEFT JOIN users ON users.user_id = user_stats.stats_user_id
WHERE
create_date BETWEEN '2020-04-01 00:00:00' AND '2020-04-30 23:59:59'
GROUP BY stats_user_id
ORDER BY total_point DESC) t1,
(SELECT #row_number:=0) AS t
ORDER BY num ASC;

Group by date and take the last one

This is my table :
What I'm trying to do, is to take the last disponibility of a user, by caserne. Example, I should have this result :
id id_user id_caserne id_dispo created_at
31 21 12 1 2019-10-24 01:21:46
33 21 13 1 2019-10-23 20:17:21
I've tried this sql, but it does not seems to work all the times :
SELECT * FROM
( SELECT id, id_dispo, id_user, id_caserne, MAX(created_at)
FROM disponibilites GROUP BY id_user, id_caserne, id_dispo
ORDER BY created_at desc ) AS sub
GROUP BY id_user, id_caserne
What am I doing wrong ?
I would simply use filtering in the where clause using a correlated subquery:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user
);
EDIT:
Based on your comments:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user and
d2.id_caserne = d.id_caserne
where date(d2.created_at) = date(d.created_at)
);
You can use a correlated subquery, as demonstrated by Gordon Linoff, or a window function if your RDBMS supports it:
select * from (
select
t.*,
rank() over(partition by id_caserne, id_user order by created_at desc) rn
from disponibilites t
) x
where rn = 1
Another option is to use a correlated subquery without aggregation, only with a sort and limit:
select *
from mytable t
where created_at = (
select created_at
from mytable t1
where t1.id_user = t.id_user and t1.id_caserne = t.id_caserne
order by created_at desc
limit 1
)
With an index on (id_user, id_caserne, created_at), this should be a very efficient option.
you can join your max(created_date) to your original table
select t1.* from disponibilites t1
inner join
(select max(created_at), id_caserne, id
from disponibilites
group by id_caserne, id) t2
on t2.id = t1.id

SQL get Max Date

I need to have the last price for each product for each client . I am not really good with SQL and I don't understand how I can do it.
Data :
What I want :
It is possible to have this data with a SQL request ?
Use window function ROW_NUMBER(), if available in your RDBMS:
SELECT product, price, date, client
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY product, client ORDER BY date DESC) rn
FROM mytable t
) x
WHERE rn = 1
In MySQL < 8.0:
SELECT product, price, date, client
FROM mytable t
WHERE NOT EXISTS (
SELECT 1
FROM mytable t1
WHERE t1.client = t.client AND t1.product = t.product AND t1.date > t.date
)
One option could be a correlated subquery
SELECT product, price, date, client
FROM tablename a where date =
(select max(date) from tablename b where a.product=b.product)

Get maximum value from two values

I have a table which gives the no of rides by a rider at each stand point. I need to find the stand for each rider for which he has the maximum rides.
My first result is in this format: 1
I require my final result like this: 2
I'm currently using this query, but I know it can be done in a better manner. Any suggestions would be helpful.
select c.rider_id, c.end_stand, b.max_rides
from
(select rider_id, max(rides) as max_rides
from
(select rider_id, end_stand, count(id) as rides
from ride where end_stand is not null
group by 1,2) a
group by 1
order by 2 desc, 1) b
join
(select rider_id, end_stand, count(id) as rides
from ride where end_stand is not null
group by 1,2) c
on c.rider_id = b.rider_id and c.rides = b.max_rides
order by 3 desc, 2,1
Before window functions, one method is a correlated subquery in the having clause:
select rider_id, end_stand, count(*) as rides
from ride r
where end_stand is not null
group by rider_id, end_stand
having count(*) = (select count(*)
from ride r2
where r2.end_stand is not null and
r2.rider_id = r.rider_id
group by r2.rider_id, r2.end_stand
order by count(*) desc
limit 1
);
With window functions, this is, of course, much simpler:
select *
from (select rider_id, end_stand, count(*) as rides
rank() over (partition by rider_id order by count(*) desc) as seqnum
from ride r
where end_stand is not null
group by rider_id, end_stand
) r
where seqnum = 1;
Both these will return duplicates, if there are ties for the max. The second version is easy to fix, if you want only one row: use row_number() instead of rank().

How to write sql query to get items from range

I would like to get values without the smallest and the biggest ones, so without entry with 2 and 29 in column NumberOfRepeating.
My query is:
SELECT Note, COUNT(*) as 'NumberOfRepeating'
WHERE COUNT(*) <> MAX(COUNT(*))AND COUNT(*) <> MIN(COUNT(*))
FROM Note GROUP BY Note;
SELECT Note, COUNT(*) as 'NumberOfRepeating'
FROM Notes
GROUP BY Note
HAVING count(*) <
(
SELECT max(t.maxi)
FROM (select
Note, COUNT(Note) maxi FROM Notes
GROUP BY Note
) as t
)
AND
count(*) >
(
SELECT min(t.min)
FROM (select
Note, COUNT(Note) min FROM Notes
GROUP BY Note
) as t
)
try this code.
One method would use order by and limit, twice:
select t.*
from (select t.*
from t
order by NumberOfRepeating asc
limit 99999999 offset 1
) t
order by NumberOfRepeating desc
limit 99999999 offset 1;
Try this code,
Select * from Note where NumberOfRepeating < (select MAX(NumberOfRepeating) from Note ) AND NumberOfRepeating > (select MIN(NumberOfRepeating) from Note );
Here in the code, as in your table Note is the name of the table, and NumberOfRepeating is the column name, as in your table.
Try this. It should work
SELECT *
FROM ( SELECT Note, COUNT(*) as 'NumberOfRepeating'
FROM Notes
GROUP BY Note
ORDER BY NumberOfRepeating DESC
LIMIT 1, 2147483647
) T1
ORDER BY T1.NumberOfRepeating
LIMIT 1, 2147483647