在select查询中执行重复聚合函数的性能

时间:2014-05-05 15:25:32

标签: mysql sql performance

我有一个SQL查询(在MySQL中),它选择一个总数和一个已完成任务有多少表的计数:

SELECT
  count(*) as total, IF(SUM(NOT `completed`) IS NULL,0,SUM(NOT `completed`)) as incomplete
FROM
  tasks

表格可以是:

CREATE TABLE `tasks` (
  `clave` int(11) NOT NULL AUTO_INCREMENT,
  `completed` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'If it is 0 isn\'t completed, otherwise is completed',
  PRIMARY KEY (`clave`)
) ENGINE=InnoDB;

您可以在查询中观察到我已经使用了SUM(NOT completed)两次,一次检查它是否将返回null(并返回0),另一次返回计数(当它不为空时)。如果表(或查询)为空,则SUM()函数返回null,即表格根本没有行。

当您尝试SUM两次时,我认为MySQL会计算两次总和。

我已经测试过如果我可以为列设置别名然后在IF中使用它,那么mysql不需要重新计算它。

作为一项要求,不完整的列在任何情况下都不能为空(例如,如果表任务为空)。

我的问题,效率如何? MySQL是否需要每次重新计算总和还是记住它?

5 个答案:

答案 0 :(得分:4)

这可能是更好的方法,因为您对效果的计算量较少

使用发布的richard模式 - 请参阅FIDDLE

SELECT 
    total, 
    num_complete, 
    total - num_complete as num_incomplete
FROM(
    SELECT
        COUNT(*) as total,
        SUM(IF(t.completed > 0, 1, 0)) as num_complete
    FROM status_log t
) as t

检查每个OP请求的空表

SELECT 
    total, 
    num_complete, 
    total - num_complete as num_incomplete
FROM(
    SELECT
        COUNT(*) as total,
        IF(COUNT(*) > 0, SUM(IF(t.completed > 0, 1, 0)), 0) as num_complete
    FROM status_log t
) as t

你应该用另一种编程语言检查空表或空表 ...一般来说SQL应该用来从表中查询..不是在它是空的时候......如果它是完全空的,那么在用另一种编程语言运行此查询时应该检查空响应。这将大大提高它的性能

答案 1 :(得分:2)

效率(或缺乏效率)将更多地与表中存在的索引以及存储引擎本身有关。同样,结果是否存储在缓存中将更多地与存储引擎有关,而不是与您正在编写的语句有关。

如果我在基于INNODB的存储引擎上写这个,我会做以下事情:

 SELECT   
       count(*) as total,    
       SUM(CASE WHEN completed = 0 OR completed IS NULL THEN  1 ELSE 0 END) AS incomplete 
 FROM tasks;

我会将我的“已完成”列编入索引以便执行此操作。

我将从“IF”更改为case-when的原因主要是代码可移植性。 CASE WHEN如果需要,将更容易移动到其他数据库。

此外,完成的索引将允许此查询简单地评估索引,而不是表值本身。使用LRU,应该为您提供充足的效率。

答案 2 :(得分:2)

以下是使用CASE语句的解决方案。您可以从单个SQL语句中获取两个聚合值。第二个查询示例显示了CASE语句与直接聚合查询语句相比如何为您提供数据透视输出:

[SQL Fiddle][1]

MySQL 5.5.32架构设置

CREATE TABLE status_log 
    (
     id int auto_increment primary key, 
     type varchar(20), 
     status varchar(30)
    );

INSERT INTO status_log
(type, status)
VALUES
('Alpha', 'COMPLETE'),
('Bravo', 'INCOMPLETE'),
('Charlie', 'INCOMPLETE'),
('Delta', 'COMPLETE'),
('Echo', 'COMPLETE'),
('Foxtrot', 'INCOMPLETE'),
('Golf', 'COMPLETE'),
('Hotel', 'COMPLETE')

查询1

SELECT count(1) as count_by_status, status
  FROM status_log
 GROUP BY status

<强> Results

| COUNT_BY_STATUS |     STATUS |
|-----------------|------------|
|               5 |   COMPLETE |
|               3 | INCOMPLETE |

查询2

SELECT count(*) as total_count, 
  sum(case when status = 'COMPLETE' then 1
           else 0 end) as completed_count,
  sum(case when status = 'INCOMPLETE' then 1
           else 0 end) as incomplete_count
  FROM status_log

<强> Results

| TOTAL_COUNT | COMPLETED_COUNT | INCOMPLETE_COUNT |
|-------------|-----------------|------------------|
|           8 |               5 |                3 |

答案 3 :(得分:0)

如果您将其设置为内嵌视图,则可以从查询中多次选择,如下所示:

SELECT total, incomplete, incomplete
FROM
(
SELECT
  count(*) as total, IF(SUM(NOT `completed`) IS NULL,0,SUM(NOT `completed`)) as incomplete
FROM
  tasks
) incomplete_count;

如果您从子查询或内联视图中多次选择,则MySQL不必重新计算它。此外,您可以在外部查询中执行任何其他操作/过滤器。

答案 4 :(得分:-1)

感谢John Ruddell和PhoneixS的澄清。这是一个使用CASE WHEN的版本:

select 
  count(*),
  sum(
   case completed
    when 1 then 1 
    else 0
   end) 'completed',
  sum(
   case when completed is null then 1 
    else 0
   end) 'incomplete'
from tasks;

SQL Fiddle