Search code examples
postgresqlpeewee

Peewee: how to do ORDER BY ... NULLS (FIRST|LAST)?


The title pretty much sums it up: I would like to specify whether nulls appear first or last in my results. In my specific case, I want to get nulls last with a descending sort order, corresponding to SQL like this:

SELECT id, something, something_else
FROM mytable
ORDER BY something DESC NULLS FIRST;

This is the PostgreSQL syntax (which is what I'm using for this project). The same problem could apply with other databases (though I don't think MySQL supports that syntax, not sure if it even supports the feature). Of course having nulls first is default for ascending order (see PostgreSQL docs), so only the combinations DESC NULLS FIRST and ASC NULLS LAST are likely to be useful...except that, conveniently, the MySQL default is precisely the opposite.

It seems like I should be able to do one of these:

MyModel.select().order_by(MyModel.something.desc().nulls_first())
MyModel.select().order_by(MyModel.something.desc(nulls_first))

I'm no Python guru but I couldn't find anything in the Peewee documentation or source code that looks like it would do what I want.

Of course there may be many workarounds. Here are the ones I found or thought of, in decreasing order of practicality (for me):

  • using raw SQL (which could actually work in my case, but isn't so great if I need the objects later)
  • faking it by ordering by something else (such as a function, which I'm not sure how to do with Peewee either, or adding a derived column along the lines of this)
  • maybe using UNION to combine a query having WHERE ?? IS NOT NULL with one having WHERE ?? IS NULL ... but that seems problematic on several fronts, such as I don't know if Peewee can do that either, and it might still be best to do ORDER BY on the whole thing anyway
  • running two separate queries and putting their result sets together in Python ... again, sounds like trouble I think (extra overhead, need 1 transaction for data integrity)
  • redesign the schema so I don't have to order by something that could be null (maybe someday...)

So: can Peewee do this? If not, is there a better workaround than I've thought of?


Solution

  • I think you're over-complicating the issue, to be honest.

    As of Peewee 3.x, you can specify the handling of nulls:

    MyModel.select().order_by(MyModel.something.desc(nulls='LAST'))
    

    You can also use a case statement to create an aliased column containing a 1 or 0 to indicate whether the column you're sorting on is null. Then use that alias in the order by.