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
This commit is contained in:
parent
7030ab9a72
commit
a1cbeb9afb
|
@ -143,6 +143,14 @@ class BaseDatabaseOperations(object):
|
||||||
"""
|
"""
|
||||||
return '%s'
|
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):
|
def fulltext_search_sql(self, field_name):
|
||||||
"""
|
"""
|
||||||
Returns the SQL WHERE clause to use in order to perform a full-text
|
Returns the SQL WHERE clause to use in order to perform a full-text
|
||||||
|
|
|
@ -133,6 +133,14 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def drop_foreignkey_sql(self):
|
def drop_foreignkey_sql(self):
|
||||||
return "DROP FOREIGN KEY"
|
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):
|
def fulltext_search_sql(self, field_name):
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
|
|
|
@ -288,6 +288,8 @@ class BaseQuery(object):
|
||||||
if self.group_by:
|
if self.group_by:
|
||||||
grouping = self.get_grouping()
|
grouping = self.get_grouping()
|
||||||
result.append('GROUP BY %s' % ', '.join(grouping))
|
result.append('GROUP BY %s' % ', '.join(grouping))
|
||||||
|
if not ordering:
|
||||||
|
ordering = self.connection.ops.force_no_ordering()
|
||||||
|
|
||||||
if self.having:
|
if self.having:
|
||||||
having, h_params = self.get_having()
|
having, h_params = self.get_having()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import datetime
|
||||||
import pickle
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.query import Q, ITER_CHUNK_SIZE
|
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
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in New Issue