Opened 6 years ago

Last modified 5 weeks ago

#13312 new New feature

Add a way to customize the order_by() of null fields

Reported by: binary Owned by: nobody
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords: ordering, order_by, null
Cc: contact@…, serhiy.int@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Applying order_by on null=True fields gives different results on mysql and pgsql: pgsql thinks that NULL values are bigger than any and mysql that smaller. I think that this must be unified on all engines for prevent problems with portability. pgsql has additional parameter for this:

SELECT select_list
    FROM table_expression
    ORDER BY sort_expression1 [ASC | DESC] [NULLS { FIRST | LAST }]
             [, sort_expression2 [ASC | DESC] [NULLS { FIRST | LAST }] ...]

But I don't know how it on other engines.

Change History (10)

comment:1 Changed 6 years ago by ubernostrum

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to wontfix
  • Status changed from new to closed

I think this is one of those cases where we really just have to tell people to know the behavior of the DB they choose to use; NULL-handling in all its many forms is one of those things that's just not worth the time and complexity of abstraction.

comment:2 Changed 6 years ago by binary

that's ok, but there is no way to control this behavior. i mean that i can't tell pgsql that i want nulls to be less than others in any good way. :(

comment:3 Changed 6 years ago by chexum

  • Keywords null added

Just to document it, there is a very easy way to control this behavior. The most portable seems to be an additional order by for "field IS NULL". It puts NULLs last in at least sqlite3, mysql and PostgreSQL. The reverse is either "field IS NULL DESC" or "field IS NOT NULL".

It's also easy to use in a Django way:

    q = q.extra(select={'null1':'field1 is null','null2':'field2 is null'})
    q = q.extra(order_by=['null1','-field1','-null2','field2'])

Because it's so easy to do in a standard way, it might be worth at some point to add a specific queryset for it (not necessarily modifying order_by()), as extra() is not always desirable. Having a new queryset would allow backend specific optimization, like nulls last/first if supported.

comment:4 Changed 5 years ago by daonb

thanks chexum

comment:5 Changed 3 months ago by blueyed

  • Easy pickings unset
  • Severity set to Normal
  • Type set to Uncategorized
  • UI/UX unset

See http://stackoverflow.com/a/35494930/15690, which uses a specilaized QuerySet/Query.

The extra() approach is deprecated, and won't work with foreign keys / joined tables.

Last edited 3 months ago by blueyed (previous) (diff)

comment:6 Changed 3 months ago by jarshwah

  • Resolution wontfix deleted
  • Status changed from closed to new

Now that we have expressions, I think it's fine to reopen this ticket. We can introduce a OrderNulls.last()/.first() expression to handle this in a standard way. We could also just build it right into the OrderBy expression:

qs.order_by(F('my_field').asc().nullslast())

If we do build this into the existing order_by, you'll need to be careful about preserving a 'feature' that stops django from inserting duplicate ordering clauses (it builds each component then does a string compare to ensure the new clause isn't a duplicate).

This won't normalise order by nulls across backends, but it'll allow users to control null handling semantics if they have a use for it. NULL handling was originally going to be added to the OrderBy expression, but I ran out of time before that could happen.

comment:7 Changed 3 months ago by timgraham

  • Summary changed from order_by on null-field gives different results on different db engines to Add a way to customize the order_by() of null fields
  • Triage Stage changed from Unreviewed to Accepted
  • Type changed from Uncategorized to New feature
  • Version changed from 1.1 to master

comment:8 Changed 8 weeks ago by ionelmc

  • Cc contact@… added

comment:9 Changed 5 weeks ago by int-ua

  • Cc serhiy.int@… added

comment:10 Changed 5 weeks ago by int-ua

AFAIU, it's possible in all main supported engines:

ORDER BY IF(ISNULL(my_field),1,0),my_field in MySQL
[NULLS { FIRST | LAST }] in both PostgreSQL and Oracle
ORDER BY col1 IS NULL in SqLite3

Note: See TracTickets for help on using tickets.
Back to Top