在Oracle中查找varchar的下一个id

Mar*_*hin 2 oracle plsql

我有一个varchar(50)行,它有一个唯一约束,我想得到一个新插入的下一个唯一编号但是有一个给定的前缀.

我的行看起来像这样:

ID (varchar)
00010001
00010002
00010003
00080001
Run Code Online (Sandbox Code Playgroud)

因此,如果我想从前缀"0001"获得下一个unqiue号码,那么它将是"00010004",但如果我希望它为前缀"0008",那么它将是"00080002".

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

我知道这个设置完全是疯了但这是我必须要处理的.我不能创建任何新表等.

Vin*_*rat 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
Run Code Online (Sandbox Code Playgroud)

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

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

CREATE INDEX ix_fetch_max ON your_table (substr(id, 1, 4), 
                                         substr(id, 5) DESC);
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • +1给出了一个很好的答案:我很想通过将substring()结果转换为数字来使索引更紧凑.如果在使用的版本中可用,似乎可以很好地使用虚拟列. (2认同)