SQLITE - insert... where -如何强制动态行为

6gpjuf90  于 2023-04-06  发布在  SQLite
关注(0)|答案(2)|浏览(208)

我尝试了带WHERE条件的INSERT。现在,WHERE将取决于每个新插入的记录,例如WHERE NOT EXISTS (select ... from <table I am inserting records into>)
但是,WHERE子句只计算一次,不会考虑每个新插入的记录。
我知道INSERT OR IGNORE,INSERT OR UPDATE或UPSERT...但是,在我的情况下,WHERE子句将比仅仅验证密钥的存在更复杂。
问题是查询优化器永远只计算一次WHERE,而不是逐个考虑新插入的记录。
问:是否有一种方法可以强制查询优化器在插入后立即考虑每条记录?
举一个(有点理论性的)例子:从递归生成的数列中引入素数,WHERE条件说明它不能被表中已经存在的任何(素数)数除尽。代码WON 'T WORK;十九个数字一起介绍。

CREATE TABLE pnumbers (pnumber number primary key);

with r as 
(select 2 as n union all select n+1 as n from r where n < 20)
insert into pnumbers
select n from r where not exists 
(select pnumber from pnumbers pn where r.n % pn.pnumber = 0 );

P.S.相反,DELETE.. WHERE完美地工作并且非常快。下面的代码在我的游戏笔记本电脑上仅用90秒就消除了1000万个连续数字中的非素数-(然而,这不是我的想法):

delete from pnumbers  where exists (select pnumber from pnumbers pn2 
where pn2.pnumber <= sqrt(pnumbers.pnumber) and pnumbers.pnumber % pn2.pnumber = 0);
d8tt03nd

d8tt03nd1#

问题是查询优化器永远只计算一次WHERE
不,这不是真的。
WHERE子句对r的每一行计算一次,因为EXISTS的子查询是相关子查询。
问题有些不同。
在任何INSERT INTO ... SELECT...语句中,首先执行SELECT...语句,并在完成后将返回的结果集插入表中。
这意味着,对于您的理论示例,在此语句返回之前,不会插入表pnumbers

with r as 
(select 2 as n union all select n+1 as n from r where n < 20)
select n from r where not exists 
(select pnumber from pnumbers pn where r.n % pn.pnumber = 0 );

在上面的语句中对表pnumbers的任何引用都将访问一个空表,因为还没有插入任何行。
这就是为什么该语句返回r的所有行。
确实,在SQLite的INSERT文档中没有清楚地概述这种行为,但这是其含义:
对于通过执行SELECT语句返回的每一行数据,都将在表中插入一个新条目。

iklwldmw

iklwldmw2#

虽然forpas在他的答案中所说的一切都是正确的,但可以解决在临时触发器中移动EXIST条件的问题。

CREATE TEMPORARY TRIGGER primes BEFORE INSERT ON main.pnumbers
WHEN exists (select pn.pnumber from pnumbers pn where NEW.pnumber % pn.pnumber = 0)
BEGIN
  SELECT RAISE(IGNORE);  
END;

WITH r AS
(select 2 as n union all select n+1 as n from r where n < 20)
insert into pnumbers
select n from r;

DROP TRIGGER primes;

Fiddle here
在本例中,您尝试插入从2到19的每个值,但触发器将在INSERT时为每一行触发,此时前面的行已经插入,并且满足exists条件的行永远不会插入。
当然,你可以使用一个普通的、永久的触发器,但是一个临时的触发器只会在你定义它的同一个连接上进行插入时触发,它不会影响同一个表上其他应用程序的其他潜在修改。

相关问题