Question

I have a column in database:

Serial Number
-------------
S1
S10
...
S2
S11
..
S13

I want to sort and return the result as follows for serial number <= 10 :

S1
S2
S10

One way I tried was:

select Serial_number form table where Serial_Number IN ('S1', 'S2',... 'S10');

This solves the purpose but looking for a better way

Was it helpful?

Solution 2

For Postgres you can use something like this:

select serial_number
from the_table
order by regexp_replace(serial_number, '[^0-9]', '', 'g')::integer;

The regexp_replace will remove all non-numeric characters and the result is treated as a number which is suited for a "proper" sorting.

Edit 1:

You can use the new "number" to limit the result of the query:

select serial_number
from (
  select serial_number, 
         regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
  from the_table
) t 
where snum <= 10
order by snum;

Edit 2

If you receive the error ERROR: invalid input syntax for integer: "" then apparently you have values in the serial_number column which do no follow the format you posted in your question. It means that regexp_replace() remove all characters from the string, so a string like S would cause that.

To prevent that, you need to either exclude those rows from the result using:

where length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0

in the inner select. Or, if you need those rows for some reason, deal with that in the select list:

select serial_number
from (
  select serial_number, 
         case
            when length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0 then regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
            else null -- or 0 whatever you need
         end as snum
  from the_table
) t 
where snum <= 10
order by snum;

This is a really nice example on why you should never mix two different things in a single column. If all your serial numbers have a prefix S you shouldn't store it and put the real number in a real integer (or bigint) column.

Using something like NOT_SET to indicate a missing value is also a bad choice. The NULL value was precisely invented for that reason: to indicate the absence of data.

OTHER TIPS

Here is an easy way for this format:

order by length(Serial_Number),
         Serial_Number

This works because the prefix ('S') is the same length on all the values.

Since only the first character spoils your numeric fun, just trim it with right() and sort by the numeric value:

SELECT *
FROM   tbl
WHERE  right(serial_number, -1)::int < 11
ORDER  BY right(serial_number, -1)::int;

Requires Postgres 9.1 or later. In older versions substitute with substring (x, 10000).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top