mysql select latest record only for record - mysql

I have a MySQL database with the following structure:
custodian | counta | countc | countc | total | date
-------------------------------------------------------
ed | 1 | 2 | 3 | 6 | 1/1/2016
ed | 2 | 3 | 5 | 10 | 1/2/2016
ed | 2 | 3 | 6 | 11 | 1/3/2016
ed | 1 | 3 | 5 | 9 | 1/4/2016
fred | 1 | 2 | 3 | 6 | 1/1/2016
fred | 2 | 3 | 5 | 10 | 1/2/2016
fred | 2 | 3 | 6 | 11 | 1/3/2016
fred | 1 | 3 | 5 | 9 | 1/4/2016
How do I return the latest record for a custodian? I've been playing around with this condition where date >= DATE_SUB(NOW(),INTERVAL 59 MINUTE) since the table is updated hourly, but if I update the script twice in an hour, I would return more than one result per custodian.
Any advice?

You need to combine ORDER BY and LIMIT:
SELECT *
FROM yourTableName
WHERE custodian = 123
ORDER BY `date` DESC
LIMIT 1

You could try this
SELECT * FROM tbl ORDER BY date DESC LIMIT 1
The most recent date will be the first record when ordered decendingly, and limiting the select to 1 means you get only the latest record.

Related

mysql return two minimum values with limit

I have a table named: workers and a table named: schedule with the following format:
workers:
| id | name | vacationA | vacationB | workhistory |
| 1 | Florin | 2017-05-05 | 2017-05-25 | 2010-01-01 |
| 2 | Andrei | 2017-06-05 | 2017-06-25 | 2010-01-01 |
| 3 | Alexandra | 2017-07-05 | 2017-07-25 | 2010-01-01 |
| 4 | Emilia | 2017-08-05 | 2017-08-25 | 2010-01-01 |
| 5 | Nicoleta | 2017-09-05 | 2017-09-25 | 2010-01-01 |
+----+-----------+------------+------------+-------------+
schedule:
| day | month | name | shifts |
+-----+-------+-----------+--------+
| 1 | 6 | Florin | 0 |
| 1 | 6 | Andrei | 1 |
| 1 | 6 | Alexandra | 2 |
| 1 | 6 | Emilia | 3 |
| 1 | 6 | Nicoleta | 4 |
+-----+-------+-----------+--------+
I need to interrogate table "workers" to give me 2 random names, with minimum shifts number, and workers should not be in vacation period. Also work history must be greater than 18 MONTHS.
In this case, the query i need should return Florin and Andrei.
This is what I've got so far, but it doesn't work as supposed:
SELECT name
FROM workers
WHERE (CURDATE() NOT BETWEEN vacationA AND vacationB) AND
workhistory > (DATE_SUB(CURDATE(), INTERVAL 18 MONTH)) AND
name IN (SELECT name FROM schedule ORDER BY shifts LIMIT 2)
ORDER BY RAND() LIMIT 2;
This query returns
1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'.
Thank you!
Join to schedule instead and then use LIMIT 2:
SELECT w.name
FROM workers w
INNER JOIN schedule s
ON w.name = s.name
WHERE CURDATE() NOT BETWEEN w.vacationA AND w.vacationB AND
w.workhistory > DATE_SUB(CURDATE(), INTERVAL 18 MONTH)
ORDER BY s.shifts
LIMIT 2;
I don't understand the random ordering, because you are only returning two worker records, and those records are not chosen randomly, but rather belong to the smallest shift numbers.

MySQL - select average of column A for first N entries from column B

I have a ratings table, where each user can add one rating a day. But each user might miss several days between ratings.
I'd like to get the average rating for each user_id's first 7 entries of created_at.
My table:
mysql> desc entries;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| rating | tinyint(4) | NO | | NULL | |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
Ideally I'd just get something like:
+------------+------------------+
| day | average_rating |
+------------+------------------+
| 1 | 2.53 |
+------------+------------------+
| 2 | 4.30 |
+------------+------------------+
| 3 | 3.67 |
+------------+------------------+
| 4 | 5.50 |
+------------+------------------+
| 5 | 7.23 |
+------------+------------------+
| 6 | 6.98 |
+------------+------------------+
| 7 | 7.22 |
+------------+------------------+
The closest I've been able to get is:
SELECT rating, user_id, created_at FROM entries ORDER BY user_id asc, created at desc
Which isn't very close at all...
Is it even possible? Will the performance be terrible? It's something that would need to run every time a web page is loaded, so would it be better to just run this once a day and save the results? (to another table!?)
edit - second attempt
Working towards a solution, I think this would get the rating for each user's first day:
select rating from entries where user_id in
(select user_id from entries order by created_at limit 1);
But I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
So now I'm going to play around with JOIN to see if that helps.
edit - third attempt, getting closer
I found this stackoverflow post, which is closer to what I want.
select e1.* from entries e1 left join entries e2
on (e1.user_id = e2.user_id and e1.created_at > e2.created_at)
where e2.id is null;
It gets the rating for the first day for each user.
Next step is to work out how to get days 2 to 7. I can't use 1.created_at > e2.created_at for that, so I'm really confused now.
edit - fourth attempt
Okay, I think it's not possible. Once I worked out how to turn off 'full group by' mode, I realised I'll probably need to use a subquery with limit <user_id>, <day_num>, for which I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
My current method is to just get the entire table, and use PHP to calculate the average for each day.
If I understand correctly you want to take the last 7 ratings the user gave, ordered by the date they gave the rating. The last 7 ratings of one user may fall on different days to another user, however they will be averaged together regardless of date.
First we need to order the data by user and date and give each user their own incrementing row count. I do this by adding two variables, one for the last user id and one for the row number:
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc;
If the previous user_id is different we reset the row counter to 1. The result from this is:
+---------------------+--------+------+---------+
| created_at | rating | row | user_id |
+---------------------+--------+------+---------+
| 2017-01-10 00:00:00 | 1 | 1 | 1 |
| 2017-01-09 00:00:00 | 1 | 2 | 1 |
| 2017-01-08 00:00:00 | 1 | 3 | 1 |
| 2017-01-07 00:00:00 | 1 | 4 | 1 |
| 2017-01-06 00:00:00 | 1 | 5 | 1 |
| 2017-01-05 00:00:00 | 1 | 6 | 1 |
| 2017-01-04 00:00:00 | 1 | 7 | 1 |
| 2017-01-03 00:00:00 | 1 | 8 | 1 |
| 2017-01-02 00:00:00 | 1 | 9 | 1 |
| 2017-01-01 00:00:00 | 1 | 10 | 1 |
| 2017-01-13 00:00:00 | 1 | 1 | 2 |
| 2017-01-11 00:00:00 | 1 | 2 | 2 |
| 2017-01-09 00:00:00 | 1 | 3 | 2 |
| 2017-01-07 00:00:00 | 1 | 4 | 2 |
| 2017-01-05 00:00:00 | 1 | 5 | 2 |
| 2017-01-03 00:00:00 | 1 | 6 | 2 |
| 2017-01-01 00:00:00 | 1 | 7 | 2 |
| 2017-01-13 00:00:00 | 1 | 1 | 3 |
| 2017-01-01 00:00:00 | 1 | 2 | 3 |
| 2017-01-03 00:00:00 | 1 | 1 | 4 |
| 2017-01-01 00:00:00 | 1 | 2 | 4 |
| 2017-01-02 00:00:00 | 1 | 1 | 5 |
+---------------------+--------+------+---------+
We now simply wrap this in another statement to select the avg where the row number is less than or equal to seven.
select e1.row day, avg(e1.rating) avg
from (
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc) e1
where e1.row <=7
group by e1.row;
This outputs:
+------+--------+
| day | avg |
+------+--------+
| 1 | 1.0000 |
| 2 | 1.0000 |
| 3 | 1.0000 |
| 4 | 1.0000 |
| 5 | 1.0000 |
| 6 | 1.0000 |
| 7 | 1.0000 |
+------+--------+

MySQL select a row from a daterange excluding the year

I'm trying to create a MySQL query to select the daily price from a table that is between a date range from another. I only want to use 'starting-ending' months and days from the table "seasons" and I want to pass the year dynamically to the query.
This is my query: (I'm giving it the Year to exclude the one on the table)
SELECT a.season, b.base_price
FROM seasons a
JOIN pricebyseason b ON a.id=b.season_id
WHERE b.prop_id='6' AND '2015-11-29' BETWEEN DATE_FORMAT(a.starting,'2015-%m-%d') AND DATE_FORMAT(a.ending,'2016-%m-%d')
ORDER BY b.base_price DESC
It works but not with all dates.
These are the tables:
seasons (these are static date values)
+----+--------------+------------+------------+
| id | season | starting | ending |
+----+--------------+------------+------------+
| 1 | Peak Season | 2015-12-11 | 2016-01-09 |
| 2 | High Season | 2015-11-27 | 2016-04-15 |
| 3 | Mid Season | 2015-04-16 | 2015-09-01 |
| 4 | Low Season | 2015-09-02 | 2015-11-26 |
| 5 | Spring Break | 2015-03-05 | 2015-03-21 |
+----+--------------+------------+------------+
pricebyseason
+----+---------+-----------+------------+
| id | prop_id | season_id | base_price |
+----+---------+-----------+------------+
| 1 | 6 | 1 | 950 |
| 2 | 6 | 2 | 750 |
| 3 | 6 | 3 | 450 |
| 4 | 6 | 4 | 400 |
| 5 | 6 | 5 | 760 |
+----+---------+-----------+------------+
What I want to achive is query the dialy price between checkin, checkout selection
I create this sqlfiddle: http://sqlfiddle.com/#!9/4a6f4
This is a previuos query that is not working either:
SELECT a.base_price,b.season,b.starting,b.ending
FROM pricebyseason a JOIN seasons b ON a.season_id=b.id
WHERE a.prop_id='6' AND
(DATE_FORMAT(b.starting,'%m-%d') <= '12-27' OR DATE_FORMAT(b.starting,'2016-%m-%d') >= '2015-12-27')
AND
(DATE_FORMAT(b.ending,'%m-%d') >= '12-27' OR DATE_FORMAT(b.ending,'2016-%m-%d') <= '2015-12-27')
ORDER BY base_price DESC
And here are some sample dates for each season: '2016-01-08','2015-12-27','2016-04-14','2015-11-29','2016-04-15','2015-09-01','2016-09-02','2015-11-26','2016-10-10','2016-03-18','2016-06-22','2015-06-15'
Thank a lot

How to select top 5 rows in current month from another table

I'm going to select top 5 product in this month from order_log where id and date equal to the columns in order_db. Because each order can be more than 1 product. It's a bit complicate to tell so please see my sample below.
order_db
+------+------+---------------------+
|od_id | p_id | od_dtime |
+------+------+---------------------+
| 1 | 1 | 2014-10-01 00:00:00 |
+------+------+---------------------+
| 2 | 2 | 2014-10-01 00:00:00 |
+------+------+---------------------+
| 3 | 3 | 2014-10-02 00:00:00 |
+------+------+---------------------+
| 4 | 4 | 2014-10-02 00:00:00 |
+------+------+---------------------+
| 5 | 5 | 2014-10-03 00:00:00 |
+------+------+---------------------+
.
.
+------+------+---------------------+
| 10 | 10 | 2014-10-31 00:00:00 |
+------+------+---------------------+
order_log
+-------+------+
| pl_id | p_id |
+-------+------+
| 1 | 5 |
+-------+------+
| 2 | 5 |
+-------+------+
| 3 | 5 |
+-------+------+
| 4 | 2 |
+-------+------+
| 5 | 1 |
+-------+------+
.
.
.
+-------+------+
| 10 | 1 |
+-------+------+
So I need to count the product_id where date is in current month from od_dtime of order_db. I wrote:
SELECT * FROM order_log WHERE p_id IN
(SELECT p_id FROM order_db WHERE month(bk_dtime)='10')
After running this query, I expect :
Top 5 order of month 10 are :
Product 3 (3 orders)
Product 1 (2 orders)
Product 2 (1 order)
**for the product name I don't have a problem with it.
If you want to select the top 5 rows from the database you can use the following sql query.
for eg: you have a table having some attributes including the time-stamp attribute and you wish to order in descending order of time
SELECT * FROM table_name where acc_id='2' order by time desc limit 0 , 5

Most recent X posts by unique authors

I have a blog where multiple authors can post. On the frontpage I want to show a carousel of the most recent X posts - let's say 5. However, I want to filter out author duplicates. So if an author would have multiple posts within those 5, only the most recent one would make it in and the others would be filtered out. Also, I want to select the entire row, not just IDs. Here an example:
Data
ID | Title | AuthorID | Date
1 | "Now" | 6 | 2013-03-27
2 | "this" | 5 | 2013-03-26
3 | "is" | 4 | 2013-03-27
4 | "the" | 2 | 2013-03-28
5 | "story" | 2 | 2013-03-29
6 | "all" | 4 | 2013-04-01
7 | "about" | 2 | 2013-04-02
8 | "how" | 3 | 2013-04-03
9 | "My" | 1 | 2013-04-04
10 | "life" | 1 | 2013-04-05
Desired result
ID | Title | AuthorID | Date
10 | "life" | 1 | 2013-04-05
8 | "how" | 3 | 2013-04-03
7 | "about" | 2 | 2013-04-02
6 | "all" | 4 | 2013-04-01
2 | "this" | 5 | 2013-03-26
I currently use the following:
SELECT * FROM posts
ORDER BY date DESC
LIMIT 5
But of course this gets me the following result
ID | Title | AuthorID | Date
10 | "life" | 1 | 2013-04-05
9 | "My" | 1 | 2013-04-04
8 | "how" | 3 | 2013-04-03
7 | "about" | 2 | 2013-04-02
6 | "all" | 4 | 2013-04-01
and I want to eliminate the duplicates. How?
I saw that there is a DISTINCT clause but this would select only the author IDs and I want to select the entire row.
This trick/hack will work, but might take some time to generate results(took me 3.713 seconds on a table with 1,00,000 rows):
SELECT *
FROM (SELECT *
FROM posts
ORDER BY date DESC ) d
GROUP BY d.`AuthorID`
LIMIT 5;
I think this is what you're looking for:
SELECT *
FROM (SELECT *
FROM posts
ORDER BY Date DESC) p
GROUP BY p.AuthorID
ORDER BY Date DESC
LIMIT 5;
SQL Fiddle