2013-07-03 103 views
1

所以我最近开始使用postgres。而且由于我不能执行“IF else”,除非在函数中我很好奇哪一个更好的资源和明智的性能。 1.创建一个如下所示的函数来查询存储该ID的数据库。检查id是否为null,如果是则插入else返回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();

+2

通常,查询次数越少意味着执行时间越快。如果你能够提高性能,这是值得的。 –

+0

您能否详细说明“首先插入的原因是不是这个问题的重点”? IMO这是一个反模式... – home

+0

我正在与一个测试平台,我是唯一的1插入数据到数据库中的数据。有两个原因检查记录是否已经存在1是我的一些数据是多对多的关系,因此记录可能已经存在,但不太可能。其次,如果有人试图重新解析包含将数据加载到数据库的文件的文件,这是不可能发生的,因为我是唯一一个将数据插入数据库的交互。一般来说,数据不存在比它存在的可能性更大。 – Jeremy

回答

2

对于相同的数据多次访问数据库是不可取的,因为它降低了应用程序的性能。所以如果你可以用单个函数执行你的任务而不是多次查询数据库,那么你应该使用函数。

0

像往常一样,“这取决于”。

如果您的数据库连接延迟较低(例如本地主机),那么启动PL/PgSQL过程的开销就变得非常小,并且启动单独的查询可能会更快。

基准,不要猜测。

在这种情况下,我怀疑在PL/PgSQL中这样做会赢,但不是纯粹出于性能原因。您的代码受限于竞赛条件。如果两个人同时运行“getallfoo()”会发生什么?

  1. 的Tx 1确实INSERT ... WHERE
  2. 的Tx 2确实INSERT ... WHERE
  3. 的Tx 1的WHERE条件运行的子查询,发现没有行
  4. 的Tx 2的WHERE条件运行的子查询,发现没有行
  5. 的Tx 1 INSERT s行
  6. Tx 2 INSERT s行
  7. 的Tx 1 COMMIT S和返回ID 1 TX 2 COMMIT S和返回ID

如果你有一个业务键UNIQUE约束(即:不使用生成的主键或也有一个约束)第二个INSERT将失败并显示错误。如果你不这样做,你会得到该行的两个副本。

把东西放在一个程序中并没有帮助;甚至没有一个单独的声明在执行中是原子的。不相关的子查询在外部查询等之前运行。MVCC可见性规则通常意味着您不会注意或关心这一点,但在这种情况下,它们只是意味着您的竞争条件暴露更加宽泛。

要正确处理此问题,您需要重新尝试循环,就像在密切相关的upsert example for PL/PgSQL in the documentation中找到的一样。您可以在应用程序或过程中执行此操作,但在过程中执行此操作应该显着缩小竞争条件窗口的范围。

因为你想要一次完成多个键,所以处理起来要复杂得多。您可以选择/插入1 2 3 4 5而另一个想要4 5 6 7 8的赛事。第一个tx将成功插入1,23 ...然后发现其他tx已经插入4,并且在它发生重复密钥错误时回滚整个更改。它不会看到4已被插入,因为第二个tx尚未提交,并且该行将不可见。

我建议一次只做一个键,或者有一个外部过程,它使用执行错误处理和重试循环的子过程逐个插入每个键。

所有的声音都很复杂吗?是的。 PostgreSQL确实可以使用一些内置的SQL扩展来解决这个问题。