【问题标题】:Find first available identifier from database table从数据库表中查找第一个可用标识符
【发布时间】:2014-05-16 16:10:09
【问题描述】:

要求是找到第一个可用的标识符,其中标识符是字母数字字符串,例如:

ABC10000
ABC10345
ABC88942
ABC90123

数据库表的结构如:

id, user, identifier

请注意,alpha 组件ABC 始终保持一致,不会改变。数字部分应介于 10000 和 99999 之间。

如何最好地解决这个问题?这似乎不是一个过于复杂的问题 - 使用 MySQL 或 SQL 和 PHP 的组合寻找最简单的解决方案。当前的解决方案将数据库中的每条记录拉入一个数组,然后从 10000 开始循环,在前面加上 ABC 并检查可用性,这似乎可以显着改善。


编辑:最初的问题不够清楚,因为已经分配了一定数量的标识符,我正在寻找填补空白。从我提供的短名单中,下一个可用的是 ABC10001。然而,最终会是 ABC10346,然后是 ABC88943,以此类推


编辑:对于一个结构不佳的问题,我们深表歉意。为避免进一步混淆,以下是实际的表结构:

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 ;

【问题讨论】:

  • 总是最后的记录吗?或者您可能会在值之间拥有免费 id,即 abc10000、abc10002 并且您需要 10001?
  • 谢谢 - 我已经在上面澄清了。它是第一个可用的“缺失”标识符。
  • @HerrSerker - 这是一个不好的评论。也许标识符是一个糟糕的选择(实际上,字段名称是value)。如果您愿意详细说明?

标签: php mysql unique identifier alphanumeric


【解决方案1】:

我想到的是一张包含所有标识符的表并使用此 sql

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

【讨论】:

    【解决方案2】:

    从您的编辑中重新考虑这一点,您应该走 PHP 路线并添加另一个表或其他方式来存储最后填充的 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'");
    

    当然,你需要在标识符字段上添加一个UNIQUE索引。

    【讨论】:

    • id 字段是自动增量的,实际上每次都会增加 4 - 显然是为了帮助复制,但这不是重点。您的回答很有趣 - 但我不同意为此使用记录 ID。表结构不会改变 - 我的问题是如何使用当前结构、数据和限制来查找丢失的标识符。
    【解决方案3】:

    您必须自行加入表并在加入表中查找第一个 NULL 值

    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


    编辑

    关于 ncatnow 的一些“专业”的评论。在子选择的帮助下,可以进行一些细微的调整,以消除 'ABC'UNION 的默认值

    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

    【讨论】:

    • 这看起来很接近,但没有返回正确的结果——我收到了ABC1。它使用主键而不是 identifier 字段。这个小提琴更好地代表了这个问题:sqlfiddle.com/#!2/d69105/1
    • @ncatnow 我不知道你的表的确切结构,所以我做了一个例子。您必须根据自己的需要采用它。
    • 小问题是,如果您有 ABC0000,它将切断 ABC 以获得 00000,并认为添加到时将转换为整数 0,得到 1,然后将其连接到给ABC1。
    • @Kickstart 是的,但 OP 确实说过,标识符数字不以零开头。
    • @HerrSerker - 它不起作用。看到这个小提琴 - sqlfiddle.com/#!2/d69105/2。它应该返回 ABC10002。 Kickstart 似乎解释了它不起作用的原因
    【解决方案4】:

    与@HerrSerker 的上述回复类似,但这将处理现有的标识符,其数字部分以零开头。

    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
    

    【讨论】:

    • 我收到#1267 - Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8_general_ci,IMPLICIT) for operation '='
    • 可能需要查看您的表为此声明,但您可能需要强制对连接进行排序。
    • 强制对连接进行排序 - 它返回 ABC00001。接近了!
    • 在您的 SQL fiddle 上尝试过(即使没有强制排序,只需更改列名和表名以匹配)并返回 ABC10002。奇怪
    • 如果你愿意发布链接,我会看看 - 我可能做错了什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    • 2014-02-01
    • 2016-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多