如果所有列的值都为真,则返回true。

20

在PostgreSQL中,是否有一种更快的方式可以对多行进行if操作?

比如说我有一个表:

ticket | row | archived
1      | 1   | true
1      | 2   | true
1      | 3   | true
2      | 1   | false
2      | 2   | true

我是否可以在票务列下方进行 if 语句判断?如此,ticket = 1 的情况将为真。

true && true && true = true

where ticket = 2 会是 false,因为

false && true = false

还是我应该坚持

SELECT ( (SELECT COUNT(*) FROM table WHERE ticket = 1)
       = (SELECT COUNT(*) FROM table WHERE ticket = 1 AND archived = true) )
3个回答

27

聚合函数 bool_and()

简单、简短、清晰:

SELECT bool_and(archived)
FROM   tbl
WHERE  ticket = 1;

手册:

如果所有输入值都为真,则为 true,否则为 false。

子查询表达式 EXISTS

假设已定义 archivedNOT NULL。更快,但您还需要另外检查是否存在任何具有 ticket = 1 的行,否则对于不存在的票务,您将得到不正确的结果:

SELECT EXISTS (SELECT FROM tbl WHERE ticket=1)
       AND NOT
       EXISTS (SELECT FROM tbl WHERE ticket=1 AND NOT archived);

索引

两种形式都可以使用类似于以下的索引:

CREATE INDEX tbl_ticket_idx ON tbl (ticket);

使用 EXISTS 查询和 优化后的查询语句 都能让查询变得更快,但是 EXISTS 查询更加快速,因为它能在找到第一条匹配行时停止扫描。如果每张票只有几行数据,这不太重要,但如果有很多行数据,就会非常重要。

为了使用 索引仅扫描(index-only scans) ,你需要一个 多列索引,形式如下:

CREATE INDEX tbl_ticket_archived_idx ON tbl (ticket, archived);

在大多数情况下,这种方法是更好的选择,并且适用于任何版本的PostgreSQL。由于数据对齐的原因,将boolean添加到索引的integer中不会使索引增长。成本微乎其微,但有额外的好处。
更新:在Postgres 13中,通过索引去重发生了变化。参考:

然而,索引列会阻止HOT(仅堆)更新。例如,如果一个UPDATE只更改列archived,如果该列未被任何索引使用(以任何方式),则可以进行HOT更新。否则,就不能采用这种快捷方式。更多关于HOT更新的内容:

所有这些都取决于您的实际工作负载。


绝对是一个干净的解决方案!好奇它的性能如何,是否可以利用9.2中的仅索引扫描? - Mike Christensen
1
@Mike:我深入探究了一下,运行了一些测试,并在我的答案中添加了一些关于索引和性能的内容。 - Erwin Brandstetter
@MikeChristensen,感谢您抽出时间回答!不过我会接受Erwin的答案,因为更短的代码将更适合我的查询!性能损失不应该太大,我不希望这个查询在每张票上运行超过几行。谢谢大家! - mouckatron
@mouckatron:我添加了一些关于HOT更新的内容。如果你有很多UPDATE,可能会对你有所帮助。 - Erwin Brandstetter
@ErwinBrandstetter 我一直对您在Postgres方面的知识印象深刻。到目前为止,我已经给您的答案点赞了数十次。 - isapir

5

如何尝试这样的东西:

select not exists (select 1 from table where ticket=1 and not archived)

我认为这可能比比较计数更有优势,因为count可能使用或不使用索引,而你只需要知道该票是否存在任何FALSE行。我认为只需在ticket上创建部分索引即可非常快速。

SQL Fiddle


如果Count是在9.2之前的PostgreSQL版本中使用,它将不会使用索引。我最近才了解到这一点。 - JayC
1
@JayC:当然,count() 函数可以使用索引。如果 select count(*) from foo where some_col = 1 返回的行数不太多,它可以使用 some_col 上的索引。但是,如果你要计算表中的 所有 行(没有任何限制),count() 将不会使用索引。 - user330315
@a_horse_with_no_name - 哦,谢谢你澄清了这个问题!我在最近的一个问题中有点好奇,链接是 this,我看到你也发表了评论。 - Mike Christensen
@a_horse_with_no_name:你说得对;我不应该让Mike把我搞糊涂;-) - JayC

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接