Fastest check if row exists in PostgreSQL
我有一堆行需要插入到表中,但这些插入总是分批完成。 所以我想检查表中是否存在批处理中的单行,因为我知道它们都已插入。
所以它不是主键检查,但不应该太重要。 我想只检查单行,所以count(*)可能不好,所以它的类似exists我猜。
但是因为我对PostgreSQL相当新,所以我宁愿问那些知道的人。
我的批处理包含具有以下结构的行:
1
| userid | rightid | remaining_count |
因此,如果表包含提供userid的任何行,则表示它们都存在于那里。
-
您想查看表中是否有任何行或批处理中的任何行?
-
来自我的批次的任何行是。他们都共享相同的领域病态编辑一点。
-
请澄清你的问题。您想要添加一批记录,全部或全部?伯爵有什么特别之处吗? (BTW保留字,作为列名不切实际)
-
好吧,我试图简化实际情况,但我们越来越接近真正的实施。插入这些行后(另一个字段为for_date)我开始递减指定用户的权限,因为他们使用特定权限,一旦权限变为0,他们就不能再执行那些日期的操作。这就是真实的故事
-
只需显示表定义(相关部分),并告诉您打算做什么。
-
[userid,rightid,for_date,remainingCount]就是真正的表格。当用户尝试使用任何权限时,我打算插入此表。我本可以创建一个特殊的进程,负责每天插入行,但目前我正在检查另一种方法是否适合我的性能。
-
remainingCount的大写字母为"C"?我建议坚持小写(remaining_count),否则你总是要引用这个标识符:"remainingCount"。也没有"postgre"之类的东西。这是"PostgreSQL"或"postgres"。最后,仍然不清楚一次插入的所有行是否共享相同的用户ID。
-
那好吧。我从C#代码的参数列表中复制粘贴,不要担心Erwin。我认为应该很清楚,但要指定更多,批处理包含共享userid的行,这就是为什么,检查具有指定userid的单行首先是有意义的..
使用EXISTS关键字返回TRUE / FALSE:
1
| SELECT EXISTS(SELECT 1 FROM contact WHERE id=12) |
-
对此进行扩展,您可以命名返回的列以便于参考。例如select exists(select 1 from contact where id=12) AS"exists"
-
这样更好,因为它总是会返回一个值(true或false),而不是有时会返回None(取决于你的编程语言),这可能不会像你期望的那样扩展。
-
我使用此方法进行Seq Scan。我做错了什么?
-
@FiftiN ::这可能会回答你的问题:stackoverflow.com/questions/5203755/…
-
@ Michael.M我有30万行的DB表,当我使用exists或limit 1时,我的性能下降很强,因为Postgres使用Seq Scan而不是Index Scan。并且analyze没有帮助。
-
子查询中的limit 1会帮助或减慢查询吗?
-
@maciek请理解'id'是主键,因此"LIMIT 1"将毫无意义,因为只有一个记录带有该id
怎么样简单:
1
| SELECT 1 FROM tbl WHERE userid = 123 LIMIT 1; |
其中123是您要插入的批次的用户ID。
上述查询将返回空集或单行,具体取决于是否存在具有给定用户标识的记录。
如果结果太慢,您可以考虑在tbl.userid上创建索引。
if even a single row from batch exists in table, in that case I
don't have to insert my rows because I know for sure they all were
inserted.
为了使这一点保持正确,即使您的程序在批处理中被中断,我建议您确保正确管理数据库事务(即整个批处理插入单个事务中)。
-
当然,即时通讯使用交易
-
有时可能在编程上更容易"从(选择1 ...限制1)中选择计数(*),因为它保证始终返回值为count(*)为0或1的行。
-
@DavidAldridge计数(*)仍然表示必须读取所有行,而限制1在第一个记录处停止并返回
-
@Imraan我认为你误解了这个问题。 COUNT作用于最多有1行的嵌套SELECT(因为LIMIT在子查询中)。
1 2 3 4 5 6 7 8 9
| 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 ...
)
; |
顺便说一句:如果你希望整个批次在重复的情况下失败,那么(给定主键约束)
1 2 3 4
| INSERT INTO target( userid, rightid, COUNT )
SELECT userid, rightid, COUNT
FROM batch
; |
会做你想做的事:要么成功,要么失败。
-
这将检查每一行。他想做一次检查。
-
不,它只进行一次检查。子查询是不相关的。一旦发现一对配对,它将纾困。
-
你是对的,我以为它引用了外部查询。 +1给你
-
BTW:由于查询在事务内部,如果要插入重复的id,则不会发生任何事情,因此可以省略子查询。
-
嗯我不确定我理解。插入权限后,我开始递减计数列。 (只是图片的一些细节)如果行已经存在并且子查询被省略,我认为生成错误时会抛出重复的唯一键或者? (userid& right表示唯一键)
1
| SELECT TRUE FROM tablename WHERE condition LIMIT 1; |
我相信这是postgres用于检查外键的查询。
在你的情况下,你也可以一气呵成:
1
| INSERT INTO yourtable SELECT $userid, $rightid, $count WHERE NOT (SELECT TRUE FROM yourtable WHERE userid = $userid LIMIT 1); |
经验法则
select exists (
select true from xx where xx
) as"Does 1+1 adds up to 2";
如果你考虑性能,可能是你可以在函数中使用"PERFORM",如下所示:
1 2 3 4 5 6
| 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; |
-
不能与我合作:我在接近执行时遇到语法错误
-
这是pl / pgsql,而不是SQL,因此如果尝试将其作为SQL运行,则会出现"PERFORM"的语法错误
1
| SELECT 1 FROM user_right WHERE userid = ? LIMIT 1 |
如果结果集包含一行,则不必插入。否则插入您的记录。
-
如果束包含100行,它将返回100行,你认为那好吗?
-
您可以将其限制为1行。应该表现更好。看看来自@aix的编辑回答。