Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

If an SQL query doesn't specify any ordering, avoid the implicit sort

that happens with MySQL when a "GROUP BY" clause is included. This is a
backend-specific operation, so any other databases requiring similar
encouragement can have a function added to their own backend code.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@9637 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a1cbeb9afbbf0f16e1ffa1891575fbc2c3edae74 1 parent 7030ab9
Malcolm Tredinnick malcolmt authored
8 django/db/backends/__init__.py
View
@@ -143,6 +143,14 @@ def field_cast_sql(self, db_type):
"""
return '%s'
+ def force_no_ordering(self):
+ """
+ Returns a list used in the "ORDER BY" clause to force no ordering at
+ all. Returning an empty list means that nothing will be included in the
+ ordering.
+ """
+ return []
+
def fulltext_search_sql(self, field_name):
"""
Returns the SQL WHERE clause to use in order to perform a full-text
8 django/db/backends/mysql/base.py
View
@@ -133,6 +133,14 @@ def date_trunc_sql(self, lookup_type, field_name):
def drop_foreignkey_sql(self):
return "DROP FOREIGN KEY"
+ def force_no_ordering(self):
+ """
+ "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped
+ columns. If no ordering would otherwise be applied, we don't want any
+ implicit sorting going on.
+ """
+ return ["NULL"]
+
def fulltext_search_sql(self, field_name):
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
2  django/db/models/sql/query.py
View
@@ -288,6 +288,8 @@ def as_sql(self, with_limits=True, with_col_aliases=False):
if self.group_by:
grouping = self.get_grouping()
result.append('GROUP BY %s' % ', '.join(grouping))
+ if not ordering:
+ ordering = self.connection.ops.force_no_ordering()
if self.having:
having, h_params = self.get_having()
18 tests/regressiontests/queries/models.py
View
@@ -6,6 +6,7 @@
import pickle
import sys
+from django.conf import settings
from django.db import models
from django.db.models.query import Q, ITER_CHUNK_SIZE
@@ -1053,3 +1054,20 @@ class PointerB(models.Model):
[]
"""
+
+if settings.DATABASE_ENGINE == "mysql":
+ __test__["API_TESTS"] += """
+When grouping without specifying ordering, we add an explicit "ORDER BY NULL"
+portion in MySQL to prevent unnecessary sorting.
+
+>>> query = Tag.objects.values_list('parent_id', flat=True).order_by().query
+>>> query.group_by = ['parent_id']
+>>> sql = query.as_sql()[0]
+>>> fragment = "ORDER BY "
+>>> pos = sql.find(fragment)
+>>> sql.find(fragment, pos + 1) == -1
+True
+>>> sql.find("NULL", pos + len(fragment)) == pos + len(fragment)
+True
+
+"""
Please sign in to comment.
Something went wrong with that request. Please try again.