Question

The requirement is to find the first available identifier where an identifier is an alphanumeric string, such as:

ABC10000
ABC10345
ABC88942
ABC90123

The database table has a structure such as:

id, user, identifier

Note that the alpha component ABC is consistent throughout and won't change. The numeric component should be between 10000 and 99999.

How best to tackle this? It does not seem like an overly complex problem - looking for simplest solution using either just MySQL or a combination of SQL and PHP. The current solution pulls each record from the database into an array and then loops from 10000 onwards, prepending ABC and checking availability, which seems like it could be improved significantly.


Edit: Original question was not clear enough in that a certain amount of identifiers have been assigned already, and I am looking to fill in the gaps. From the short list I provided, the next available would be ABC10001. Eventually, however, it would be ABC10346 and then ABC88943 and so on


Edit: Sorry for a poorly structured question. To avoid any further confusion, here is the actual table structure:

CREATE TABLE IF NOT EXISTS `User_Loc` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user` int(11) DEFAULT NULL,
  `value` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQ_64FB41DA17323CBC` (`user`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4028 ;
Was it helpful?

Solution

You have to self join the table and look for the first NULL value in the joined table

SELECT CONCAT('ABC', SUBSTRING(t1.value, 4)+1) AS next_value
FROM test t1
LEFT JOIN test t2 on SUBSTRING(t1.value, 4)+1 = SUBSTRING(t2.value, 4)
WHERE ISNULL(t2.value)
ORDER BY t1.value ASC
LIMIT 1

http://sqlfiddle.com/#!2/d69105/22


edit

With the comment about some 'specialities' at ncatnow. There are slight adjusments to make with the help of subselects for ridding the 'ABC' and UNION for having a default value

SELECT 
  CONCAT('ABC', t1.value+1) AS next_value

FROM 
((SELECT '09999' AS value) UNION (SELECT SUBSTRING(value, 4) AS value FROM test)) t1
LEFT JOIN 
((SELECT '09999' AS value) UNION (SELECT SUBSTRING(value, 4) AS value FROM test)) t2 
  ON t1.value+1 = t2.value

WHERE 
  ISNULL(t2.value) 
  AND t1.value >= '09999'

ORDER BY t1.value ASC
LIMIT 1

http://sqlfiddle.com/#!2/28acf6/50

OTHER TIPS

Similar to the above reply by @HerrSerker, but this will cope with existing identifiers which have the numeric part starting with a zero.

SELECT CONCAT('ABC',SUBSTRING(CONCAT('00000', CAST((CAST(SUBSTRING(a.identifier, 4) AS SIGNED) + 1) AS CHAR)), -5)) AS NextVal
FROM SomeTable a
LEFT OUTER JOIN SomeTable b
ON b.identifier = CONCAT('ABC',SUBSTRING(CONCAT('00000', CAST((CAST(SUBSTRING(a.identifier, 4) AS SIGNED) + 1) AS CHAR)), -5))
WHERE b.identifier IS NULL
ORDER BY NextVal
LIMIT 1

what comes to my mind is one table with all indentifiers and use this sql

SELECT identifier FROM allIdentifiersTable WHERE identifier NOT IN (SELECT identifier FROM yourTable) LIMIT 1

Reconsidering this from your edit, you should go the PHP route and add another table or other means to store the last filled id:

$identifier = 0;
$i = mysql_query("SELECT identifier FROM last_identifier");
if ($row = mysql_fetch_assoc($i)) $identifier = $row["identifier"];
if ($identifier < 10000) $identifier = 10000;

do {
    $identifier += 1;
    $result = mysql_query("
         INSERT IGNORE INTO table (id, user, identifier)
         VALUES ('[...]', '[...]',
         'ABC" . str_pad($identifier, 5, "0", STR_PAD_LEFT) . "'
    )");
    if (mysql_affected_rows($result) < 1) continue;
} while (false);

mysql_query("UPDATE last_identifier SET identifier = '$identifier'");

Of course, you need to add a UNIQUE index on the identifier field.

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