Order of NULL in SQL result
This article explains the order of the NULL value in the ORDER BY
clause in sql, with reference to the official document for each dialect.
The default behavior
The following SQL dialects consider NULL as the largest value (the last position in ascending order of any value type): PostgreSQL, Oracle.
In contrast, these SQL dialects treat the NULL value as smallest (the first value in ascending order of any value type): SQLite, MySQL, SQL Server.
The official document
By default, null values sort as if larger than any non-null value
Oracle document: (search for the following paragraph)
If the null ordering is not specified then the handling of the null values is:
* NULLS LAST if the sort is ASC
* NULLS FIRST if the sort is DESC
SQLite considers NULL values to be smaller than any other values for sorting purposes
When doing anORDER BY
,NULL
values are presented first if you doORDER BY ... ASC
and last if you doORDER BY ... DESC
.
Null values are treated as the lowest possible values.
Change the default behavior
PostgreSQL, Oracle, SQLite support modifying the order of the NULL value in the ORDER BY clause by adding NULLS FIRST/LAST
to the order by clause.
NULLS FIRST/LAST
specifies the order of NULL values regardless of the sorting order.
For example: with ORDER BY "subscribedAt" ASC NULLS FIRST
/ORDER BY "subscribedAt" DESC NULLS FIRST
, record with subscribedAt
being NULL always come first.
Note: currently, MySQL, SQL Server do not support this feature.
Workaround with MySQL, SQL Server
There are various ways to achieve the required sorting order.
With reverse operator
The trick: minus of NULL is still a NULL, NOT
of NULl is still a NULL.
For values of types that can be applied with the reverse operator such as number, date, or boolean, reserving the order (DESC to ASC, vice versa), and adding the reverse operator to the sorted value swap NULL values' order.
For example, change ORDER BY cnt ASC
to ORDER BY -cnt DESC
.
Change ORDER BY "isSubscribed" DESC
to ORDER BY NOT "isSubscribed" ASC
.
Sort the NULL value first
Use multiple sorts to sort the NULL value first.
For example, ORDER BY cnt IS NULL DESC, cnt DESC
.
Note: cnt IS NULL
is simply a boolean value. For boolean values, True/False is equivalent to 1/0, respectively when sorting.
Use COALESCE function
COALESCE(val_1, val_2, ..., val_n)
returns the first non-null value. With COALESCE, you can provide a default value if the sorted expression is NULL.
For example, ORDER BY COALESCE(cnt, 0) DESC
.
Note: This workaround is very different for all the above-mentioned ways, it is even different from the NULLS LAST/FIRST
clause. Because it treats NULL and the default value in the same order, while in other methods, the null value always has a strictly higher or lower value than any other value.
Simpler approaches in specific use cases
Similar to the effect of the method using the COALESCE function, in some cases, you may want to make the NULL value have the same order with some default value.
If the value's type is boolean, you can simply use ORDER BY "isSubscribed" IS TRUE DESC
. This will make False
and NULL
same order.
If you need a more specific complex logic, you can replace the expression in the order by clause. For example: ORDER BY (CASE WHEN val IS NULL then 1 WHEN val IS 1 THEN 2 ELSE 3) DESC
(parenthesis are optional).
If you need exactly this strict order true, null, false
, then use ORDER BY COALESCE(val::INTEGER * 2, 1) DESC
.
Knex usage
Because knex supports many dialects (including MySQl, SQL Server), it does not support NULLS LAST/FIRST
clause natively.
.orderBy('col', 'asc', 'first')
generates ORDER BY col IS NULL ASC
.
To mimic the NULL priority clause, you need to use multiple order by:
queryBuilder.orderBy([
{column: 'col', order: 'ASC', nulls: 'first'},
{column: 'col', order: 'ASC'}
])