我正在开发一个使用Postgres作为其数据库的Rails 3应用程序。我有下面的表格:
Table "public.test"
Column | Type | Modifiers
---------------+---------+-----------
id | integer | not null
some_other_id | integer |
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
"some_other_id_key" UNIQUE CONSTRAINT, btree (some_other_id)
这有两列:
现在,如果我尝试插入一个带有重复some_other_id
的行,它会失败(好),我在Postgres日志中得到以下输出:
ERROR: duplicate key value violates unique constraint "some_other_id_key"
问题在于,我的应用程序尝试添加相同的ID两次是完全主线,并且我的日志被这个“错误”消息发送垃圾邮件,这会导致各种问题:文件占用大量磁盘空间,诊断获取失去了噪音,Postgres不得不丢弃错误以保持日志文件的大小限制等。
有谁知道我怎么做:
INSERT
的事务中指定某些内容来抑制日志。INSERT
。我听说过规则和触发器,但我不能上班(虽然我不是Postgres专家)。请注意,任何解决方案都需要使用Rails,它执行如下插入:
INSERT INTO test (some_other_id) VALUES (123) RETURNING id;
答案 0 :(得分:11)
要避免重复键错误开头:
INSERT INTO test (some_other_id)
SELECT 123
WHERE NOT EXISTS (SELECT 1 FROM test WHERE some_other_id = 123)
RETURNING id;
我假设id是 serial 列,它会自动获取其值。
这受非常小的竞争条件(在SELECT
和INSERT
之间的时间段)。但是可能发生的最糟糕的事情是,你毕竟会遇到重复的密钥错误,这种情况几乎不会发生,在你的情况下不应该成为问题。
如果您的框架限制您使用正确语法的选项,则可以始终使用原始SQL。
或者您可以为此目的创建UDF(用户定义的函数):
CREATE FUNCTION f_my_insert(int)
RETURNS int LANGUAGE SQL AS
$func$
INSERT INTO test (some_other_id)
SELECT $1
WHERE NOT EXISTS (SELECT 1 FROM test WHERE some_other_id = $1)
RETURNING id;
$func$
呼叫:
SELECT f_my_insert(123);
或者,默认为已存在的id
:
CREATE FUNCTION f_my_insert(int)
RETURNS int LANGUAGE plpgsql AS
$func$
BEGIN;
RETURN QUERY
SELECT id FROM test WHERE some_other_id = $1;
IF NOT FOUND THEN
INSERT INTO test (some_other_id)
VALUES ($1)
RETURNING id;
END IF;
END
$func$
同样,这为竞争条件留下了极小的机会。您可以以降低性能为代价来消除这种情况:
答案 1 :(得分:3)
您可以禁用会话(或全局实际)的错误消息记录,但需要超级用户权限:
通过运行:
set log_min_messages=fatal;
在会话(=连接)结束或发出新的set
语句以重置值之前,仅记录致命错误。
但由于只允许超级用户更改它,因此它可能不是一个好的解决方案,因为它会要求您的应用程序用户拥有这个特权,这是一个主要的安全问题。
答案 2 :(得分:0)
如果您只是想在psql
工作时抑制这些错误,可以执行
SET client_min_messages TO fatal
将持续你的剩余会话。