Oracle PL/SQL 中自定义生成的 ID/序列? [复制]
Posted
技术标签:
【中文标题】Oracle PL/SQL 中自定义生成的 ID/序列? [复制]【英文标题】:Custom Generated ID/Sequence in Oracle PL/SQL? [duplicate] 【发布时间】:2020-11-11 12:12:12 【问题描述】:我有一个客户要求,需要每天唯一的 ID。我正在开发 Oracle 12c。
在这种情况下,表 (Client_Doc) 的结构如下:
Doc_ID (PK), Date(PK), Doc_Description, Other_Columns
样本数据如下:
Value_Date Doc_ID Doc_Description
01-11-2020 1 Test Doc
01-11-2020 2 User Info
01-11-2020 3 Customer Doc
02-11-2020 1 Live
02-11-2020 2 Region
03-11-2020 1 Test
所以如果我想生成 ID,我可以这样做:
SELECT NVL(MAX(Doc_ID), 0) + 1
INTO V_ID
FROM Client_Doc
WHERE Value_Date = :P_DATE;
但由于脏读和大量表行的性能问题,这不是一个好的做法,并且由于要求也不能使用序列。满足要求的最佳方法是什么?
【问题讨论】:
如果您在任何给定日期允许在DOC_ID
中出现空白,则在此处提出并回答了几乎相同的问题:***.com/q/38913246/5174436
【参考方案1】:
生成单个主键列(从序列或作为标识生成),然后使用ROW_NUMBER
分析函数创建一个生成复合键的视图:
CREATE TABLE client_doc (
ID INT
GENERATED ALWAYS AS IDENTITY
CONSTRAINT client_doc__id__pk PRIMARY KEY,
Value_Date DATE
NOT NULL,
Doc_Description VARCHAR2(50)
);
CREATE VIEW client_doc_view ( id, value_date, doc_id, doc_description ) AS
SELECT id,
value_date,
ROW_NUMBER() OVER ( PARTITION BY value_date ORDER BY id ),
doc_description
FROM client_doc;
INSERT INTO client_doc ( value_date, doc_description )
SELECT DATE '2020-11-01', 'Test Doc' FROM DUAL UNION ALL
SELECT DATE '2020-11-01', 'User Info' FROM DUAL UNION ALL
SELECT DATE '2020-11-01', 'Customer Doc' FROM DUAL UNION ALL
SELECT DATE '2020-11-02', 'Live' FROM DUAL UNION ALL
SELECT DATE '2020-11-02', 'Region' FROM DUAL UNION ALL
SELECT DATE '2020-11-03', 'Test' FROM DUAL;
然后,视图包含:
SELECT *
FROM client_doc_view;
身份证 | VALUE_DATE |文档 ID |文档描述 -: | :--------- | -----: | :-------------- 1 | 20 年 11 月 1 日 | 1 |测试文件 2 | 20 年 11 月 1 日 | 2 |用户信息 3 | 20 年 11 月 1 日 | 3 |客户文件 4 | 20 年 11 月 2 日 | 1 |居住 5 | 20 年 11 月 2 日 | 2 |地区 6 | 20 年 11 月 3 日 | 1 |测试
然后,当您想向客户显示数据时,您可以向他们显示value_date
和doc_id
的复合数据,但在后台您有一个唯一的列可用于由序列支持的连接和外键。
db小提琴here
【讨论】:
似乎必须做出很多假设才能确保安全。例如,不允许对表进行删除(否则它们会更改所有后续行的标识符)。 @MatthewMcPeak 见最后一段,这只是为了展示;所有引用约束都应该在主键上完成。 谢谢 - 这是有道理的。不过,我不会删除我的评论,因为我认为这是一个重要的压力点。此“密钥”的系统外使用仍可能导致错误。例如,用户查看显示的记录并认为“我需要调查和编辑第 01-NOV-20 / 2 行”。后来,他们回来进行编辑。如果他们查询 01-NOV-20 / 2 进行编辑,他们可能会犯错误。 (这也是我一直提倡将用户的大脑物理连接到系统中的部分原因,但我不断遭到反对)。 @MT0 mate 不幸的是,它不仅用于显示目的,而且 ROW_NUMBER() OVER ( PARTITION BY value_date ORDER BY id ) 将对超过 10M 的记录产生性能问题... @NafiPantha 你追求的东西并不真正存在。您希望将序列分区到另一列,但这样的结构不存在,并且您不想使用MAX
,因为它不处理并行插入,并且您希望使用的任何解决方案都具有高性能......不会找到东西,因为它不存在。我试图提供一个替代方案,如果这还不够好,那么您可能会陷入困境,应该与客户讨论需求并要求他们更改需求。或者,您可以使用 MatthewMcPeak 对您的回答的评论。【参考方案2】:
当您使用 12c 时,请使用身份列。这是一个例子:
SQL> create table client_doc
2 (doc_id number generated always as identity,
3 datum date,
4 doc_description varchar2(20),
5 --
6 constraint pk_clidoc primary key (doc_id, datum)
7 );
Table created.
SQL> insert into client_doc (datum, doc_description) values (sysdate, 'Test');
1 row created.
SQL> select * From client_doc;
DOC_ID DATUM DOC_DESCRIPTION
---------- -------- --------------------
1 11.11.20 Test
SQL>
【讨论】:
以上是关于Oracle PL/SQL 中自定义生成的 ID/序列? [复制]的主要内容,如果未能解决你的问题,请参考以下文章