对于绝对性能,SUM 更快还是 COUNT 更快?

如何解决对于绝对性能,SUM 更快还是 COUNT 更快?

你已经自己回答了这个问题。我有几点要补充:

在(和其他支持该boolean类型的 RDBMS)中,您可以boolean直接使用测试结果。将其转换为integerand SUM()

SUM((amount > 100)::int))

NULLIF()或者在表达式中使用它and COUNT()

COUNT(NULLIF(amount > 100, FALSE))

或者用一个简单的OR NULL

COUNT(amount > 100 OR NULL)

或其他各种表达方式。。COUNT()通常比 快得多SUM()。不像SUM()和像保罗已经评论过的,COUNT()永远不会返回NULL,这可能很方便。

它比上述所有方法约 5 - 10 %:

COUNT(*) FILTER (WHERE amount > 100)

查询和你的测试用例一样简单,只有一个计数,没有别的,你可以重写:

SELECT count(*) FROM tbl WHERE amount > 100;

…这是真正的性能之王,即使没有索引。 使用适用的索引,速度可以提高几个数量级,尤其是使用仅索引扫描时。

基准

Postgres 13

db<>在这里

与下面的 Postgres 10 的结果基本相同。(我还在小提琴中添加了一个没有新并行性的测试,以比较苹果和苹果。)

Postgres 10

我为 Postgres 10 进行了一系列新的测试,包括聚合FILTER子句和演示索引对大小计数的作用。

简单设置:

CREATE TABLE tbl (
   tbl_id int
 , amount int NOT NULL
);

INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM   generate_series (1, 1000000) g;

-- only relevant for the last test
CREATE INDEX ON tbl (amount);

由于背景噪音和测试台的具体情况,实际时间会有很大差异。从一组更大的测试中显示典型的最佳时间。这两种情况应该抓住本质:

计数〜所有行的 1%

SELECT COUNT(NULLIF(amount > 148, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int)                      FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END)      FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL)                 FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148)          FROM tbl; -- 118 ms -- !

SELECT count(*) FROM tbl WHERE amount > 148; -- without index  --  75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index     --   1.4 ms -- !!!

db<>在这里

计数〜所有行的 33%

SELECT COUNT(NULLIF(amount > 100, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int)                      FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END)      FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL)                   FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100)          FROM tbl; -- 132 ms -- !

SELECT count(*) FROM tbl WHERE amount > 100; -- without index  -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index     --  55 ms -- !!!

db<>在这里

每组中的最后一个测试使用扫描,这就是为什么它有助于计算所有行的三分之一。当涉及大约 5% 或更多的行时,普通索引或位图索引扫描无法与顺序扫描竞争。

Postgres 9.1 的旧测试

为了验证我EXPLAIN ANALYZE对 PostgreSQL 9.1.6 中的真实表进行了快速测试。

184568 行中的 74208 行符合条件kat_id > 50。所有查询都返回相同的结果。我依次运行了 10 次以排除缓存效果,并将最佳结果作为注释附加:

SELECT SUM((kat_id > 50)::int)                      FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE))            FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END)      FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL)                 FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms

性能上几乎没有任何真正的差异。

FILTERPostgres 9.4+ 中的聚合子句

从 Postgres 9.4 开始,有一种干净快速(SQL 标准)的方式:

SELECT count(*) FILTER (WHERE score BETWEEN 0 AND 3)  AS low
     , count(*) FILTER (WHERE score BETWEEN 4 AND 7)  AS mid
     , count(*) FILTER (WHERE score BETWEEN 8 AND 10) AS high
     , count(*)                                       AS total
FROM   foo;
total`加起来和`low`,除非涉及 NULL 或其他值。`mid``high

链接:

另请阅读下文。

Postgres 9.3-

有几个技巧:

@Phil提供了一个带有CASE语句的通用替代方法(除了sum(1),这不是标准方式)。我喜欢使用更短的形式:

SELECT count(score BETWEEN 0 AND 3  OR NULL) AS low
     , count(score BETWEEN 4 AND 6  OR NULL) AS mid
     , count(score BETWEEN 7 AND 10 OR NULL) AS high
     , count(*)                              AS total
FROM   foo;

如果您的值与您的问题中定义的一样(仅0-10可能),请进一步简化:

SELECT count(score < 4 OR NULL)             AS low
     , count(score BETWEEN 4 AND 6 OR NULL) AS mid
     , count(score > 6 OR NULL)             AS high
     , count(*)                             AS total
FROM   foo;

稍微短一点,快一点。

需要注意的是,除了 之外count,这些函数在没有选择行时返回空值。特别是,sumof no rows 返回 null,而不是预期的零,…

  • count(*) 标准方式,比sum(1). 同样,null vs. 0 适用。

这些查询中的任何一个(包括 Phil 的)都将total. 如果这不是可取的,请改用:

count(score) AS total_not_null

db<>fiddle here

解决方法

这与计算符合特定条件的记录数有关,例如invoice amount > $100

我倾向于更喜欢

COUNT(CASE WHEN invoice_amount > 100 THEN 1 END)

但是,这同样有效

SUM(CASE WHEN invoice_amount > 100 THEN 1 ELSE 0 END)

我会认为 COUNT 更可取,原因有两个:

  1. 传达意图,即COUNT
  2. COUNT 可能在某处涉及一个简单的i += 1操作,而 SUM 不能指望它的表达式是一个简单的整数值。

有没有人有关于特定 RDBMS 差异的具体事实?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-