Reputation: 2433
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
Upvotes: 0
Views: 835
Reputation: 657357
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)
.
Upvotes: 1
Reputation:
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.
Upvotes: 2
Reputation: 1270181
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.
Upvotes: 3