2

我有一行是具有唯一约束的 varchar(50),我想获取新插入但具有给定前缀的下一个唯一编号。

我的行可能如下所示:

ID (varchar)
00010001
00010002
00010003
00080001

因此,如果我想从前缀“0001”中获取下一个 unqiue 号码,它将是“00010004”,但如果我想要它作为前缀“0008”,它将是“00080002”。

此表中将有超过 1 万个条目。Oracle 11 有没有办法以相当快的速度执行这种操作?

我知道这个设置非常疯狂,但这是我必须使用的。我无法创建任何新表等。

4

3 回答 3

5

您可以搜索指定前缀的最大值并将其递增:

SQL> WITH DATA AS (
  2     SELECT '00010001' id FROM DUAL UNION ALL
  3     SELECT '00010002' id FROM DUAL UNION ALL
  4     SELECT '00010003' id FROM DUAL UNION ALL
  5     SELECT '00080001' id FROM DUAL
  6  )
  7  SELECT :prefix || to_char(MAX(to_number(substr(id, 5)))+1, 'fm0000') nextval
  8    FROM DATA
  9   WHERE ID LIKE :prefix || '%';

NEXTVAL
---------
00010004

我相信您知道这是一种低效的生成主键的方法。此外,它不会在多用户环境中很好地发挥作用,因此无法扩展。并发插入将等待然后失败,因为列上有一个 UNIQUE 约束。

如果前缀总是相同的长度,您可以在一定程度上减少工作量:您可以创建一个专门的索引,以最少的步数找到最大值:

CREATE INDEX ix_fetch_max ON your_table (substr(id, 1, 4), 
                                         substr(id, 5) DESC);

然后以下查询可以使用该索引,并将在检索到的第一行处停止:

SELECT id 
  FROM (SELECT substr(id, 1, 4) || substr(id, 5) id
          FROM your_table
         WHERE substr(id, 1, 4) = :prefix
         ORDER BY substr(id, 5) DESC)
 WHERE rownum = 1

如果您需要使用相同的前缀同时插入,我建议您使用DBMS_LOCK请求锁定指定的newID. 如果由于有人已经在插入此值而导致调用失败,请尝试使用newID+1. 尽管这比传统序列涉及更多的工作,但至少您的插入不会相互等待(可能导致死锁)。

于 2013-05-29T10:13:19.593 回答
3

这对你来说是一个非常不令人满意的情况。正如其他海报所指出的那样 - 如果您不使用序列,那么您几乎肯定会遇到并发问题。我在评论中提到了您存在巨大差距的可能性。这是最简单的解决方案,但在插入 9999 之后您将用完数字。

也许另一种选择是为每个前缀创建一个单独的序列。这只有在前缀数量相当少的情况下才真正实用,但可以做到。

ps - 实际上,您要求 > 1000000 条记录可能意味着您别无选择,只能重新设计数据库。

于 2013-05-29T10:31:43.377 回答
0
SELECT to_char(to_number(max(id)) + 1, '00000000')
FROM mytable
WHERE id LIKE '0001%'

SQLFiddle 演示在这里http://sqlfiddle.com/#!4/4f543/5/0

于 2013-05-29T10:05:40.593 回答