Reputation: 6491
I have a table in by database that has a name
field with a non-null and unique constraint.
On the frontend, a user can clone certain entities. In this scenario, the name
field gets suffixed with a version number.
For instance, if record A exists with the name TEST_NAME
, cloning this record would result in record B being created with the name TEST_NAME [2]
. Cloning record A again would result in a record C with the name TEST_NAME [3]
.
To determine the version number, I run a count(*)
against the table, returning the number of records that match the root name, in this case: 'TEST_NAME'
.
Here is the query:
SELECT COUNT(*)
FROM my_table
WHERE name LIKE 'TEST_NAME%'
The issue here, is that if a user changes the name of record C to TEST_NAME [3]abc
, then the above query would still pick this up and create record D with the name TEST_NAME [4]
, instead of TEST_NAME [3]abc [2]
.
How can I avoid this? I'd like to only match name
values that follow the format ^TEST_NAME [x]$
, where x
is any integer.
Upvotes: 2
Views: 207
Reputation: 3210
PostgreSQL has support for regexes.
For instance, I guess what you need is
SELECT COUNT(*)
FROM my_table
WHERE name ~ '^TEST_NAME \[[0-9]+\]$'
For computing the next version, I propose the following :
SELECT
version,
COALESCE(
matches[1] || ' [' || matches[2]::int + 1 || ']',
matches[3] || ' [2]'
) AS nextVersion
FROM versions
CROSS JOIN LATERAL (
SELECT regexp_matches(version, '^(.*) \[([0-9]+)\]$|^(.*)$') matches
) t
Here is what's going on :
For each version, we match the regexp ^(.)[([0-9]+)]$|^(.)$. Groups 1 and 2 will be populated if the version ends with a version number. Group 3 always contains the whole name. We put this result in the lateral table t(matches).
If group 1 and 2 have values, then "matches[1] || ' [' || matches[2]::int + 1 || ']'" is the next version, otherwise pick matches[3] and add [2] to it.
As a bonus, the following query will give the last version for every rootname, and the next version available.
SELECT rootname, MAX(t2.version) AS lastVersion, MAX(t2.version) + 1 AS nextVersion
FROM versions
CROSS JOIN LATERAL (
SELECT regexp_matches(version, '^(.*) \[([0-9]+)\]$|^(.*)$') matches
) t1
CROSS JOIN LATERAL (
SELECT
COALESCE(matches[1], matches[3]) AS rootname,
COALESCE(matches[2]::int, 1) AS version
) t2
GROUP BY rootname;
If you just have a rootname (say TEST_NAME), and assuming you only have one column version in your table which would be called versions, you can clone the record using :
INSERT INTO versions
SELECT rootname || ' [' || MAX(t2.version) + 1 || ']'
FROM versions
CROSS JOIN LATERAL (
SELECT regexp_matches(version, '^(.*) \[([0-9]+)\]$|^(.*)$') matches
) t1
CROSS JOIN LATERAL (
SELECT
COALESCE(matches[1], matches[3]) AS rootname,
COALESCE(matches[2]::int, 1) AS version
) t2
WHERE rootname = 'TEST_NAME';
Upvotes: 2