我有一个包含两列的表:k(主键)和value。我想:
是否可以使此“选择更新并在未找到时插入默认值”?
如果将(1)实现为select/check if found/insert if not found,我们会遇到并发问题,因为两个会话可能会在不存在的键上并发地进行选择,两个会话都会尝试插入,其中一个实例会失败。
在这种情况下,所需的行为是以原子方式执行 select/insert,其中一个实例执行它,第二个实例保持锁定状态,直到第一个实例提交,然后使用第一个实例插入的值。
我们实现它总是做一个“插入...如果不存在.../commit“,则在”选择更新“之前,但这意味着在不太可能需要时始终尝试插入。
有没有办法在一个sql语句上实现?
谢谢!!
使用序列生成代理主键而不是使用自然键。如果您有一个真正的自然键,那么两个用户同时提交相同值的可能性极小。
有几种方法可以自动生成主键。我更喜欢使用序列默认值,如下所示:
create sequence test_seq;
create table test1
(
k number default test_seq.nextval,
value varchar2(4000),
constraint test1_pk primary key(k)
);
如果无法切换到代理密钥或真正的自然密钥:
将“insert…if not exist…/commit”更改为更简单的“insert…(if not-exist)”,并在单个事务中执行所有操作。在不同的会话中插入相同的主键,即使未提交,在Oracle中也是不可能的。虽然<code>选择
如果两个会话尝试同时插入相同的主键,则第二个会话将挂起,并且当第一个会话最终提交时,第二个会话将失败,并出现异常“ORA-00001:违反唯一约束 (X.Y)”。让该异常成为您了解用户何时提交重复值的标志。您可以捕获应用程序中的异常,并让用户重试。
<代码>选择...for update是您应该做的第一步;没有它,您就不能“保留”该行以供进一步处理(除非您愿意以独占模式锁定整个表;如果“处理”不花时间,那也是一种选择,特别是如果没有很多用户会这么做的话)。
如果row存在,剩下的就很简单了——处理它,更新它,提交。
但是,如果它不存在,您将不得不插入一个新行(正如您所说的),这里有一个两个(或更多)用户插入相同值的问题。
为了避免它,创建一个函数
用户必须使用该函数来获取下一个ID
值。这是一个示例:您需要一个保存上次使用的ID的表(my_id
)(每个通过该函数访问它的用户都将创建一个新值)。
表格:
SQL> create table my_id (id number);
Table created.
功能:
SQL> create or replace function f_id
2 return number
3 is
4 pragma autonomous_transaction;
5 l_nextval number;
6 begin
7 select id + 1
8 into l_nextval
9 from my_id
10 for update of id;
11
12 update my_id set
13 id = l_nextval;
14
15 commit;
16 return (l_nextval);
17
18 exception
19 when no_data_found then
20 lock table my_id in exclusive mode;
21
22 insert into my_id (id)
23 values (1);
24
25 commit;
26 return(1);
27 end;
28 /
Function created.
用它作为
SQL> html" target="_blank">select f_id from dual;
F_ID
----------
1
SQL>
就这样…您将使用的代码如下:
SQL> create table test
2 (id number constraint pk_test primary key,
3 name varchar2(10),
4 datum date
5 );
Table created.
SQL> create or replace procedure p_test (par_id in number)
2 is
3 l_id test.id%type;
4 begin
5 select id
6 into l_id
7 from test
8 where id = par_id
9 for update;
10
11 update test set datum = sysdate where id = par_id;
12 exception
13 when no_data_found then
14 insert into test (id, name, datum)
15 values (f_id, 'Little', sysdate); --> function call is here
16 end;
17 /
Procedure created.
SQL> exec p_test (1);
PL/SQL procedure successfully completed.
SQL> select * from test;
ID NAME DATUM
---------- ---------- -------------------
1 Little 04.09.2021 20:49:21
SQL> exec p_test (1);
PL/SQL procedure successfully completed.
SQL> select * from test;
ID NAME DATUM
---------- ---------- -------------------
1 Little 04.09.2021 20:49:21 --> row was inserted
SQL> exec p_test (1);
PL/SQL procedure successfully completed.
SQL> select * from test;
ID NAME DATUM
---------- ---------- -------------------
1 Little 04.09.2021 20:49:30 --> row was updated
SQL>
查看 k 是否可用
从表中选择 * 其中 k = 更新值
如果没有返回行,则它不存在。插入它:
INSERT INTO TABLE(k, col1, col2) VALUES (value, val1, default))
问题内容: 我正在执行插入查询,其中如果已经存在唯一键,则许多列中的大多数都需要更新为新值。它是这样的: 我不确定该子句的语法应该是什么。如何从子句引用当前行? 问题答案: MySQL将假定等号之前的部分引用INSERT INTO子句中命名的列,而第二部分引用SELECT列。
问题内容: 有几种将数据插入表中的方法: 有没有办法使用,但我不想在colA中插入例如colAA值,而是总是将其插入1。 感谢帮助 问题答案: 只需在列表中添加一个常量
主要内容:Oracle INSERT INTO SELECT语句概述,Oracle INSERT INTO SELECT示例在本教程中,您将学习如何使用Oracle 语句将数据从语句的结果中插入到表中。 Oracle INSERT INTO SELECT语句概述 有时候,想要将从其它表中选择数据并将其插入到另一个表中。要做到这一点,可使用Oracle 语句,如下所示: Oracle 语句要求源表和目标表匹配数据类型。 Oracle INSERT INTO SELECT示例 1. 插入所有销售数据
我们有一个简单的案例,我们有一个带有列emailId的表,即唯一的......使用oracle DB 问题#1多个并发用户可以检查某些电子邮件ID是否可用。就像2个用户同时检查:abc@test.com的可用性 会话1:从user_table中选择电子邮件id;如果不存在,则允许用户完成该过程的其余部分 会话2:从user_table中选择电子邮件id; 现在,两个会话都将获得此电子邮件ID(ab
问题内容: 我有一个产品对象,它属于某些类别,即经典的多对一关系。 我想插入和更新产品而不预先选择类别。像这样: 要么 是否可以在不选择类别的情况下进行更新和插入?我不想为此使用HQL或直接查询。 问题答案: session.load()专门用于此类情况。以下: 不会打数据库。但是,如果没有提供给定ID的类别,它将在稍后阶段(刷新期间或多或少)引发异常。 使用速度快且没有副作用(级联等)。
问题内容: 我发现了具有正确答案的类似问题。但是它们对我来说有点复杂。我只想要一个简单的基本声明。 我有: …和: 如何合并它们? 问题答案: 有关OUTPUT子句的更多信息,请查看此文章。