Retrieve the second latest record with same id - mysql

From the following four records, I want to select the OwnerId of second-latest record
ItemId OwnerId Date
11477 20981 2013-05-13
11477 1 2013-05-21
11477 21086 2013-05-22 #this is the one I'm talking about
11477 3868 2013-05-24
How to go about it?

This needs ItemID to be specified,
SELECT *
FROM TableName
WHERE ItemID = '11477'
ORDER BY DATE DESC
LIMIT 1,1
SQLFiddle Demo
However, if you don't want to specify the ItemID, and you want to get all second latest record for every ItemID, you can use a correlated subquery to generate a sequence number for every ItemID based on lastest DATE,
SELECT ItemId, OwnerID, Date
FROM
(
SELECT A.ItemId,
A.OwnerId,
A.Date,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.ItemId = a.ItemId AND
c.Date >= a.Date) AS RowNumber
FROM TableName a
) x
WHERE RowNumber = 2
SQLFiddle Demo

select ownerid
from your_table
order by date desc
limit 1, 1

I think you can just to ORDER BY date descending, which will give you an order from newer to older, then LIMIT 1,1 to get only the second result, which should be the one you look for
SELECT *
FROM table
ORDER BY date DESC
LIMIT 1,1

Related

Not getting this SQL query

Print all details of the 16th order placed by each customer if any.
How to print exact 16th Order?
SELECT COUNT(orderId)
FROM orders
GROUP BY CustomerID
ORDER BY CustomerID;
We can use a CTE and RANK to create a list of all orderId's, customerID's and their "order" as you named it.
Then we fetch those entries from the entire result whose order is 16.
WITH result AS
(
SELECT orderId, customerID,
RANK() OVER
(PARTITION BY customerID
ORDER BY orderId) AS rnk
FROM orders
)
SELECT orderId, customerID
FROM result
WHERE rnk=16
GROUP BY orderId, customerID
ORDER BY customerID;
For customerID's having less than 16 orders, nothing will be selected.
We can also use ROW_NUMBER instead of RANK in the above query, this makes no difference in your use case.
Select * from
(
SELECT *,
DENSE_RANK()
OVER(
PARTITION BY customerID
ORDER BY orderID
) my_rank
FROM orders
) as myTable
where my_rank = 16
order by CustomerID;
You can just use offset like:
SELECT *
FROM orders
GROUP BY CustomerID
ORDER BY CustomerID
LIMIT 1 OFFSET 15;
and set the OFFSET value to 15 so it skips the first 15 values and prints from the 16th value and limit it to only one row by setting the LIMIT value to 1

mysql query order by id desc plus if a value is less then on top

my query fetches results by id descending but what i wish is say by default new entry results show by id desc on top whenever page is refreshed but if any value in already added data in quantity column is less than 2 then it should displays on top then followed by new entries order by id desc
SELECT * FROM product ORDER BY id DESC, quantity <2 DESC
I tried that but does not seem to be working.
You could check the condition in order by prior of the id
SELECT *
FROM product
ORDER BY case when quantity <2 then 0 else 1 end desc,
id DESC
You can try below query:
select * ,(case when quantity<2 then 1 else 2 end)priority, row_number()over (order by id desc) rown from product
order by priority,rown
or if you want to avoid priority and rownumber in your result you can use cte as below:
with cte as (select * ,(case when quantity<2 then 1 else 2 end)priority, row_number()over (order by id desc) rown from product
)
select col1,col2.... from cte order by priority,rown

Select sum of top three scores for each user

I am having trouble writing a query for the following problem. I have tried some existing queries but cannot get the results I need.
I have a results table like this:
userid score timestamp
1 50 5000
1 100 5000
1 400 5000
1 500 5000
2 100 5000
3 1000 4000
The expected output of the query is like this:
userid score
3 1000
1 1000
2 100
I want to select a top list where I have n best scores summed for each user and if there is a draw the user with the lowest timestamp is highest. I really tried to look at all old posts but could not find one that helped me.
Here is what I have tried:
SELECT sum(score) FROM (
SELECT score
FROM results
WHERE userid=1 ORDER BY score DESC LIMIT 3
) as subquery
This gives me the results for one user, but I would like to have one query that fetches all in order.
This is a pretty typical greatest-n-per-group problem. When I see those, I usually use a correlated subquery like this:
SELECT *
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3;
This is not the whole solution, as it only gives you the top three scores for each user in its own row. To get the total, you can use SUM() wrapped around that subquery like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId;
Here is an SQL Fiddle example.
EDIT
Regarding the ordering (which I forgot the first time through), you can just order by totalScore in descending order, and then by MIN(timestamp) in ascending order so that users with the lowest timestamp appears first in the list. Here is the updated query:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
and here is an updated Fiddle link.
EDIT 2
As JPW pointed out in the comments, this query will not work if the user has the same score for multiple questions. To settle this, you can add an additional condition inside the subquery to order the users three rows by timestamp as well, like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score
AND mT.timeCol <= m.timeCol) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
I am still working on a solution to find out how to handle the scenario where the userid, score, and timestamp are all the same. In that case, you will have to find another tiebreaker. Perhaps you have a primary key column, and you can choose to take a higher/lower primary key?
Query for selecting top three scores from table.
SELECT score FROM result
GROUP BY id
ORDER BY score DESC
LIMIT 3;
Can you please try this?
SELECT score FROM result GROUP BY id ORDER BY score DESC, timestamp ASC LIMIT 3;
if 2 users have same score then it will set order depends on time.
You can use a subquery
SELECT r.userid,
( SELECT sum(r2.score)
FROM results r2
WHERE r2.userid = r.userid
ORDER BY score DESC
LIMIT 3
) as sub
FROM result r
GROUP BY r.userid
ORDER BY sub desc
You should do it like this
SELECT SUM(score) as total, min(timestamp) as first, userid FROM scores
GROUP BY userid
ORDER BY total DESC, first ASC
This is way more efficient than sub queries. If you want to extract more fields than userid, then you need to add them to the group by.
This will of cause not limit the number of scores pr user, which indeed seems to require a subquery to solve.

Get highest value from results

I have a table
t1
--------------
id
date
val_1
val_2
I need to get 10 latest results and have them sorted from earlier to latest date, so I do it as
SELECT * FROM (
SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER BY id ASC
Now, I need to get the highest value of val_1. I am trying to build a bar graph and would like to get the highest value because I need to divide it by the max height and use that result as a multiplier to stack-up val_1 and val_2 on top of each other without going outside of the graph's height.
So you want something like this?
SELECT *,
t2.biggest_value,
((t1.val_1/t2.biggest_value)*100) AS `%`
FROM myTable t1
JOIN (SELECT MAX(val_1) AS biggest_value,
project
FROM myTable
) AS t2
ON t2.project = t1.project
WHERE t1.project = $project_id
ORDER BY date DESC, id ASC
LIMIT 10
The JOIN gets the highest value on val_1 and the outer query is gathering all the data along with calculating the percentage of each val_1 based on the max value.
Live DEMO
Try like
SELECT val_1 as max_val FROM myTable ORDER by val_1 DESC LIMIT 1
You can also try with MAX like
SELECT MAX(val_1) as max_val FROM myTable
You may try this:
SELECT *, MAX(val_1) FROM (
SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER by id ASC
Do something like;
SELECT MAX(val_1) FROM(SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER by id ASC
See 3.6.1 The Maximum Value for a Column:
“What is the highest item number?”
SELECT MAX(article) AS article FROM shop;
You are free to use MAX() on query results as well:
SELECT MAX(val_1) FROM (SELECT …) t;
This query will give you the max with other columns : -
SELECT id,(select MAX(`price`) FROM `products` ) as FINAL from products
note :- This query assemble id and max price from the product table .i.e It associate max price with each record set.

mySQL query to return last record for each table

I have a mySQl db (name "stocks") with 50 tables, each tables with
id, symbol, date, time, open, high, low, close, volume as columns (9 columns).
I would like to know what is the last record for each table, ordered for date then time.
Should I have to ORDER BY all data for each table or there is a better way to just know last record?
I am asking help for a query that just return only last record for each table in db.
Thanks
PS For last record I mean most recent as Date then Time
There are two options how to do that:
-- I would use this only if you need more than one records
SELECT * FROM table ORDER BY date DESC LIMIT 1;
-- Way to go:
SELECT * FROM table WHERE date = (SELECT MAX(date) FROM table) LIMIT 1;
Don't forget to add index on date. If it's possible you add lot's of records at the same time you will have to add:
ORDER BY id DESC -- In case that date is highest for records for last records
ORDER BY time DESC -- Every other case
To the end of query
I am going to make the assumption that the record with the largest ID is the "last" (assuming strictly increasing sequential IDs that are unique within a table). If you have a better definition of "last" that could make a difference.
To get one "last" record, you could do:
Select * from table_1 where id = (select max(id) from table_1);
To get the results of all 50 tables into a single result set, you could do:
Select * from table_1 where id = (select max(id) from table_1)
union
Select * from table_2 where id = (select max(id) from table_2)
union
Select * from table_3 where id = (select max(id) from table_3)
union...
A MySQL-specific solution could be
Select * from table_1 order by id desc limit 1
union
Select * from table_2 order by id desc limit 1
union
Select * from table_3 order by id desc limit 1
union...
Based on your edit (where you actually define what you mean by "last"):
Select * from table_1 order by date desc, time desc, id desc limit 1
union
Select * from table_2 order by date desc, time desc, id desc limit 1
union
Select * from table_3 order by date desc, time desc, id desc limit 1
union...
Here is one way to do it without sorting the table:
select * from tab1
where time = (select max(time)
from tab1
where date = (select max(date) from tab1))
and date = (select max(date) from tab1)
It should be very fast, like, O(c), provided that both columns are indexed, otherwise the time will simply be O(n)