我有一个varchar(50)行,它有一个唯一约束,我希望获得新插入的下一个唯一编号,但是带有给定的前缀。
我的行看起来像这样:
ID (varchar)
00010001
00010002
00010003
00080001
因此,如果我想从前缀“0001”获取下一个unqiue号码,那么它将是“00010004”,但如果我想要它作为前缀“0008”,那么它将是“00080002”。
此表中将有超过1百万个条目。有没有办法让Oracle 11执行相当快的这种操作?
我知道这个设置完全是疯了但这是我必须要处理的。我不能创建任何新表等。
答案 0 :(得分: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
。虽然这涉及比传统序列更多的工作,但至少你的插入不会在彼此等待(可能导致死锁)。
答案 1 :(得分:3)
这对你来说是一个非常令人不满意的情况。正如其他海报所指出的那样 - 如果你不使用序列,那么你几乎肯定会遇到并发问题。我在评论中提到你存在巨大差距的可能性。这是最简单的解决方案,但是在9999次插入后你将用完数字。
也许另一种方法是为每个前缀创建一个单独的序列。如果前缀的数量相当低但可以完成,那么这只会非常实用。
ps - 您的要求>事实上,1000000条记录可能意味着你别无选择,只能重新设计数据库。
答案 2 :(得分:0)
SELECT to_char(to_number(max(id)) + 1, '00000000')
FROM mytable
WHERE id LIKE '0001%'
这里的SQLFiddle演示http://sqlfiddle.com/#!4/4f543/5/0