86

I found this some time ago and have been using it since; however, looking at it today, I realized that I do not fully understand why it works. Can someone shed some light on it for me?

ORDER BY  s.type!= 'Nails',
          s.type!= 'Bolts',
          s.type!= 'Washers',
          s.type!= 'Screws',
          s.type!= 'Staples',
          s.type!= 'Nuts', ...

If I order by s.type, it orders alphabetically. If I use the example above it uses the same order as the line positions. What I don't understand is the use of !=. If I use = it appears in the opposite order. I cannot wrap my head around the concept of this.

It would reason to me that using = in place of the !='s above would place Nails first in position, but it does not, it place it in the last. I guess my question is this: Why do i have to use !=, not = in this situation?

Evil Elf
  • 2,157
  • 3
  • 22
  • 28
  • 16
    So, if I don't understand something, even though I like the elegance and simplicity of a solution, I should hold my hands over my ears and repeat "La, la, la". I don't like this approach; I would rather learn something new and become comfortable with it. – Evil Elf Mar 02 '11 at 15:30
  • wow, neat hack! :) – Anentropic Sep 12 '18 at 21:13

5 Answers5

86

@Scott Bailey suggested great idea. But it can be even simpler (you don't have to create custom function) since PostgreSQL 9.5. Just use array_position function:

ORDER BY array_position(array['Nails','Bolts','Washers','Screws','Staples','Nuts'], s.type)
Community
  • 1
  • 1
Daniil Ryzhkov
  • 7,416
  • 2
  • 41
  • 58
63

Each expression gets evaluated as a bool and treated as 0 for false and 1 for true and sorted appropriately. Even though this works, the logic is hard to follow (and thus maintain). What I use is a function that finds a value's index in an array.

ORDER BY idx(array['Nails','Bolts','Washers','Screws','Staples','Nuts'], s.type)

This is much easier to follow. Nails will be sorted first and nuts sorted last. You can see how to create the idx function in the Postgres snippets repository. http://wiki.postgresql.org/wiki/Array_Index

Scott Bailey
  • 7,748
  • 2
  • 23
  • 21
50

I've never seen it but it seems to make sense.

At first it orders by s.type != 'Nails'. This is false for every row that contains Nails in the type column. After that it is sorted by Bolts. Again for all columns that do contain Bolts as a type this evaluates to false. And so on.

A small test reveals that false is ordered before true. So you have the following: First you get all rows with Nails on top because the according ORDER BY evaluated to false and false comes first. The remaining rows are sorted by the second ORDER BY criterion. And so on.

 type     | != Nails | != Bolts | != Washers
'Nails'   | false    | true     | true
'Bolts'   | true     | false    | true
'Washers' | true     | true     | false
musiKk
  • 14,751
  • 4
  • 55
  • 82
  • 4
    This is purely a thought. But I'd assume that it orders false before true, because of their respected integer values of 0 and 1 where 0 (false) comes before 1 (true). – Michael Lynch Aug 19 '13 at 17:53
  • It worked amazingly for me across every SQL server I threw it at (PostgreSQL, MySQL and SQLite)!! – Theodore R. Smith Sep 28 '18 at 17:56
12

with array_position, it needs to have the same type that you're querying against.

e.g:

select array_position(array['foo'::char,'bar','baz'::char], 'bar');
select array_position(array['foo'::char,'bar','baz'::char], 'baz'::char);
lifeiscontent
  • 593
  • 5
  • 18
3

This is to sort by a given list of values.

Since Postgres 9.4, a clean solution is to LEFT JOIN to an unnested array WITH ORDINALITY and ORDER BY the resulting ordinality number:

SELECT ...
FROM   ...
LEFT   JOIN unnest ('{Nails,Bolts,Washers,Screws,Staples,Nuts}'::text[]) WITH ORDINALITY sort(type, ord) USING (type)
ORDER  BY sort.ord ...

fiddle

See:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228