mysql中sum()和count()的使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql中sum()和count()的使用相关的知识,希望对你有一定的参考价值。

小弟php菜鸟一枚,想请教一下sql语句的一点问题:
我知道sum()是求和,count()是求条数。
对于sum(1),count(1)中,加了这个参数1是什么意思?
另,count(*)和count(name)有什么区别,使用哪个更好?

小弟mysql用得稀烂,有没有什么好的教程或博客推荐,非常感谢!!

sum(1)就是1这个数值,count(1)相当于第一列的名字,虽然统计行结果是一样的,从效率上还是推荐count。显然对于*和name知道主键列的直接指定会更好,不知道的话用星号也无妨。 参考技术A count(*),是合并完全相同的行,得到相同的行的行数. *代表所有的列及所有的行的意思.

count(name)有合并单列完全相同的行,得到相同的行的行数
参考技术B

我们知道,MySQL 一直依赖对 count(*) 的执行很头疼。很早的时候,MyISAM 引擎自带计数器,可以秒回;不过 InnoDB 就需要实时计算,所以很头疼。以前有多方法可以变相解决此类问题,比如:1. 模拟 MyISAM 的计数器比如表 ytt1,要获得总数,我们建立两个触发器分别对 insert/delete 来做记录到表 ytt1_count,这样只需要查询表 ytt1_count 就能拿到总数。ytt1_count 这张表足够小,可以长期固化到内存里。不过缺点就是有多余的触发器针对 ytt1 的每行操作,写性能降低。这里需要权衡。


2. 用 MySQL 自带的 sql_calc_found_rows 特性来隐式计算

依然是表 ytt1,不过每次查询的时候用 sql_calc_found_rows 和 found_rows() 来获取总数,比如:

    1 row in set, 1 warning (0.00 sec)

    这样的好处是写法简单,用的是 MySQL 自己的语法。缺点也有,大概有两点:1. sql_calc_found_rows 是全表扫。2. found_rows() 函数是语句级别的存储,有很大的不确定性,所以在 MySQL 主从架构里,语句级别的行级格式下,从机数据可能会不准确。不过行记录格式改为 ROW 就 OK。所以最大的缺点还是第一点。

    从 warnings 信息看,这种是 MySQL 8.0 之后要淘汰的语法。

    3. 从数据字典里面拿出来粗略的值


    那这样的适合新闻展示,比如行数非常多,每页显示几行,一般后面的很多大家也都不怎么去看。缺点是数据不是精确值。

    4. 根据表结构特性特殊的取值

    这里假设表 ytt1 的主键是连续的,并且没有间隙,那么可以直接  mysql> select max(id) as cnt from ytt1;    +------+    | cnt  |    +------+    | 3072 |    +------+    1 row in set (0.00 sec)


    不过这种对表的数据要求比较高。


    5. 标准推荐取法(MySQL 8.0.17 建议)

    MySQL 8.0 建议用常规的写法来实现。

    第五种写法是 MySQL 8.0.17 推荐的,也就是说以后大部分场景直接实时计算就 OK 了。MySQL 8.0.17 以及在未来的版本都取消了sql_calc_found_rows 特性,可以查看第二种方法里的 warnings 信息。相比 MySQL 5.7,8.0 对 count(*) 做了优化,没有必要在用第二种写法了。我们来看看 8.0 比 5.7 在此类查询是否真的有优化?MySQL 5.7

    请点击输入图片描述

MYSQL sum() 用于不同的行

【中文标题】MYSQL sum() 用于不同的行【英文标题】:MYSQL sum() for distinct rows 【发布时间】:2011-01-27 00:50:56 【问题描述】:

我正在寻找在我的 SQL 查询中使用 sum() 的帮助:

SELECT links.id, 
       count(DISTINCT stats.id) as clicks, 
       count(DISTINCT conversions.id) as conversions, 
       sum(conversions.value) as conversion_value 
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
GROUP BY links.id 
ORDER BY links.created desc;

我使用DISTINCT 是因为我正在执行“分组依据”,这样可以确保同一行不会被多次计算。

问题在于 SUM(conversions.value) 不止一次计算每一行的“值”(由于 group by)

我基本上想为每个 DISTINCT conversions.id 做SUM(conversions.value)

这可能吗?

【问题讨论】:

发布完整的查询会很有帮助。如何使用 GROUP BY 复制值? 你在加入吗?你应该发布你的查询。根据查询,您可以使用几个选项。 我用完整的查询更新了我的问题 显然,你可以这样做SUM(DISTINCT column) 【参考方案1】:
Select sum(x.value) as conversion_value,count(x.clicks),count(x.conversions)
FROM
(SELECT links.id, 
       count(DISTINCT stats.id) as clicks, 
       count(DISTINCT conversions.id) as conversions,
       conversions.value,       
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
GROUP BY conversions.id) x
GROUP BY x.id 
ORDER BY x.created desc;

我相信这会给你你正在寻找的答案。

【讨论】:

【参考方案2】:

如需了解您看到错误数字的原因,请read this

我认为杰罗姆已经掌握了导致您的错误的原因。 Bryson 的查询可以工作,但在 SELECT 中使用该子查询可能效率低下。

【讨论】:

感谢您的回答!我建议到达此页面的每个人都阅读链接的文章。这是对联接和分组的简明、居高临下和滑稽的解释,并为问题提供了适当的解决方案。【参考方案3】:

这可以解决问题,只需将总和除以重复的对话 id 的计数。

SELECT a.id,
       a.clicks,
       SUM(a.conversion_value/a.conversions) AS conversion_value,
       a.conversions
FROM (SELECT links.id, 
       COUNT(DISTINCT stats.id) AS clicks, 
       COUNT(conversions.id) AS conversions, 
       SUM(conversions.value) AS conversion_value 
      FROM links 
      LEFT OUTER JOIN stats ON links.id = stats.parent_id 
      LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
      GROUP BY conversions.id,links.id
      ORDER BY links.created DESC) AS a
GROUP BY a.id

【讨论】:

【参考方案4】:

Jeromes 解决方案实际上是错误的,可能会产生不正确的结果!!

sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value

让我们假设下表

conversions
id value
1 5
1 5
1 5
2 2
3 1

不同 id 的正确值总和为 8。 杰罗姆的公式产生:

sum(conversions.value) = 18
count(distinct conversions.id) = 3
count(*) = 5
18*3/5 = 9.6 != 8

【讨论】:

假设conversions.id 是一个唯一字段,JOIN 不可能产生3 行conversions.id = 1 并且只有1 行conversions.id = 2。假设conversions.id = 1 .id 是唯一的,是隐含的,可能应该是明确的,但除此之外,公式是可靠的。 9.6 还是10.8?。顺便问一下,除了多级子查询或者join(子查询),还有什么解决办法吗? @Jonathan 鉴于 OP 的确切查询,您是对的。如果这种情况是在一个更大的查询中,Clemens 是正确的,因为其他连接导致多行相同的conversions.id。不幸的是,这就是我现在面临的情况。 @Rikaelus 公平点。我想我没有想到的另一个隐含假设是使用的确切 JOIN。在 OP 查询中,GROUP BY 在主键 links.id 上,conversions.value 上有一个连接表/字段,并且没有其他连接来自转换表。最后一个子句非常重要 如果您所处的场景涉及更多直接从links 表连接,您可能 能够执行类似的技巧来稍微向后计算总和。否则,您可能不得不退回到依赖子查询或只是多个 SQL 查询。祝你好运!【参考方案5】:

这样的事情怎么样:

select l.id, count(s.id) clicks, count(c.id) clicks, sum(c.value) conversion_value
from    (SELECT l.id id, l.created created,
               s.id clicks,  
               c.id conversions,  
               max(c.value) conversion_value                    
        FROM links l LEFT
        JOIN stats s ON l.id = s.parent_id LEFT
        JOIN conversions c ON l.id = c.link_id  
        GROUP BY l.id, l.created, s.id, c.id) t
order by t.created  

【讨论】:

【参考方案6】:

使用以下查询:

SELECT links.id
  , (
    SELECT COUNT(*)
    FROM stats
    WHERE links.id = stats.parent_id
  ) AS clicks
  , conversions.conversions
  , conversions.conversion_value
FROM links
LEFT JOIN (
  SELECT link_id
    , COUNT(id) AS conversions
    , SUM(conversions.value) AS conversion_value
  FROM conversions
  GROUP BY link_id
) AS conversions ON links.id = conversions.link_id
ORDER BY links.created DESC

【讨论】:

【参考方案7】:

我可能错了,但据我了解

conversions.idconversions 表的主键 stats.id 是您的表 stats主键

因此,对于每个 Conversions.id,您最多有一个 links.id 受到影响。

你的要求有点像做 2 组的笛卡尔积:

[clicks]
SELECT *
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 

[conversions]
SELECT *
FROM links 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 

对于每个链接,您会得到 sizeof([clicks]) x sizeof([conversions]) 行数

正如您所指出的,您的请求中的唯一转化次数可以通过

count(distinct conversions.id) = sizeof([conversions])

这个 distinct 设法删除笛卡尔积中的所有 [clicks] 行

但很明显

sum(conversions.value) = sum([conversions].value) * sizeof([clicks])

在你的情况下,因为

count(*) = sizeof([clicks]) x sizeof([conversions])
count(*) = sizeof([clicks]) x count(distinct conversions.id)

你有

sizeof([clicks]) = count(*)/count(distinct conversions.id)

所以我会用

测试您的请求
SELECT links.id, 
   count(DISTINCT stats.id) as clicks, 
   count(DISTINCT conversions.id) as conversions, 
   sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value 
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
GROUP BY links.id 
ORDER BY links.created desc;

让我知道! 杰罗姆

【讨论】:

太好了,当您不想处理大型数据集无法接受的依赖子查询解决方案时,此解决方案非常完美且非常通用。 Jeromes 解决方案实际上是错误的,可能会产生不正确的结果!!请参阅下面的答案。 @ClemensValiente,Jerome 的解决方案是正确的,鉴于 conversions.id 是转换表上的唯一列。这可能是一个重要的区别,应该在答案中注明。编辑——实际上,它被声明(conversions.id 是你的表转换的主键) 还有什么方法更好,子查询还是杰罗姆斯解决方案 这不是一个充分的答案,因为它除以连接中的总行数,如果父表在连接表中有多个关系,这将产生不需要的结果。【参考方案8】:

我使用子查询来执行此操作。它消除了分组的问题。 所以查询会是这样的:

SELECT COUNT(DISTINCT conversions.id)
...
     (SELECT SUM(conversions.value) FROM ....) AS Vals

【讨论】:

用我的完整查询更新了问题。我不确定如何将子查询集成到我拥有的内容中以及它会如何影响性能。 子查询通常会对性能产生负面影响。为了尽量减少影响,请确保所有子查询都作用于索引。

以上是关于mysql中sum()和count()的使用的主要内容,如果未能解决你的问题,请参考以下文章

SQL sum和count的连接关系

mysql 带条件的sum/count 使用技巧

mysql 带条件的sum/count 使用技巧

Mysql中sum()与if()联合使用

Mysql中sum()与if()联合使用

Mysql sum(if())和count(if())的用法