Select the last record in MySQL in WHERE condition - mysql

I have a table that store ticket and statue relation
ticketstatus_Id ticket_Id status_Id
===================================
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2 *
6 3 1
7 4 1
8 3 2 *
I want to select rows that last status_Id equal 2 ,Rows are marked in table.
I think I have to use GROUP BY on column ticket_Id but it return with first status_Id.

This problem is a good candidate for ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ticket_Id ORDER BY ticketstatus_Id DESC) rn
FROM yourTable
)
SELECT ticketstatus_Id, ticket_Id, status_Id
FROM cte
WHERE rn = 1 AND status_Id = 2;
The above logic finds all latest rows for each ticket_Id, as ordered by the ticketstatus_Id, whose status_Id values also happens to be 2.

All records with status_Id= '2'
Select * from TABLENAME where status_Id = '2'
Last record with status_Id= '2'
Select * from TABLENAME where status_Id = '2' order by ticketstatus_Id desc limit 1

SELECT * from TABLENAME where status_Id = '2' ORDER BY ticketstatus_Id DESC LIMIT 1;

You can do this with group by and a having clause:
select ticket_Id, max(ticketstatus_Id)
from ticketstatuses
group by ticket_id
having max(ticketstatus_Id) = max(case when status_id = 2 then ticketstatus_Id end);
It would be interesting to know if this has better performance than the row_number() version. However, for performance, this is probably best:
select ts.*
from ticketstatuses ts
where ts.ticketstatus_Id = (select max(ts2.ticketstatus_Id)
from ticketstatus ts2
where ts2.ticket_id = ts.ticket_id
) and
ts.status_id = 2;
This can take advantage of an index on (ticket_id, ticketstatus_id).

Related

MySQL Query to select from table specific rows

I have a table which looks has the following values:
product_id
custom_id
custom_value
1
10
A
1
9
V
2
10
B
3
3
Q
I am looking for a mysql query to get all values from product_id once and select the row which has custom_id = "10" in case it is available. Nevertheless in case custom_id = 10 is not available for a product_id I would still like to return the product_id but also only once.
So the result I am looking for is
product_id
custom_id
custom_value
1
10
A
2
10
B
3
NULL
NULL
Could please someone direct me in the right direction.
select product_id, custom_id, custom_value from table where custom_id = 10
does of course only return the values for product_id "1" and "2"
You can select the first set of rows, then union by a distinct of all the other product id's
select product_id, custom_id, custom_value from table where custom_id = 10
union
select distinct product_id, NULL as custom_id, NULL as custom_value where custom_id <> 10
You can first generate a ROW_NUMBER to get the first element for each "product_id", then transform to NULL values for which "product_id" does not match your value 10, using the IF function.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY custom_id = 10 DESC) AS rn
FROM tab
)
SELECT product_id,
IF(custom_id=10, custom_id, NULL) AS custom_id,
IF(custom_id=10, custom_value, NULL) AS custom_value
FROM cte
WHERE rn = 1
Check the demo here.

SQL add same contents

How can I delete all columns that are duplicate and don't have the biggest "amount". I have the following table:
ID TIME AMOUNT
-----------------------------------
1 x 5
2 y 1
2 y 3
3 z 1
3 z 2
3 z 3
But I want it to be like this, so that only the column which has the biggest number "survives":
ID TIME AMOUNT
------------------------------------
1 x 5
2 y 3
3 z 3
How can I do this?
You can get the max amount per id and time and then get the rows matching:
select t.Id, t.Time, t.amount
from myTable t
inner join
(select Id, time, max(amount) as amt
from myTable
group by Id, Time) tmp on t.id = tmp.id and
t.time = tmp.time and
t.amount = tmp.amt
DbFiddle demo
EDIT: You may want to add DISTINCT depending on your needs.
One other approach using a CTE
with del as (
select *,
First_Value(amount) over(partition by id order by amount desc) maxamount
from t
)
delete from t
using t join del on t.id = del.id and t.amount < maxamount;
WITH cte AS
(
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY TIME ORDER BY AMOUNT DESC) AS ROWNUM
FROM
MyTable
)
DELETE MyTable
FROM MyTable
JOIN cte USING (ID)
WHERE ROWNUM > 1;
WITH syntax requires MySQL 8.0.
I think some of the answers here are overly complicated.
delete t
from yourtable t
join yourtable t2 on t.id = t2.id
and t.time = t2.time
and t2.amount > t.amount

MYSQL - Select Only Records Where Previous Record Column Data Differs

Hoping this is possible with just sql. I have a query that returns a data set with time_stamp and hash_index columns. Basically something to the effect of:
1 1583365548 6ff11ad5536f28d66098f6d74f97d877
2 1583365554 6ff11ad5536f28d66098f6d74f97d877
3 1583365556 6ff11ad5536f28d66098f6d74f97d877
4 1583365562 a2e99acb2540d49955ef93fb2684ac25
5 1583365571 a2e99acb2540d49955ef93fb2684ac25
6 1583365572 a2e99acb2540d49955ef93fb2684ac25
7 1583365574 a2e99acb2540d49955ef93fb2684ac25
8 1583365578 a2e99acb2540d49955ef93fb2684ac25
9 1583365580 a2e99acb2540d49955ef93fb2684ac25
What I want to do is further filter this query to only include the record if the hash_index differs from the previous record. Is this something I can do without having to dump it into PHP and loop through it?
My current query is below:
SELECT
(#cnt:=#cnt + 1) AS row_number,
time_stamp,
MD5(GROUP_CONCAT(CONCAT(user_state_name,
option_id,
option_code,
item_id,
item_code))) AS hash_index
FROM
user_state
WHERE
user_id = 2
GROUP BY
time_stamp;
What is with a query like this:
SELECT t1.*
FROM user_state t1
LEFT JOIN user_state t2 ON t1.id-1 = t2.id
WHERE t1.hash_index <> t2.hash_index;
If you want to filter out adjacent duplicates, I would just use lag() and dispense with hashing and aggregation:
SELECT us.*
FROM (SELECT us.*,
LAG(time_stamp) OVER (PARTITION BY user_id ORDER BY time_stamp) as prev_ts,
LAG(time_stamp) OVER (PARTITION BY user_id, user_state_nae, option_id, option_code, item_id, item_code ORDER BY time_stamp) as prev_ts_values
FROM user_state us
WHERE user_id = 2
) t
WHERE prev_ts_values is null or prev_ts_values <> prev_ts;
You can select whichever rows you want.

MySQL select rows until fixed number of condition is reached

I have this table
id fruit
---------
1 apple
2 banana <--
3 apple
4 apple
5 apple
6 apple
7 banana <----
8 apple
9 banana
10 apple
And I want to select rows until 2 bananas are found, like
SELECT id FROM table_fruit UNTIL number_of_bananas = 2
So the result would be 1,2,3,4,5,6,7
How could I achieve this?
thanks
I wish I could give credits to all of you who answered my question. I'v tested all of them, and they all work perfectly (got the expected result).
Though answers of Devart and ypercube seem a little bit complex and difficult for me to understand.
And since AnandPhadke was the first one provided a working solution, I'll choose his answer as accepted.
You guys are awesome, thanks!
Try this query -
SELECT id, fruit FROM (
SELECT
b.*, #b:=IF(b.fruit = 'banana', 1, 0) + #b AS banana_number
FROM
bananas b,
(SELECT #b := 0) t
ORDER BY id) t2
WHERE
banana_number < 2 OR banana_number = 2 AND fruit = 'banana'
SQLFiddle demo
select * from tables where id <=
(
select id from (
select id from tables where fruit='banana'
order by id limit 2) a order by id desc limit 1
)
SQLFIDDLE DEMO
#Devart's answer is perfect but it's an alternative option to we can use:
SELECT * FROM table_fruit WHERE id <=
(
SELECT id FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
ORDER BY ID DESC LIMIT 1
);
Or using MAX
SELECT * FROM table_fruit WHERE id <=
(
SELECT MAX(id) FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
);
See this SQLFiddle
select * from table_fruit where id <=
(
select max(id) from
(select id from table_fruit where fruit='banana' order by id limit 2) t
)
If there are less than 2 rows with 'banana', this will return all rows of the table:
SELECT t.*
FROM table_fruit AS t
JOIN
( SELECT MAX(id) AS id
FROM
( SELECT id
FROM table_fruit
WHERE fruit = 'banana'
ORDER BY id
LIMIT 1 OFFSET 1
) AS lim2
) AS lim
ON t.id <= lim.id
OR lim.id IS NULL ;

Select the 3 most recent records where the values of one column are distinct

I have the following table:
id time text otheridentifier
-------------------------------------------
1 6 apple 4
2 7 orange 4
3 8 banana 3
4 9 pear 3
5 10 grape 2
What I want to do is select the 3 most recent records (by time desc), whose otheridentifiers are distinct. So in this case, the result would be id's: 5, 4, and 2.
id = 3 would be skipped because there's a more recent record with the same otheridentifier field.
Here's what I tried to do:
SELECT * FROM `table` GROUP BY (`otheridentifier`) ORDER BY `time` DESC LIMIT 3
However, I end up getting rows of id = 5, 3, and 1 instead of 5, 4, 2 as expected.
Can someone tell me why this query wouldn't return what I expected? I tried changing the ORDER BY to ASC but this simply rearranges the returned rows to 1, 3, 5.
It doesn't return what you expect because grouping happens before ordering, as reflected by the position of the clauses in the SQL statement. You're unfortunately going to have to get fancier to get the rows you want. Try this:
SELECT *
FROM `table`
WHERE `id` = (
SELECT `id`
FROM `table` as `alt`
WHERE `alt`.`otheridentifier` = `table`.`otheridentifier`
ORDER BY `time` DESC
LIMIT 1
)
ORDER BY `time` DESC
LIMIT 3
You could join the table on itself to filter the last entry per otheridentifier, and then take the top 3 rows of that:
SELECT last.*
FROM `table` last
LEFT JOIN `table` prev
ON prev.`otheridentifier` = last.`otheridentifier`
AND prev.`time` < last.`time`
WHERE prev.`id` is null
ORDER BY last.`time` DESC
LIMIT 3
I had a similar requirement, but I had more advanced selection criteria. Using some of the other answers I couldn't get exactly what I needed, but I found you can still do a GROUP BY after and ORDER BY like this:
SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t
GROUP BY t.otheridentifier
SELECT * FROM table t1
WHERE t1.time =
(SELECT MAX(time) FROM table t2
WHERE t2.otheridentifier = t1.otheridentifier)
Andomar's answer is probably best in that it doesn't use a subquery.
An alternative approach:
select *
from `table` t1
where t1.`time` in (
select max(s2.`time`)
from `table` t2
group by t2.otheridentifier
)
You can use this query to get correct answer:
SELECT * FROM
(SELECT * FROM `table` order by time DESC)
t group by otheridentifier
what about
SELECT *, max(time) FROM `table` group by otheridentifier
This also:
SELECT * FROM
OrigTable T INNER JOIN
(
SELECT otheridentifier,max(time) AS duration
FROM T
GROUP BY otheridentifier) S
ON S.duration = T.time AND S.otheridentifier = T.otheridentifier.