Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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
@malcolmt malcolmt authored
View
8 django/db/backends/__init__.py
@@ -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
View
8 django/db/backends/mysql/base.py
@@ -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
View
2  django/db/models/sql/query.py
@@ -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()
View
18 tests/regressiontests/queries/models.py
@@ -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.