最快检查PostgreSQL中是否存在行


176

我有一堆行需要插入表中,但是这些插入总是分批完成的。因此,我想检查表中是否存在该批处理中的一行,因为那样我就知道它们都已插入。

因此,它不是主键检查,但不要紧。我只想检查单行,所以count(*)可能不好,所以exists我猜是这样。

但是由于我是PostgreSQL的新手,所以我宁愿问认识的人。

我的批处理包含具有以下结构的行:

userid | rightid | remaining_count

因此,如果表包含提供的任何行,userid则意味着它们都存在于此。


您想查看表中是否有ANY行,或批处理中有任何行?
JNK

我批次中的任何行都是。他们都共享同一个领域,但编辑有些困难。
Valentin Kuzub 2011年

请澄清您的问题。您要添加全部或全部记录吗?计数有什么特别之处吗?(顺便说一句,保留字,列名不切实际)
wildplasser 2011年

好的,我试图稍微简化实际情况,但是我们越来越接近实际实现。一旦插入了这些行(另一个字段for_date),我便开始减少指定用户的权限,因为他们使用特定权限,一旦权限变为0,他们就无法在该日期执行这些操作。那就是真实的故事
Valentin Kuzub 2011年

1
只需显示表定义(的相关部分),然后说出您打算做什么。
wildplasser 2011年

Answers:


340

将EXISTS关键字用于TRUE / FALSE返回:

select exists(select 1 from contact where id=12)

21
对此扩展,您可以命名返回的列,以方便参考。例如select exists(select 1 from contact where id=12) AS "exists"
Rowan

3
这样做会更好,因为它将始终返回一个值(true或false),而不是有时返回None(取决于您的编程语言),这可能不会扩展您的期望方式。
isaaclw 2014年

1
我有使用此方法的Seq Scan。我做错了吗?
2016年

2
@ Michael.MI的数据库表具有3000万行,当我使用它时,exists或者limit 1由于PostgreSQL使用Seq Scan而不是Index Scan而导致性能下降很大。而且analyze没有帮助。
2016年

2
@maciek请理解,“ id”是主键,因此“ LIMIT 1”将毫无意义,因为只有一个具有该ID的记录
StartupGuy

34

简单地说:

select 1 from tbl where userid = 123 limit 1;

哪里 123您要插入的批次的用户ID。

上面的查询将返回空集或单行,这取决于是否存在具有给定用户标识的记录。

如果结果太慢,则可以考虑在上创建索引tbl.userid

如果表中甚至存在批处理中的一行,在这种情况下,我也不必插入行,因为我知道它们都已插入。

为了即使在程序在批处理过程中被中断时也能保持这种状态,我建议您确保适当地管理数据库事务(即,将整个批处理插入单个事务中)。


11
它有时可能是更容易编程“SELECT COUNT(*)从(选择1 ...极限1)”,因为它是保证总是返回一行与计数的值(*)0或1
大卫·阿尔德里奇

@DavidAldridge count(*)仍然意味着必须读取所有行,而限制1在第一条记录处停止并返回
Imraan 2013年

3
@Imraan我认为您误解了该查询。的COUNT作用于嵌套SELECT具有至多1行(因为LIMIT在子查询)。
jpmc26 2014年

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 ...
    )       
    ;

顺便说一句:如果您希望整个批次在重复的情况下失败,那么(考虑到主键约束)

INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count 
FROM batch
    ;

将完全按照您想要的方式进行操作:成功还是失败。


这将检查每一行。他想做一张支票。
JNK

1
不,它会进行一次检查。子查询是不相关的。一旦找到一对匹配的货币对,它将纾困。
wildplasser 2011年

是的,我认为它是指外部查询。+1给您
JNK

顺便说一句:由于查询位于事务内部,因此如果要插入重复的ID将不会发生任何事情,因此可以省略子查询。
wildplasser 2011年

嗯,我不确定我是否理解。插入权限后,我开始减少计数列。(只是图片的一些细节)如果行已经存在并且省略了子查询,我认为由于重复的唯一键被抛出而导致错误或者是?(用户名和唯一形式的唯一密钥)
Valentin Kuzub 2011年

1
select true from tablename where condition limit 1;

我相信这是postgres用于检查外键的查询。

对于您的情况,也可以一口气做到这一点:

insert into yourtable select $userid, $rightid, $count where not (select true from yourtable where userid = $userid limit 1);

1

正如@MikeM指出的。

select exists(select 1 from contact where id=12)

指数上的接触,它通常可以减少时间成本为1毫秒。

CREATE INDEX index_contact on contact(id);


0

如果您考虑性能,也许可以在函数中使用“ 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;

对我不起作用:我在执行时遇到语法错误
Simon

1
那是pl / pgsql,不是SQL,因此如果尝试以SQL形式运行“ PERFORM”,则语法错误
Mark K Cowan,2016年

0

我想提出另一种想法来专门解决您的句子:“因此,我想检查表中是否存在该批处理中的一行,因为这样我便知道它们都已插入。”

您通过插入“批”来使事情变得高效,但是然后进行一次存在性检查一次记录吗?这对我来说似乎很直觉。因此,当您说“ 插入总是成批完成 ”时,我认为您是在用一条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。换句话说,如果您解决了“批处理”的概念,而不必首先进行任何逐行检查,那么情况会更好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.