Select previous record from row - mysql

Example
id |u_id
1 | 1
2 | 1
3 | 2
4 | 1
5 | 2
6 | 3
I know, the id 4 has u_id of 1, but I want to select the last row having u_id 1 before that with id 4 i.e. I want to select the row with the id 2.
Note that I don't know this id.
How can I achieve that?
This is what the result should look like:
id |u_id
2 | 1
4 | 1

select * from table where uid=1 order by id desc limit 2

This may help you.
SELECT * FROM ( SELECT * FROM yourTable WHERE u_id=1 ORDER BY id DESC LIMIT 2) AS tab ORDER BY tab.id ASC

Finally figured out the correct sql query for it.
SELECT * FROM table WHERE u_id = 1 AND (id = 4 OR id < 4) ORDER BY id DESC LIMIT 0,2

If you are using a fairly recent version of MySQL, what you need is the LAG() windowed function:
SELECT id,
u_id,
LAG(id) OVER W AS prev_id
FROM MyTable
WINDOW w AS (PARTITION BY u_id ORDER BY id)
ORDER BY id, u_id;
It will produce a result like this:
id |u_id |prev_id
1 | 1 | null
2 | 1 | 1
3 | 2 | null
4 | 1 | 2
5 | 2 | 3
6 | 3 | null
You can play with the query here.

As your title states:
select previous record from row
The following gives you every row that comes prior to a 2, and will work with more than just the data you've shown in your example:
SELECT *
FROM example
WHERE id IN (
SELECT id - 1
FROM example
WHERE u_id = 2);
[SEE DEMO HERE]

Related

How can I create a column with incremented values from a column with cumulated values in MySQL?

Table1 contains a column with cumulated values (all positive integers):
id ValuesCum
1 5
2 8
3 20
I would like to write a statement that returns an extra column with the incremented values for each row. The output should read something like:
id ValuesCum ValuesInc
1 5 (5)
2 8 3
3 20 12
Does anyone have a solution for this?
If you are running MySQL 8.0, you can use window function lag() for this:
select
t.*,
ValuesCum - lag(ValuesCum, 1, 0) over(order by id) ValuesInc
from mytable t
In earlier versions, an alternative is a correlated subquery:
select
t.*,
ValuesCum - (
select coalesce(max(t1.ValuesCum), 0)
from mytable t1
where t1.id < t.id
) ValuesInc
from mytable t
You can use a correlated subquery to get the value of ValuesCum of the previous id:
select t.*,
t.ValuesCum -
coalesce((select ValuesCum from tablename where id < t.id order by id desc limit 1), 0) ValuesInc
from tablename t
See the demo.
Results:
| id | ValuesCum | ValuesInc |
| --- | --------- | --------- |
| 1 | 5 | 5 |
| 2 | 8 | 3 |
| 3 | 20 | 12 |

What optimal selection statements are possible to select previous and next rows (in one statement)?

How to select previous AND next rows from an ordered table, ordered by an order column?
This is a simple example of such a table (e.g. test_table):
+--------+-----------+----------+
| id | name | order |
+--------+-----------+----------+
| 126 | Test 0 | 0 |
+--------+-----------+----------+
| 73 | Test 1 | 1 | >
+--------+-----------+----------+
| 801 | Test 5 | 5 | <<<
+--------+-----------+----------+
| 3 | Test 8 | 8 | >
+--------+-----------+----------+
| 45 | Test 12 | 12 |
+--------+-----------+----------+
This is an example statement, that makes it possible to do what I need (in this example I have the order-value 5, and I need the previous and next rows by order):
SELECT * FROM
(
SELECT * FROM test_table
WHERE test_table.order < 5
ORDER BY test_table.order DESC LIMIT 1
) AS a
UNION
SELECT * FROM
(
SELECT * FROM test_table
WHERE test_table.order > 5
ORDER BY test_table.order LIMIT 1
) AS b
However, I think it is too complicated. Is there another way to do it, using less selects (and/or without a union)? In short: are there more performant/efficient and optimal statements/algorithms or some best practices?
To be clearer, I expect the following result set:
+--------+-----------+----------+
| id | name | order |
+--------+-----------+----------+
| 73 | Test 1 | 1 |
+--------+-----------+----------+
| 3 | Test 8 | 8 |
+--------+-----------+----------+
P.S. Please, do not use any procedures or custom functions. Assume that there are no appropriate administrator rights for it.
As I see, your solution is already optimal. I would though write it a bit shorter and use UNION ALL instead of UNION (which is a shortcut for UNION DISTINCT):
(
SELECT * FROM test_table
WHERE test_table.order < 5
ORDER BY test_table.order DESC LIMIT 1
) UNION ALL (
SELECT * FROM test_table
WHERE test_table.order > 5
ORDER BY test_table.order LIMIT 1
)
Given an index on the order column, it should also have the best possible performance.
One alternative way to do this would be to use the LEAD and LAG analytic functions:
WITH cte1 AS (
SELECT id,
LAG(id) OVER (ORDER BY `order`) id_lag,
LEAD(id) OVER (ORDER BY `order`) id_lead
FROM test_table
),
cte2 AS (
SELECT * FROM cte1 WHERE `order` = 5
)
SELECT id, name, `order` FROM test_table WHERE id = (SELECT id_lag FROM cte2)
UNION ALL
SELECT id, name, `order` FROM test_table WHERE id = (SELECT id_lead FROM cte2);

How to get n rows of records of given m ids in Mysql by id desc

Suppose i have a table like this
id | user_id | rating
1 | 500 | 5
2 | 501 | 3
3 | 500 | 5
4 | 502 | 4
5 | 502 | 1
How can i write a mysql query to find the last 10 records for each id of given three ids (500, 501,502) by id desc
assuming your id is an auto increment column and the three ids (500, 501,502) are for user_id
then you could use
select *
from my_table
where user_id in (500, 501,502)
order by id desc
limit 10
Being a bit lazy here's a way to get the last 2 per user_id by using a variable to allocate a row number joined to the max occurrences per user
DROP TABLE IF EXISTS T;
CREATE TABLE T
(ID INT AUTO_INCREMENT PRIMARY KEY, USER_ID INT, RATING INT);
INSERT INTO T (USER_ID,RATING) VALUES
(500,1),
(502,1),
(500,2),(500,3),
(502,2),
(600,1);
SELECT U.*
FROM
(
SELECT S.ID,S.USER_ID,S.RATING,
T.MAXROWS
FROM
(
SELECT ID,USER_ID,RATING,
IF(USER_ID <> #P,#R:=1,#R:=#R+1) RN,
#P:=USER_ID
FROM T
ORDER BY USER_ID ASC,ID ASC
) S
JOIN
(SELECT USER_ID,COUNT(*) MAXROWS FROM T GROUP BY USER_ID) T ON T.USER_ID = S.USER_ID
) U
WHERE U.RATING > U.MAXROWS - 2 AND U.USER_ID IN(500,502);
+----+---------+--------+---------+
| ID | USER_ID | RATING | MAXROWS |
+----+---------+--------+---------+
| 3 | 500 | 2 | 3 |
| 4 | 500 | 3 | 3 |
| 2 | 502 | 1 | 2 |
| 5 | 502 | 2 | 2 |
+----+---------+--------+---------+
4 rows in set (0.00 sec)
You can use UNION ALL operator to get 10 result of each id
(select * from test
where user_id =500
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =501
order by id desc
limit 10)
UNION ALL
(select * from test
where user_id =502
order by id desc
limit 10)
order by id desc;
Check here for DEMO
NOTE: Reviewer are welcome to enhance or improve the query.

How to select last 4 data which start from second last row using sql

I have table named posts with 3 columns which are id, details, date which contains the following data in ascending order:
+----+----------+-------+
| id | details | date |
+----+----------+-------+
| 1 | details1 | date1 |
| 2 | details2 | date2 |
| 3 | details3 | date3 |
| 4 | details4 | date4 |
+----+----------+-------+
I want to select data in descending order but I want to leave the first row details, like I want to leave row 4th id details, details4, date4, but then I want to select data from id 3 to 2 in, like order by id desc limit 2 but leave the first row from last
You can use the query with ORDER BY DESC LIMIT 1, n
That way, n is the amount of rows you want to fetch, and you're skipping the first row of the result by using LIMIT 1, .
WITH a AS (
SELECT 1 i
UNION ALL
SELECT 2 i
UNION ALL
SELECT 3 i
UNION ALL
SELECT 4 i
)
, b as (
SELECT TOP 1 i FROM a ORDER BY i DESC
)
SELECT *
FROM A
EXCEPT
SELECT * FROM B
ORDER BY i DESC

Get Records in Order of conditions in where in clause

How do you get the result from database in the order of conditions in "where in" clause in mysql?
e.g:
table
id name
1 | riz
2 | irf
3 | far
4 | zab
5 | yas
i want my data in the order of conditions put in where in clause...?
select * from foo where id in (2,3,4,1,5);
eg.
id name
2 | irf
3 | far
4 | zab
1 | riz
5 | yas
You can Use :
select * from foo where id IN(2,3,4,1,5) order by FIND_IN_SET(id, '2,3,4,1,5');
It will work for you.
SELECT *
FROM foo
where id in (2,3,4,1,5);
ORDER BY FIND_IN_SET( id, '2,3,4,1,5' )
Here is the reference ORDER BY FIND_IN_SET()
You can provide each id as a different sorting case.
SELECT *
FROM foo
WHERE
id IN(2,3,4,1,5)
ORDER BY
id = 2 DESC,
id = 3 DESC,
id = 4 DESC,
id = 1 DESC,
id = 5 DESC