diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-12-10 05:19:27 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-12-10 05:19:27 +0000 |
| commit | a1cbeb9afbbf0f16e1ffa1891575fbc2c3edae74 (patch) | |
| tree | c5741e275b870b9d5decd0f5ab716497b9305a2c | |
| parent | 7030ab9a72ea590b359c22644e45edaf05dd8b5c (diff) | |
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
| -rw-r--r-- | django/db/backends/__init__.py | 8 | ||||
| -rw-r--r-- | django/db/backends/mysql/base.py | 8 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 2 | ||||
| -rw-r--r-- | tests/regressiontests/queries/models.py | 18 |
4 files changed, 36 insertions, 0 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 353eb39488..736667d2ff 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -143,6 +143,14 @@ class BaseDatabaseOperations(object): """ 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 diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 2fb1988f7a..f6eccb94b7 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -133,6 +133,14 @@ class DatabaseOperations(BaseDatabaseOperations): 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 diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index e30deee29b..8c5ef3355e 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -288,6 +288,8 @@ class BaseQuery(object): 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() diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 868200f60c..d8737df197 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -6,6 +6,7 @@ import datetime 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 @@ FieldError: Infinite loop caused by ordering. [] """ + +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 + +""" |
