How can I combine the following information into 1 mysql query? - mysql

DETAILS
I want to find the difference between start and stop times(duration) for each test taken then rank users according to the highest score and lowest duration for each test. Ultimately I will use PDO to do this in mysql. So far though I've just been working on the sql. I think I'm pretty close now, but am unsure how to combine the information into one query.
WHAT I'VE GOT(untested).
I have the following table called testresults
--------------------------------------------------------------------
| index | id | start | stop | score|
--------------------------------------------------------------------
| 1 | 23 | 2012-06-06 07:30:20 | 2012-06-06 07:30:34 | 100 |
--------------------------------------------------------------------
In my select statement I want to get the id and the score along with the calculated duration.
I can get duration for any test taken today with the below query.
SELECT TIMESTAMPDIFF(SECOND,stop,start) AS duration
FROM testresults WHERE `start` >= DATE(NOW())
Then I sort the results based on rank. Rank is determined according to score and duration. Top place goes to the highest score with the shortest duration.I believe I can do that with something like this:
ORDER BY score DESC, duration ASC
I only want maybe 100 entries so,
LIMIT 100
QUESTION
How can I combine the above information into 1 query?

SELECT `id`, `score`,
TIMESTAMPDIFF( SECOND, start, stop ) AS `duration`
FROM `testresults`
WHERE `start` >= DATE(NOW())
ORDER BY `score` DESC, `duration` ASC
LIMIT 100;
This will work pretty good. You'll also need to put id & score in SELECT statement.

SELECT id, score, TIMESTAMPDIFF(SECOND,stop,start) AS duration FROM testresults
WHERE `start` >= DATE(NOW()) ORDER BY score DESC, duration ASC LIMIT 100
this should work

Related

How to select records from mysql along with one previous record

I have table like this
+----+----------------------+------------+
| id | desc | date |
+----+----------------------+------------+
| 15 | nah_i_kid | 2017-06-07 |
+----+----------------------+------------+
| 17 | it_is_just_the_cat | 2017-06-08 |
+----+----------------------+------------+
| 18 | thank_God | 2017-06-09 |
+----+----------------------+------------+
| 44 | no_kidding | 2017-06-10 |
+----+----------------------+------------+
My sql is
SELECT * FROM TABLE WHERE date between '2017-06-09' AND '2017-06-12'
I want the result should contain one previous record also (i-e record having id=17 take it example)
Thanks.
If you are using MYSQL, I have tried and it work good.
(select * from table where date < '2017-06-09' order by date desc limit 1 ) union (select * from table where date between '2017-06-09' AND '2017-06-12' order by date)
If you want the records from the previous date, you can do:
select t.*
from t
where date > (select max(t2.date) from t t2 where t2.date < '2017-06-09') and
date <= '2017-06-12';
This does what you want, assuming you have no duplicates on a date.
If you want exactly one row and you know the ids are assigned in chronological order, you can do:
select t.*
from t
where id > (select max(t2.id) from t t2 where t2.date < '2017-06-09') and
date <= '2017-06-12';
This solves the problem by taking the most recent previous record based on id.
If the ids are not in chronological order and you can have duplicates, the query gets more difficult. There is no definition of the "previous record". A union all is the best solution:
(select t.*
from t
where date < '2017-06-09'
order by date desc
limit 1
) union all
select t.*
from t
where date > >= '2017-06-09' and
date <= '2017-06-12'
I think this is what you're looking for.
The first part of the query is identical to yours, the second part gets all the values before your first date 2017-06-09, orders the values by date DESC, then limits the query to only take the topmost value using LIMIT 1.
SELECT
*
FROM table
WHERE `date` BETWEEN '2017-06-09' AND '2017-06-12'
OR `id` = (
SELECT
`id`
FROM table
WHERE `date` < '2017-06-09'
ORDER BY `date` DESC
LIMIT 1
)
I want the result should contain one previous record also (i-e record having id=17 take it example)
If you know that the rows are created in chronological order, and your id field is auto-increment, then you don't even need to use the date field, because you can assume that a higher id indicates a later record. So just cap your search on the id you want, and grab two rows:
SELECT * FROM TABLE WHERE id <= 17 ORDER BY id DESC LIMIT 2;
This has the added benefit of being fully indexed, which may not be the case if you introduce a WHERE clause on the date field.

MySQL - Group By Latest and Join First Instance

I've tried a few things but I've ended up confusing myself.
What I am trying to do is find the most recent records from a table and left join the first after a certain date.
An example might be
id | acct_no | created_at | some_other_column
1 | A0001 | 2017-05-21 00:00:00 | x
2 | A0001 | 2017-05-22 00:00:00 | y
3 | A0001 | 2017-05-22 00:00:00 | z
So ideally what I'd like is to find the latest record of each acct_no sorted by created_at DESC so that the results are grouped by unique account numbers, so from the above record it would be 3, but obviously there would be multiple different account numbers with records for different days.
Then, what I am trying to achieve is to join on the same table and find the first record with the same account number after a certain date.
For example, record 1 would be returned for a query joining on acct_no A0001 after or equal to 2017-05-21 00:00:00 because it is the first result after/equal to that date, so these are sorted by created_at ASC AND created_at >= "2017-05-21 00:00:00" (and possibly AND id != latest.id.
It seems quite straight forward but I just can't get it to work.
I only have my most recent attempt after discarding multiple different queries.
Here I am trying to solve the first part which is to select the most recent of each account number:
SELECT latest.* FROM my_table latest
JOIN (SELECT acct_no, MAX(created_at) FROM my_table GROUP
BY acct_no) latest2
ON latest.acct_no = latest2.acct_no
but that still returns all rows rather than the most recent of each.
I did have something using a join on a subquery but it took so long to run I quite it before it finished, but I have indexes on acct_no and created_at but I've also ran into other problems where columns in the select are not in the group by. I know this can be turned off but I'm trying to find a way to perform the query that doesn't require that.
Just try a little edit to your initial query:
SELECT latest.* FROM my_table latest
join (SELECT acct_no, MAX(created_at) as max_time FROM my_table GROUP
BY acct_no) latest2
ON latest.acct_no = latest2.acct_no AND latest.created_at = latest2.max_time
Trying a different approach. Not sure about the performance impact. But hoping that avoiding self join and group by would be better in terms of performance.
SELECT * FROM (
SELECT mytable1.*, IF(#temp <> acct_no, 1, 0) selector, #temp := acct_no FROM `mytable1`
JOIN (SELECT #temp := '') a
ORDER BY acct_no, created_at DESC , id DESC
) b WHERE selector = 1
Sql Fiddle
you need to get the id where max date is created.
SELECT latest.* FROM my_table latest
join (SELECT max(id) as id FROM my_table GROUP
BY acct_no where created_at = MAX(created_at)) latest2
ON latest.id = latest2.id

mysql query cant order records as needed

My table is:
+----visits----+
| id |
| client_ip |
| date |
| type |
+--------------+
It stores all site user visits. Now i need to make a graph by getting data from this table, my query is:
SELECT COUNT(*) as hit_counts, date FROM visits GROUP BY date ORDER BY date ASC LIMIT 25
But the idea is that each time i refresh a page on my site, i get a new row inserted in table, and mysql counts 25 records(but i need to limit my query at 25 days(1 day can have 100+ visits), not only 25 records).
Try this:
SELECT temp_table.* FROM
(SELECT COUNT(*) as hit_counts, date
FROM visits
GROUP BY date) as temp_table
ORDER BY temp_table.date ASC
LIMIT 25
use DESC for the last 25 days.

MySQL Order by Date, with NULL first

I have a select statement that I would like to select 1 record from a table. The structure is:
id | start_time
--------------
1 NULL
2 2014-08-23
3 2014-09-01
I would like to select the item with a NULL start time, but if that does not exist I would like it to select the latest start_time. I have tried using ORDER with LIMIT 1, but using ORDER BY start_time either gives NULL first followed by the earliest starting, or latest starting then NULL. Is it possible to have result order 1,3,2 ?
You can use two sort expressions to get the ordering you want:
select t.*
from table t
order by (start_time is null) desc,
start_time desc
limit 1;
You can have two different ORDER BY expressions:
SELECT * from table ORDER BY (start_time IS NULL) DESC, start_time DESC;

MySQL getting MAX of previous WHERE condition

I have a table:
id | score | date
bob | 40 | 2010-1-1
bob | 70 | 2010-1-15
sue | 55 | 2010-1-1
sue | 80 | 2010-2-1
I want to query for either the score for a user on a specific date OR, if no score exists for that user on that date,return the score from the most recent date for that user.
Is there a way to do this without a sub-query?
For instance, if I do:
SELECT score
FROM table
WHERE id = '$id'
AND IFNULL(
date = DATE(FROM_UNIXTIME($date)),
MAX(date)
)
I would get no result, as the id does not show up for the most recent date.
Update
Felix reminded me I can't use aggregate functions in the WHERE clause, so now I'm wondering if there is a pseudo-aggregate date function for saying "most recent date" in the where clause, and if so, if I can specify the user when using THAT function?
Update 2
So this is what I have gotten to work, but I still don't know if it's the best way to go (ie, do I need the nested query?):
SELECT score
FROM table
WHERE id = '$id'
AND date = IFNULL(
(SELECT date FROM table
WHERE id = '$id' AND
date = DATE(FROM_UNIXTIME($date))
),
(SELECT MAX(date) FROM table
WHERE id = '$id'
)
)
Not sure of the performance of this one:
SELECT a.*
FROM table a
LEFT JOIN table b ON (
b.date=DATE(FROM_UNIXTIME($date)) AND b.date=a.date AND b.id=a.id
)
WHERE a.id='$id'
ORDER BY b.date DESC, a.date DESC
LIMIT 1
SELECT score
FROM table
WHERE id = '$id'
AND date > '2010-01-01'
ORDER BY date DESC
LIMIT 1
EDIT
See my second comment regarding the interpretations:
Interpretation 1:
SELECT score
FROM table
WHERE id = '$id'
AND date >= '2010-01-01'
ORDER BY date ASC
LIMIT 1
Interpretation 2:
SELECT score,
LEAST(ABS(DATEDIFF('2010-01-01', date)), 1) AS myorder
FROM table
WHERE id = '$id'
AND date >= '2010-01-01'
ORDER BY myorder ASC, date DESC
LIMIT 1
Use this query :
SELECT * FROM test
WHERE date = (
SELECT MAX(date) FROM test
) ;
If you want based on the id , you need to do group by operation on id column