Bug #14016 ORDER BY DATE_FORMAT() and uppercase/lowercase DATE_FORMAT argument pairs
Submitted: 13 Oct 2005 21:28 Modified: 19 Jan 2006 23:28
Reporter: Geoff Traugott
Status: Closed
Category:Server: Optimizer Severity:S3 (Non-critical)
Version:4.1.14-standard/5.0 BK source OS:Linux (Linux (x86, glibc-2.3))
Assigned to: Evgeny Potemkin Target Version:

[13 Oct 2005 21:28] Geoff Traugott
Description:
When a SELECT query includes DATE_FORMAT(field, '%M'), and ORDER BY DATE_FORMAT(field,
'%m'), the result is sorted by the '%M' value instead of '%m'.

Likewise, if the SELECT query includes DATE_FORMAT(field, '%m') and ORDER BY
DATE_FORMAT(field, '%M'), it is sorted by the '%m' value instead of '%M'.

Further, if the SELECT contains multiple DATE_FORMAT functions referencing '%m' and '%M',
and is ORDER BY DATE_FORMAT(field, '%m'), it will sort the results by whichever reference
to month appears first in the SELECT portion of the query.

This applies to other uppercase/lowercase DATE_FORMAT argument pairs, such as '%w' and
'%W'

Examples:

mysql> SELECT * FROM tblDateSort;
+---------------------+
| dtmDate             |
+---------------------+
| 2005-01-05 00:00:00 |
| 2005-02-06 00:00:00 |
| 2005-03-22 00:00:00 |
| 2005-04-11 00:00:00 |
| 2005-05-01 00:00:00 |
| 2005-06-08 00:00:00 |
| 2005-07-07 00:00:00 |
| 2005-08-29 00:00:00 |
| 2005-09-20 00:00:00 |
| 2005-10-03 00:00:00 |
| 2005-11-14 00:00:00 |
| 2005-12-25 00:00:00 |
+---------------------+
12 rows in set (0.00 sec)

mysql> SELECT DATE_FORMAT(dtmDate, "%M") FROM tblDateSort ORDER BY DATE_FORMAT(dtmDate,
"%m");
+----------------------------+
| DATE_FORMAT(dtmDate, "%M") |
+----------------------------+
| April                      |
| August                     |
| December                   |
| February                   |
| January                    |
| July                       |
| June                       |
| March                      |
| May                        |
| November                   |
| October                    |
| September                  |
+----------------------------+
12 rows in set (0.00 sec)

mysql> select date_format(dtmDate, "%m") from tblDateSort order by date_format(dtmDate,
"%M");
+----------------------------+
| date_format(dtmDate, "%m") |
+----------------------------+
| 01                         |
| 02                         |
| 03                         |
| 04                         |
| 05                         |
| 06                         |
| 07                         |
| 08                         |
| 09                         |
| 10                         |
| 11                         |
| 12                         |
+----------------------------+
12 rows in set (0.00 sec)

mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as Date2
from tblDateSort order by date_format(dtmDate, "%M");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 01    | January   |
| 02    | February  |
| 03    | March     |
| 04    | April     |
| 05    | May       |
| 06    | June      |
| 07    | July      |
| 08    | August    |
| 09    | September |
| 10    | October   |
| 11    | November  |
| 12    | December  |
+-------+-----------+
12 rows in set (0.00 sec)

Switiching the field order in the SELECT changes the ORDER BY behavior:

mysql> select date_format(dtmDate, "%M") as Date1, date_format(dtmDate, "%m") as Date2
from tblDateSort order by date_format(dtmDate, "%M");
+-----------+-------+
| Date1     | Date2 |
+-----------+-------+
| April     | 04    |
| August    | 08    |
| December  | 12    |
| February  | 02    |
| January   | 01    |
| July      | 07    |
| June      | 06    |
| March     | 03    |
| May       | 05    |
| November  | 11    |
| October   | 10    |
| September | 09    |
+-----------+-------+
12 rows in set (0.00 sec)

Additionally, this behavior is seen with other uppercase/lowercase DATE_FORMAT argument
pairs, such as '%w' with '%W'

mysql> select date_format(dtmDate, "%w") as Date1, date_format(dtmDate, "%W") as Date2
from tblDateSort order by date_format(dtmDate, "%W");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 0     | Sunday    |
| 0     | Sunday    |
| 0     | Sunday    |
| 1     | Monday    |
| 1     | Monday    |
| 1     | Monday    |
| 1     | Monday    |
| 2     | Tuesday   |
| 2     | Tuesday   |
| 3     | Wednesday |
| 3     | Wednesday |
| 4     | Thursday  |
+-------+-----------+
12 rows in set (0.00 sec)

(Thursday should be before Tuesday alphabetically)

This behavior can be reproduced on MySQL server versions 4.1.10a and 4.1.14.

This bug does not exist on MySQL server version 4.0.23. An example of the same query on
MySQL 4.0.23:

mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as Date2
from tblDateSort order by date_format(dtmDate, "%M");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 04    | April     |
| 08    | August    |
| 12    | December  |
| 02    | February  |
| 01    | January   |
| 07    | July      |
| 06    | June      |
| 03    | March     |
| 05    | May       |
| 11    | November  |
| 10    | October   |
| 09    | September |
+-------+-----------+
12 rows in set (0.00 sec)

How to repeat:
mysql> create database test;
mysql> use test;
mysql> CREATE TABLE tblDateSort (dtmDate DATETIME);
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-01-05");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-02-06");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-03-22");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-04-11");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-05-1");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-06-08");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-07-07");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-08-29");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-09-20");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-10-03");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-11-14");
mysql> INSERT INTO tblDateSort (dtmDate) VALUES ("2005-12-25");
mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as Date2
from tblDateSort order by date_format(dtmDate, "%M");

Suggested fix:
Use month() or another appropriate function in ORDER BY.
[14 Oct 2005 0:45] Miguel Solorzano
Thank you for the bug report.

mysql> select version();
+------------------+
| version()        |
+------------------+
| 4.1.16-debug-log |
+------------------+
1 row in set (0.00 sec)

mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as
    -> Date2 from tblDateSort order by date_format(dtmDate, "%M");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 01    | January   |
| 02    | February  |
| 03    | March     |
| 04    | April     |
| 05    | May       |
| 06    | June      |
| 07    | July      |
| 08    | August    |
| 09    | September |
| 10    | October   |
| 11    | November  |
| 12    | December  |
+-------+-----------+
12 rows in set (0.00 sec)

-----------------------------------------------------------------------------------
mysql> select version();
+-----------------+
| version()       |
+-----------------+
| 5.0.15-rc-debug |
+-----------------+
1 row in set (0.00 sec)

mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as
    -> Date2 from tblDateSort order by date_format(dtmDate, "%M");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 01    | January   |
| 02    | February  |
| 03    | March     |
| 04    | April     |
| 05    | May       |
| 06    | June      |
| 07    | July      |
| 08    | August    |
| 09    | September |
| 10    | October   |
| 11    | November  |
| 12    | December  |
+-------+-----------+
12 rows in set (0.00 sec)
-----------------------------------------------------------------------------------

mysql> select version();
+------------------+
| version()        |
+------------------+
| 4.0.26-debug-log |
+------------------+
1 row in set (0.00 sec)

mysql> select date_format(dtmDate, "%m") as Date1, date_format(dtmDate, "%M") as
    -> Date2 from tblDateSort order by date_format(dtmDate, "%M");
+-------+-----------+
| Date1 | Date2     |
+-------+-----------+
| 04    | April     |
| 08    | August    |
| 12    | December  |
| 02    | February  |
| 01    | January   |
| 07    | July      |
| 06    | June      |
| 03    | March     |
| 05    | May       |
| 11    | November  |
| 10    | October   |
| 09    | September |
+-------+-----------+
12 rows in set (0.00 sec)
[25 Oct 2005 18:38] Evgeny Potemkin
By default constant strings in second parameter of date_time() have case
insensitive collation. Because of this expressions date_format(f,'%m') and 
date_format(f,'%M') wrongly becomes equal, which results in choosing wrong 
column to sort by.

Fixed in 4.1.16, cset  1.2462.2.1
[28 Oct 2005 16:08] Evgeny Potemkin
Fixed in 5.0.16
[19 Jan 2006 23:28] Mike Hillyer
Added to 4.1.16 and 5.0.16 changelogs:

      <listitem>
        <para>
          When the <function>DATE_FORMAT()</function> function appeared
          in both the <literal>SELECT</literal> and <literal>ORDER
            BY</literal> clauses of a query but with arguments that
          differ by case (i.e. %m and %M), incorrect sorting may have
          occurred.
          (Bug #14016)
        </para>
      </listitem>