MYSQL - MAX() returns wrong date - mysql

I have a mysql table like this
+----------+----------+-------------------+
| entrykey | user_key | validfrom |
+----------+----------+-------------------+
| 1 | 3 | 2016-4-1 0:0:0.0 |
| 2 | 3 | 2016-12-1 0:0:0.0 |
| 3 | 2 | 2016-12-1 0:0:0.0 |
| 4 | 2 | 2016-3-1 0:0:0.0 |
+----------+----------+-------------------+
now I am trying to get only the row for each user where the validfrom is the newest. So I am doing a query like this:
SELECT entrykey, user_key, max(validfrom)
FROM table
Group by user_key;
It is working fine for almost all of my data, just this two examples I posted here in the table select the wrong row which is older. So for user_key 3 it selects entrykey 1 and for the user_key 2 it selects entrykey 4.
What am I doing wrong?

I guess the validform should be 2016-03-01 instead of 2016-3-01 because it is not converted into date before compare.

I am totally agree with the point made in the accepted answer (+1 for that). But, even if op somehow convert validfrom from string to DateTime, his attempt won't give him the desired result.
Let's examine the query given in question:
SELECT entrykey, user_key, MAX(STR_TO_DATE(validfrom, '%Y-%c-%e')) as date1
FROM table
Group by user_key;
Now, this query will return user_key with maximum value of validfrom for that particular user_key. But the entrykey won't be the entrykey with max validfrom. (Check the first result in demo link)
In order to achieve above task, following query will work just fine!
SELECT t1.entrykey, t1.user_key, t2.maxdate as MaxDate
FROM t t1
inner join (select user_key,MAX(STR_TO_DATE(validfrom, '%Y-%c-%e')) as maxdate
from t
Group by user_key
) t2
on t1.user_key = t2.user_key and t1.validfrom = t2.maxdate;
Click here for Demo with DateTime as datatype of validfrom
Click here for Demo with STR_TO_DATE() function
Hope it helps!

Related

MySQL group/order behaves differently in 5.7

I have a table that looks like this:
id | text | language_id | other_id | dateCreated
1 | something | 1 | 5 | 2015-01-02
2 | something | 1 | 5 | 2015-01-01
3 | something | 2 | 5 | 2015-01-01
4 | something | 2 | 6 | 2015-01-01
and I want to get all latest rows for each language_id that have other_id 5.
my query looks like this
SELECT * (
SELECT *
FROM tbl
WHERE other_id = 5
ORDER BY dateCreated DESC
) AS r
GROUP BY r.language_id
With MySQL 5.6 I get 2 rows with ID 1 and 3, which is what I want.
With MySQL 5.7.10 I get 2 rows with IDs 2 and 3 and it seems to me that the ORDER BY in the subquery is ignored.
Any ideas what might be the problem ?
You should go with the query below:
SELECT
*
FROM tbl
INNER JOIN
(
SELECT
other_id,
language_id,
MAX(dateCreated) max_date_created
FROM tbl
WHERE other_id = 5
GROUP BY language_id
) AS t
ON tbl.language_id = t.language_id AND tbl.other_id = t.other_id AND
tbl.dateCreated = t.max_date_created
Using GROUP BY without aggregate function will pick row in arbitrary order. You should not rely on what's row is returned by the GROUP BY. MySQL doesn't ensure this.
Quoting from this post
In a nutshell, MySQL allows omitting some columns from the GROUP BY,
for performance purposes, however this works only if the omitted
columns all have the same value (within a grouping), otherwise, the
value returned by the query are indeed indeterminate, as properly
guessed by others in this post. To be sure adding an ORDER BY clause
would not re-introduce any form of deterministic behavior.
Although not at the core of the issue, this example shows how using *
rather than an explicit enumeration of desired columns is often a bad
idea.
Excerpt from MySQL 5.0 documentation:
When using this feature, all rows in each group should have the same
values for the columns that are omitted from the GROUP BY part. The
server is free to return any value from the group, so the results are
indeterminate unless all values are the same.

Get the MAX date between a range

I was searching for querys but i cant find an answer that helps me or if exit a similar question.
i need to get the info of the customers that made their last purchase between two dates
+--------+------------+------------+
| client | amt | date |
+--------+------------+------------+
| 1 | 2440.9100 | 2014-02-05 |
| 1 | 21640.4600 | 2014-03-11 |
| 2 | 6782.5000 | 2014-03-12 |
| 2 | 1324.6600 | 2014-05-28 |
+--------+------------+------------+
for example if i want to know all the cust who make the last purchase between
2014-02-11 and 2014-03-16, in that case the result must be
+--------+------------+------------+
| client | amt | date |
+--------+------------+------------+
| 1 | 21640.4600 | 2014-03-11 |
+--------+------------+------------+
cant be the client number 2 cause have a purchease on 2014-05-28,
i try to make a
SELECT MAX(date)
FROM table
GROUP BY client
but that only get the max of all dates,
i dont know if exist a function or something that can help, thanks.
well i dont know how to mark this question as resolved but this work for me
to complete the original query
SELECT client, MAX(date)
FROM table
GROUP BY client
HAVING MAX(date) BETWEEN date1 AND date2
thanks to all that took a minute to help me with my problem,
special thanks to Ollie Jones and Peter Pei Guo
Something in this format, replace date1 and date 2 with the real values.
SELECT client, max(date)
from table
group by client
having max(date) between date1 AND date2
There is more than one way to do this. Here is one of them.
select * from
(
select client, max(date) maxdate
from table
group by client ) temp
where maxdate between '2014-02-11' and '2014-03-06'
This will allow you to grab the amount column of the applicable rows as well:
select t.*
from tbl t
join (select client, max(date) as last_date
from tbl
group by client
having max(date) between date1 and date2) v
on t.client = v.client
and t.date = v.last_date
I had to change the field "Date" to "TheDate" since date is a reserved word. I assume you are using SQL? My table name is Table1. You need to group records:
SELECT Table1.Client, Sum(Table1.Amt) AS SumOfAmt, Table1.TheDate
FROM Table1
GROUP BY Table1.Client, Table1.TheDate
HAVING (((Table1.TheDate) Between #2/11/2014# And #3/16/2014#));
Query Results:
Client SumOfAmt TheDate
1 21640 03/11/14
2 6792 03/12/14
You may want to get yourself a copy of MS Access. You can generate SQL statements using their query builder which I used to generate this SQL. When I make a post here I will always test it first to make sure it works! I have never written even 1 line of SQL code, but have executed thousands of them from within MS Access.
Good luck,
Dan

Using ORDER BY and GROUP BY together

My table looks like this (and I'm using MySQL):
m_id | v_id | timestamp
------------------------
6 | 1 | 1333635317
34 | 1 | 1333635323
34 | 1 | 1333635336
6 | 1 | 1333635343
6 | 1 | 1333635349
My target is to take each m_id one time, and order by the highest timestamp.
The result should be:
m_id | v_id | timestamp
------------------------
6 | 1 | 1333635349
34 | 1 | 1333635336
And i wrote this query:
SELECT * FROM table GROUP BY m_id ORDER BY timestamp DESC
But, the results are:
m_id | v_id | timestamp
------------------------
34 | 1 | 1333635323
6 | 1 | 1333635317
I think it causes because it first does GROUP_BY and then ORDER the results.
Any ideas? Thank you.
One way to do this that correctly uses group by:
select l.*
from table l
inner join (
select
m_id, max(timestamp) as latest
from table
group by m_id
) r
on l.timestamp = r.latest and l.m_id = r.m_id
order by timestamp desc
How this works:
selects the latest timestamp for each distinct m_id in the subquery
only selects rows from table that match a row from the subquery (this operation -- where a join is performed, but no columns are selected from the second table, it's just used as a filter -- is known as a "semijoin" in case you were curious)
orders the rows
If you really don't care about which timestamp you'll get and your v_id is always the same for a given m_i you can do the following:
select m_id, v_id, max(timestamp) from table
group by m_id, v_id
order by max(timestamp) desc
Now, if the v_id changes for a given m_id then you should do the following
select t1.* from table t1
left join table t2 on t1.m_id = t2.m_id and t1.timestamp < t2.timestamp
where t2.timestamp is null
order by t1.timestamp desc
Here is the simplest solution
select m_id,v_id,max(timestamp) from table group by m_id;
Group by m_id but get max of timestamp for each m_id.
You can try this
SELECT tbl.* FROM (SELECT * FROM table ORDER BY timestamp DESC) as tbl
GROUP BY tbl.m_id
SQL>
SELECT interview.qtrcode QTR, interview.companyname "Company Name", interview.division Division
FROM interview
JOIN jobsdev.employer
ON (interview.companyname = employer.companyname AND employer.zipcode like '100%')
GROUP BY interview.qtrcode, interview.companyname, interview.division
ORDER BY interview.qtrcode;
I felt confused when I tried to understand the question and answers at first. I spent some time reading and I would like to make a summary.
The OP's example is a little bit misleading.
At first I didn't understand why the accepted answer is the accepted answer.. I thought that the OP's request could be simply fulfilled with
select m_id, v_id, max(timestamp) as max_time from table
group by m_id, v_id
order by max_time desc
Then I took a second look at the accepted answer. And I found that actually the OP wants to express that, for a sample table like:
m_id | v_id | timestamp
------------------------
6 | 1 | 11
34 | 2 | 12
34 | 3 | 13
6 | 4 | 14
6 | 5 | 15
he wants to select all columns based only on (group by)m_id and (order by)timestamp.
Then the above sql won't work. If you still don't get it, imagine you have more columns than m_id | v_id | timestamp, e.g m_id | v_id | timestamp| columnA | columnB |column C| .... With group by, you can only select those "group by" columns and aggreate functions in the result.
By far, you should have understood the accepted answer.
What's more, check row_number function introduced in MySQL 8.0:
https://www.mysqltutorial.org/mysql-window-functions/mysql-row_number-function/
Finding top N rows of every group
It does the simlar thing as the accepted answer.
Some answers are wrong. My MySQL gives me error.
select m_id,v_id,max(timestamp) from table group by m_id;
#abinash sahoo
SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
FROM table_name
GROUP BY m_id
#Vikas Garhwal
Error message:
[42000][1055] Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'testdb.test_table.v_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Why make it so complicated? This worked.
SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
FROM table_name
GROUP BY m_id
Just you need to desc with asc. Write the query like below. It will return the values in ascending order.
SELECT * FROM table GROUP BY m_id ORDER BY m_id asc;

MySQL GROUP BY - returning wrong dates

table: chat_thread
+-----------+----------+--------------------+
| id | title | date |
+-----------+----------+--------------------+
| 1 | Thread 1 | 2012-02-16 01:12:40|
| 2 | Thread 2 | 2012-02-17 02:32:44|
+-----------+----------+--------------------+
table: chat_comment
+-----------+----------+--------------------+
| id | t_id | date |
+-----------+----------+--------------------+
| 1 | 1 | 2012-02-19 19:45:32|
| 2 | 1 | 2012-02-15 22:29:20|
+-----------+----------+--------------------+
Here's my situation, I creating a basic forum with threads and comments. I want to order by the last comment date, and if there is no comments, then the thread start date. The problem I'm having is trying to echo out the last reply date in my while loop.
mysql_query("
SELECT *,
chat_comment.date AS commentDate,
chat_thread.date AS threadDate
FROM chat_thread
LEFT JOIN chat_comment ON chat_thread.id = chat_comment.t_id
GROUP BY chat_thread.id
ORDER BY commentDate DESC, threadDate DESC");
My issue isn't the correct order of the threads, but the comment dates. In my tables above, if I'm trying to echo out the date, I'm currently getting 2012-02-15 22:29:20 instead of the more recent 2012-02-19 19:45:32.
Is there something I'm doing wrong?
I think it has something to do with the GROUP BY, because if I change it to GROUP BY chat_comment.date then the dates in the while loop are accurate, but there are duplicates, because I am not grouping by the chat_thread.id
Instead of selecting the last chat_comment.date, you are selecting an arbitrary one. (See MySQL Reference Manual, ยง11.6.3: GROUP BY and HAVING with Hidden Columns for a detailed explanation of this.) You need to use MAX(chat_comment.date) instead of just chat_comment.date.
You have to use MAX() if you want a maximum value, and then you have to GROUP by all the other columns, like so:
mysql_query("
SELECT chat_thread.id, chat_thread.title, chat_thread.date,
MAX(chat_comment.date) AS commentDate
FROM chat_thread
LEFT JOIN chat_comment ON chat_thread.id = chat_comment.t_id
GROUP BY chat_thread.id, chat_thread.title, chat_thread.date
ORDER BY MAX(chat_comment.date) DESC, chat_thread.date DESC");

MySQL - Exclude rows from Select based on duplication of two columns

I am attempting to narrow results of an existing complex query based on conditional matches on multiple columns within the returned data set. I'll attempt to simplify the data as much as possible here.
Assume that the following table structure represents the data that my existing complex query has already selected (here ordered by date):
+----+-----------+------+------------+
| id | remote_id | type | date |
+----+-----------+------+------------+
| 1 | 1 | A | 2011-01-01 |
| 3 | 1 | A | 2011-01-07 |
| 5 | 1 | B | 2011-01-07 |
| 4 | 1 | A | 2011-05-01 |
+----+-----------+------+------------+
I need to select from that data set based on the following criteria:
If the pairing of remote_id and type is unique to the set, return the row always
If the pairing of remote_id and type is not unique to the set, take the following action:
Of the sets of rows for which the pairing of remote_id and type are not unique, return only the single row for which date is greatest and still less than or equal to now.
So, if today is 2011-01-10, I'd like the data set returned to be:
+----+-----------+------+------------+
| id | remote_id | type | date |
+----+-----------+------+------------+
| 3 | 1 | A | 2011-01-07 |
| 5 | 1 | B | 2011-01-07 |
+----+-----------+------+------------+
For some reason I'm having no luck wrapping my head around this one. I suspect the answer lies in good application of group by, but I just can't grasp it. Any help is greatly appreciated!
/* Rows with exactly one date - always return regardless of when date occurs */
SELECT id, remote_id, type, date
FROM YourTable
GROUP BY remote_id, type
HAVING COUNT(*) = 1
UNION
/* Rows with more than one date - Return Max date <= NOW */
SELECT yt.id, yt.remote_id, yt.type, yt.date
FROM YourTable yt
INNER JOIN (SELECT remote_id, type, max(date) as maxdate
FROM YourTable
WHERE date <= DATE(NOW())
GROUP BY remote_id, type
HAVING COUNT(*) > 1) sq
ON yt.remote_id = sq.remote_id
AND yt.type = sq.type
AND yt.date = sq.maxdate
The group by clause groups all rows that have identical values of one or more columns together and returns one row in the result set for them. If you use aggregate functions (min, max, sum, avg etc.) that will be applied for each "group".
SELECT id, remote_id, type, max(date)
FROM blah
GROUP BY remote_id, date;
I'm not whore where today's date comes in, but assumed that was part of the complex query that you didn't describe and I assume isn't directly relevant to your question here.
Try this:
SELECT a.*
FROM table a INNER JOIN
(
select remote_id, type, MAX(date) date, COUNT(1) cnt from table
group by remote_id, type
) b
WHERE a.remote_id = b.remote_id,
AND a.type = b.type
AND a.date = b.date
AND ( (b.cnt = 1) OR (b.cnt>1 AND b.date <= DATE(NOW())))
Try this
select id, remote_id, type, MAX(date) from table
group by remote_id, type
Hey Carson! You could try using the "distinct" keyword on those two fields, and in a union you can use Count() along with group by and some operators to pull non-unique (greatest and less-than today) records!