【问题标题】:Fastest check if row exists in PostgreSQL最快检查 PostgreSQL 中是否存在行
【发布时间】:2011-11-20 06:14:55
【问题描述】:

我有一堆行需要插入到表中,但这些插入总是分批完成。所以我想检查表中是否存在批处理中的一行,因为那时我知道它们都被插入了。

所以它不是主键检查,但应该没有太大关系。我只想检查单行,所以count(*) 可能不好,所以我猜它类似于exists

但由于我对 PostgreSQL 还很陌生,所以我宁愿问问知道的人。

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

userid | rightid | remaining_count

因此,如果表中包含任何提供 userid 的行,则表示它们都存在于那里。

【问题讨论】:

  • 您想查看表中是否有任何行,或者您的批次中是否有任何行?
  • 我的批次中的任何行是的。他们都共享相同的字段,请稍作编辑。
  • 请澄清您的问题。您要添加一批记录,全部还是全部? count 有什么特别之处吗? (顺便说一句,保留字,作为列名不切实际)
  • 好的,我试图稍微简化一下实际情况,但我们越来越接近真正的实施。插入这些行后(还有另一个字段 for_date),我开始减少指定用户的权限,因为他们使用特定权限,一旦权限变为 0,他们就无法再在该日期执行这些操作。这就是真实的故事
  • 只需显示(相关部分)表定义,并说明您打算做什么。

标签: sql postgresql


【解决方案1】:

使用 EXISTS 关键字返回 TRUE / FALSE:

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。我做错了什么?
  • @Michael.M 我有 3000 万行的数据库表,当我使用 existslimit 1 时,我的性能下降很大,因为 Postgres 使用 Seq Scan 而不是 Index Scan。而analyze 也无济于事。
  • 子查询中的limit 1 会帮助还是减慢查询速度?
【解决方案2】:

简单地说:

select 1 from tbl where userid = 123 limit 1;

123 是您将要插入的批次的用户 ID。

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

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

如果表中甚至存在批处理中的单行,在这种情况下,我 不必插入我的行,因为我确定它们都是 插入。

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

【讨论】:

  • 有时可能以编程方式更容易“从 (select 1 ... limit 1) 中选择 count(*)”,因为它保证始终返回值为 count(*) 的行0 或 1。
  • @DavidAldridge count(*) 仍然意味着必须读取所有行,而 limit 1 在第一条记录处停止并返回
  • @Imraan 我认为您误解了查询。 COUNT 作用于最多有 1 行的嵌套 SELECT(因为 LIMIT 在子查询中)。
【解决方案3】:
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
    ;

会做你想做的事:要么成功,要么失败。

【讨论】:

  • 这将检查每一行。他想做一次检查。
  • 不,它只进行一次检查。子查询不相关。一旦找到一对匹配的,它就会退出。
  • 没错,我认为它指的是外部查询。 +1 给你
  • 顺便说一句:由于查询在事务中,如果要插入重复的 id,则不会发生任何事情,因此可以省略子查询。
  • 嗯,我不确定我是否理解。插入权限后,我开始减少计数列。 (只是图片的一些细节)如果行已经存在并且子查询被省略,我认为会抛出重复的唯一键或错误? (用户 ID 和权限形式的唯一键)
【解决方案4】:

正如@MikeM 指出的那样。

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

index on contact,通常可以将时间成本降低到1毫秒。

CREATE INDEX index_contact on contact(id);

【讨论】:

  • 1ms 的成本是巨大的——每秒只能进行 1000 次这样的检查。应该瞄准每秒大约 10M 的检查。
【解决方案5】:

如果你考虑一下性能,也许你可以像这样在函数中使用“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;

【讨论】:

  • 对我不起作用:我在执行附近遇到语法错误
  • 这是 pl/pgsql,而不是 SQL,因此如果尝试将其作为 SQL 运行,则会出现“PERFORM”的语法错误
【解决方案6】:
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);

【讨论】:

    【解决方案7】:
    SELECT 1 FROM user_right where userid = ? LIMIT 1
    

    如果您的结果集包含一行,那么您不必插入。否则插入您的记录。

    【讨论】:

    • 如果 bundle 包含 100 行,它将返回 100 行,你认为这很好吗?
    • 您可以将其限制为 1 行。应该表现更好。请查看@aix 的已编辑答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 2018-07-26
    • 2022-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多