我有一堆行需要插入表中,但是这些插入总是分批完成的。因此,我想检查表中是否存在该批处理中的一行,因为那样我就知道它们都已插入。
因此,它不是主键检查,但不要紧。我只想检查单行,所以count(*)
可能不好,所以exists
我猜是这样。
但是由于我是PostgreSQL的新手,所以我宁愿问认识的人。
我的批处理包含具有以下结构的行:
userid | rightid | remaining_count
因此,如果表包含提供的任何行,userid
则意味着它们都存在于此。
我有一堆行需要插入表中,但是这些插入总是分批完成的。因此,我想检查表中是否存在该批处理中的一行,因为那样我就知道它们都已插入。
因此,它不是主键检查,但不要紧。我只想检查单行,所以count(*)
可能不好,所以exists
我猜是这样。
但是由于我是PostgreSQL的新手,所以我宁愿问认识的人。
我的批处理包含具有以下结构的行:
userid | rightid | remaining_count
因此,如果表包含提供的任何行,userid
则意味着它们都存在于此。
Answers:
将EXISTS关键字用于TRUE / FALSE返回:
select exists(select 1 from contact where id=12)
select exists(select 1 from contact where id=12) AS "exists"
exists
或者limit 1
由于PostgreSQL使用Seq Scan而不是Index Scan而导致性能下降很大。而且analyze
没有帮助。
简单地说:
select 1 from tbl where userid = 123 limit 1;
哪里 123
您要插入的批次的用户ID。
上面的查询将返回空集或单行,这取决于是否存在具有给定用户标识的记录。
如果结果太慢,则可以考虑在上创建索引tbl.userid
。
如果表中甚至存在批处理中的一行,在这种情况下,我也不必插入行,因为我知道它们都已插入。
为了即使在程序在批处理过程中被中断时也能保持这种状态,我建议您确保适当地管理数据库事务(即,将整个批处理插入单个事务中)。
COUNT
作用于嵌套SELECT
具有至多1行(因为LIMIT
在子查询)。
INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count
FROM batch
WHERE NOT EXISTS (
SELECT * FROM target t2, batch b2
WHERE t2.userid = b2.userid
-- ... other keyfields ...
)
;
顺便说一句:如果您希望整个批次在重复的情况下失败,那么(考虑到主键约束)
INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count
FROM batch
;
将完全按照您想要的方式进行操作:成功还是失败。
SELECT 1 FROM user_right where userid = ? LIMIT 1
如果结果集包含一行,则无需插入。否则插入您的记录。
如果您考虑性能,也许可以在函数中使用“ PERFORM”,如下所示:
PERFORM 1 FROM skytf.test_2 WHERE id=i LIMIT 1;
IF FOUND THEN
RAISE NOTICE ' found record id=%', i;
ELSE
RAISE NOTICE ' not found record id=%', i;
END IF;
我想提出另一种想法来专门解决您的句子:“因此,我想检查表中是否存在该批处理中的一行,因为这样我便知道它们都已插入。”
您通过插入“批”来使事情变得高效,但是然后进行一次存在性检查一次记录吗?这对我来说似乎很直觉。因此,当您说“ 插入总是成批完成 ”时,我认为您是在用一条insert语句插入多条记录。您需要认识到Postgres符合ACID。如果要通过一个insert语句插入多个记录(一批数据),则无需检查是否已插入某些记录。该语句要么通过,要么失败。所有记录将被插入或不插入。
另一方面,例如,如果您的C#代码只是在循环中简单地“设置”单独的插入语句,并且在您的脑海中,这是一个“批处理” ..那么您实际上不应将其描述为“插入总是分批完成”。您期望实际上可能未插入“批处理”部分的事实,因此感到需要检查,这一事实强烈表明了这种情况,在这种情况下,您有一个更根本的问题。您需要更改范例,以实际插入一个插入的多个记录,并放弃检查单个记录是否插入。
考虑以下示例:
CREATE TABLE temp_test (
id SERIAL PRIMARY KEY,
sometext TEXT,
userid INT,
somethingtomakeitfail INT unique
)
-- insert a batch of 3 rows
;;
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 1, 1),
('bar', 2, 2),
('baz', 3, 3)
;;
-- inspect the data of what we inserted
SELECT * FROM temp_test
;;
-- this entire statement will fail .. no need to check which one made it
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 2, 4),
('bar', 2, 5),
('baz', 3, 3) -- <<--(deliberately simulate a failure)
;;
-- check it ... everything is the same from the last successful insert ..
-- no need to check which records from the 2nd insert may have made it in
SELECT * FROM temp_test
实际上,这是任何与ACID兼容的数据库的范式,而不仅仅是Postgresql。换句话说,如果您解决了“批处理”的概念,而不必首先进行任何逐行检查,那么情况会更好。