I have two columns, id integer and version text. I am trying to convert the strings in version into integers so that I may select the maximum (most recent) version of the id.
However, the the first instance of the id stores itself as the version. Example:
id | version
---+--------
10 | '10'
as opposed to:
id | version
---+--------
10 | '10-0'
Additional rows follow the convention id: 10, version: 10-1. Etc.
How can I accomplish this? I have tried split_part() and cast as int. However, split_part(version, "-", 2) will return what looks like an empty string. I have tried running this using a COALESCE(splitpart..., '0') to no avail as it tried to read the empty field returned by the field index 2.
split_part() returns the empty string ('') - not NULL - when the part to be returned is empty or non-existent. That's why COALESCE does nothing here. And the empty string ('') has no representation as integer value, hence it throws an error when trying to cast it.
The shortest way in this example should be GREATEST(split_part( ... ) , '0') before casting, since the empty string sorts before any other non-empty string or even NULL (in any locale). Then use DISTINCT ON () to get the row with the "biggest" version for each id.
CREATE TABLE tbl (
id integer NOT NULL
, version text NOT NULL
);
INSERT INTO tbl VALUES
(10, '10-2')
, (10, '10-1')
, (10, '10') -- missing subversion
, (10, '10-111') -- multi-digit number
, (11, '11-1')
, (11, '11-0') -- proper '0'
, (11, '11-') -- missing subversion but trailing '-'
, (11, '11-2');
SELECT DISTINCT ON (id) *
FROM tbl
ORDER BY id, GREATEST(split_part(version, '-', 2), '0')::int DESC;
Result:
id | version
----+---------
10 | 10-111
11 | 10-2
Or you could also use NULLIF and use NULLS LAST (in descending order) to sort:
SELECT DISTINCT ON (id) *
FROM tbl
ORDER BY id, NULLIF(split_part(version, '-', 2), '')::int DESC NULLS LAST;
Same result.
Or a more explicit CASE statement:
CASE WHEN split_part(version, '-', 2) = '' THEN '0' ELSE split_part(version, '-', 2) END
dbfiddle here
Related:
Use the combination of coalesce() and nullif(), example:
with my_table(version) as (
values
('10'), ('10-1'), ('10-2')
)
select
version,
split_part(version, '-', 1)::int as major,
coalesce(nullif(split_part(version, '-', 2), ''), '0')::int as minor
from my_table
version | major | minor
---------+-------+-------
10 | 10 | 0
10-1 | 10 | 1
10-2 | 10 | 2
(3 rows)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With