所以我最近开始使用postgres。由于我不能执行"IF else",除非在某个函数中,我很好奇资源和性能哪个更好。1.创建如下函数来查询存储id的数据库。如果是,请检查id是否为null。否则,请插入并返回id;
CREATE OR REPLACE FUNCTION getallfoo() RETURN INT AS
$BODY$
DECLARE
id INTEGER := (SELECT id FROM foo where (lots of condition checks 15+);
BEGIN
IF id is null then
INSERT INTO foo(a,b,c,d,e,f) SELECT (1,2,3,4,5,6) RETURNING id INTO id;
return id;
ELSE
RETURN id;
END IF;
END;
$BODY$
Language 'plpgsql';
select * from getallfoo();
或者2:首先尝试插入数据库,执行以下操作。随后查询数据库,导致插入失败。我先插入是有原因的——这不是这个问题的重点。我知道大多数人会说先查询,然后如果不存在插入。
INSERT INTO foo (a,b,c,d,e,f, ........, 15+) SELECT (1,2,3,4,5,6, ........., 15+)
WHERE NOT EXIST(SELECT 1 from foo where a=1, b=2, c=3, up to 15);
由于我在java中工作,我只想检查rs.getGeneratedKeys()是否为0;如果插入失败,我会重新查询数据库中的id;
SELECT id from foo where a=1, b=2, c=3, ect;
所以问题是,因为我在查询中的条件检查很长,所以最好创建一个函数并查询数据库一次。或者跳过该函数并查询两次。我在大约50张不同的桌子上做这些类型的插入。
附带问题是,你能用preparedStatement(上面的函数)在java中创建一个函数吗ResultSet rs=ps.executeUpdate();
对于相同的数据多次访问数据库是不可取的,因为这会降低应用程序的性能。所以,若您可以在单个函数中执行任务,而不是多次查询DB,那个么您应该使用函数。
和往常一样,"这取决于"。
如果您与数据库的连接是低延迟的(比如说本地主机),那么启动PL/PgSQL过程的开销就变得很小,并且启动单独的查询可能会更快。
基准,不要猜测。
在这种情况下,我怀疑在PL/PgSQL中这样做会成功,但不仅仅是出于性能原因。您的代码受几个竞赛条件的约束。如果两个人同时运行"getallfoo()"会发生什么?
- Tx 1执行
INSERT ... WHERE
- Tx 2执行
INSERT ... WHERE
- Tx 1的
WHERE
条件运行子查询,未找到行 - Tx 2的
WHERE
条件运行子查询,未找到行 - Tx 1
INSERT
s该行 - Tx 2
INSERT
s该行 - Tx 1
COMMIT
s并返回ID1 Tx 2COMMIT
s并返回ID
如果您对业务密钥有UNIQUE
约束(即:不使用生成的主键或也有约束),则第二个INSERT
将失败并出现错误。如果你不这样做,你会得到两份这一行的副本。
把事情放在程序中没有帮助;甚至没有一个语句在执行中是原子的。不相关的子查询在外部查询之前运行,等等。MVCC可见性规则通常意味着你没有注意到或关心这一点,但在这种情况下,它们只是意味着你的种族条件暴露范围有点广。
要正确处理此问题,您需要一个重试循环,如文档中与PL/PgSQL密切相关的upstart示例中所示。您可以在应用程序或过程中执行此操作,但在过程中执行该操作会显著缩小竞争条件窗口。
因为要同时执行多个关键点,所以处理起来要复杂得多。在比赛中,一个transition试图选择/插入1 2 3 4 5
,而另一个transaction想要4 5 6 7 8
。第一个tx将成功插入1
、2
和3
。。。然后发现另一个tx已经插入了CCD_ 16,并且当它得到重复密钥错误时回滚整个更改。它不会看到4
已经插入,因为第二个tx还没有提交,行也不可见。
我建议一次只执行一个键,或者使用一个外部过程,使用一个子过程逐个插入每个键,该子过程负责错误处理和重试循环。
所有这些听起来都很复杂?是的。PostgreSQL确实可以使用一些内置的SQL扩展来帮助实现这一点。