代码之家  ›  专栏  ›  技术社区  ›  zneak

count(*)vs.count(1)vs.count(pk):哪个更好?[复制品]

  •  196
  • zneak  · 技术社区  · 14 年前

    这个问题已经有了答案:

    我经常发现这三种变体:

    SELECT COUNT(*) FROM Foo;
    SELECT COUNT(1) FROM Foo;
    SELECT COUNT(PrimaryKey) FROM Foo;
    

    据我所见,它们都做同样的事情,我发现自己在代码库中使用了这三个代码。但是,我不喜欢用不同的方式做同样的事情。我应该坚持哪一个?他们中有谁比其他两个强吗?

    6 回复  |  直到 5 年前
        1
  •  200
  •   Quibblesome    7 年前

    底线

    要么使用 COUNT(field) COUNT(*) 如果你的数据库允许 COUNT(tableHere) COUNT(tableHere.*) 用这个。

    简而言之,不要使用 COUNT(1) 为了任何事。它是一匹一招小马,很少做你想做的事,在那些罕见的情况下,它相当于 count(*)

    使用 伯爵(*) 计数

    使用 * 对于所有需要计算所有内容的查询,即使对于联接,也要使用 *

    SELECT boss.boss_id, COUNT(subordinate.*)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    但不要使用 伯爵(*) 对于左联接,因为即使子表与父表中的任何内容都不匹配,也将返回1

    SELECT boss.boss_id, COUNT(*)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    使用时不要被那些建议所愚弄 * 在count中,它从您的表中提取整行,并说 * 是慢的。这个 * SELECT COUNT(*) SELECT * 彼此没有关系,他们是完全不同的东西,他们只是共享一个共同的象征,即。 * .

    替代语法

    实际上,如果不允许将字段命名为与其表名相同的名称,RDBMS语言设计器可以 COUNT(tableNameHere) 语义与 伯爵(*) . 例子:

    为了计算行数,我们可以这样做:

    SELECT COUNT(emp) FROM emp
    

    它们可以使事情变得简单:

    SELECT COUNT() FROM emp
    

    对于左连接,我们可以得到:

    SELECT boss.boss_id, COUNT(subordinate)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    但他们不能那样做( 计数(此处为表名) )由于SQL标准允许使用与其表名相同的名称命名字段:

    CREATE TABLE fruit -- ORM-friendly name
    (
    fruit_id int NOT NULL,
    fruit varchar(50), /* same name as table name, 
                    and let's say, someone forgot to put NOT NULL */
    shape varchar(50) NOT NULL,
    color varchar(50) NOT NULL
    )
    

    计数为空

    而且,如果字段的名称与表名匹配,那么将其设置为空也不是一个好的实践。假设你有“香蕉”、“苹果”、“空”、“梨”等值 fruit 字段。这不会计算所有行,只会产生3行,而不是4行。

    SELECT count(fruit) FROM fruit
    

    尽管有些RDBMS会这样做(为了计算表的行数,它接受表名作为count的参数),但这在PostgreSQL中是有效的(如果没有 subordinate 以下两个表中的任何一个表中的字段,即只要字段名和表名之间没有名称冲突):

    选择boss.boss_id,count(下属)
    从老板
    supervisor.boss_id=boss.boss_id上的左联接下属
    按boss.id分组
    

    但如果我们以后再加一个 下属 表中的字段,因为它将计算字段(可以为空),而不是表行。

    为了安全起见,请使用:

    选择boss.boss_id,count(下属。*)
    从老板
    supervisor.boss_id=boss.boss_id上的左联接下属
    按boss.id分组
    

    count(1) :一招小马

    尤其是 伯爵(1) 这是一个 只会一招的小马驹 ,它只在一个表查询上工作良好:

    SELECT COUNT(1) FROM tbl
    

    但是,当您使用连接时,如果不混淆多表查询的语义,这种技巧就不起作用,尤其是您不能编写:

    -- count the subordinates that belongs to boss
    SELECT boss.boss_id, COUNT(subordinate.1)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    那么,这里的计数(1)是什么意思?

    SELECT boss.boss_id, COUNT(1)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    这是……吗?

    -- counting all the subordinates only
    SELECT boss.boss_id, COUNT(subordinate.boss_id)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    或者这个…?

    -- or is that COUNT(1) will also count 1 for boss regardless if boss has a subordinate
    SELECT boss.boss_id, COUNT(*)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    仔细想想,你就能推断出 伯爵(1) 是一样的 伯爵(*) ,不考虑联接类型。但是对于左连接的结果,我们不能塑造 伯爵(1) 工作如下: COUNT(subordinate.boss_id) , COUNT(subordinate.*)

    因此,只需使用以下任一项:

    -- count the subordinates that belongs to boss
    SELECT boss.boss_id, COUNT(subordinate.boss_id)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    在PostgreSQL上工作,很明显您想要计算集合的基数

    -- count the subordinates that belongs to boss
    SELECT boss.boss_id, COUNT(subordinate.*)
    FROM boss
    LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
    GROUP BY boss.id
    

    另一种计算集合基数的方法,非常类似于英语(只是不要使名称与表名相同的列): http://www.sqlfiddle.com/#!1/98515/7

    select boss.boss_name, count(subordinate)
    from boss
    left join subordinate on subordinate.boss_code = boss.boss_code
    group by boss.boss_name
    

    您不能这样做: http://www.sqlfiddle.com/#!1/98515/8

    select boss.boss_name, count(subordinate.1)
    from boss
    left join subordinate on subordinate.boss_code = boss.boss_code
    group by boss.boss_name
    

    您可以这样做,但这样会产生错误的结果: http://www.sqlfiddle.com/#!1/98515/9

    select boss.boss_name, count(1)
    from boss
    left join subordinate on subordinate.boss_code = boss.boss_code
    group by boss.boss_name
    
        2
  •  47
  •   Piotr Dobrogost    5 年前

    它们中的两个总是产生相同的答案:

    • COUNT(*) 统计行数
    • COUNT(1) 还计算行数

    假设 pk 是主键,并且值中不允许为空,则

    • COUNT(pk) 还计算行数

    然而,如果 PK 不约束为非空,则生成不同的答案:

    • COUNT(possibly_null) 统计列中具有非空值的行数 possibly_null .

    • COUNT(DISTINCT pk) 还计算行数(因为主键不允许重复)。

    • COUNT(DISTINCT possibly_null_or_dup) 统计列中非空的非重复值的数目 possibly_null_or_dup .

    • COUNT(DISTINCT possibly_duplicated) 统计列中不同(必须为非空)值的数目 possibly_duplicated 当它有 NOT NULL 条款。

    通常,我写 伯爵(*) ;它是SQL的原始推荐符号。同样,使用 EXISTS 条款,我通常写 WHERE EXISTS(SELECT * FROM ...) 因为这是最初推荐的符号。替代方案不应该有任何好处;优化器应该看穿更模糊的符号。

        3
  •  9
  •   Jarod Elliott    14 年前

    在某些情况下,这将取决于所使用的数据库类型以及表的类型。

    例如,使用mysql, count(*) 在myisam表下速度快,在innodb下速度慢。在InnoDB下,您应该使用 count(1) count(pk) .

        4
  •  6
  •   Community arnoo    7 年前

    Asked and answered before...

    Books on line COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

    “1”是非空表达式,因此它与 COUNT(*) . 乐观者认为它是 琐碎的 所以给出了同样的计划。pk是唯一的且非空的(至少在SQL Server中),因此 COUNT(PK) =计数(*)

    这是一个类似于 EXISTS (SELECT * ... EXISTS (SELECT 1 ...

    看到 ANSI 92 spec ,第6.5节,一般规则,案例1

            a) If COUNT(*) is specified, then the result is the cardinality
              of T.
    
            b) Otherwise, let TX be the single-column table that is the
              result of applying the <value expression> to each row of T
              and eliminating null values. If one or more null values are
              eliminated, then a completion condition is raised: warning-
              null value eliminated in set function.
    
        5
  •  5
  •   ZeissS    14 年前

    至少在Oracle上,它们是相同的: http://www.oracledba.co.uk/tips/count_speed.htm

        6
  •  -1
  •   arunmur    14 年前

    我感觉到性能特性从DBMS到DBMS发生了变化。一切都取决于他们如何选择实施。既然我在甲骨文上做了大量的工作,我就从这个角度讲出来。

    count(*)-在传递给count函数之前将整行提取到结果集中,如果该行不为空,count函数将聚合1

    count(1)-将不提取任何行,而是在WHERE匹配时,用常量值1为表中的每一行调用count。

    count(pk)-对Oracle中的pk进行索引。这意味着Oracle只能读取索引。通常索引B+树中的一行比实际行小很多倍。因此,考虑到磁盘IOPS的速度,与整行相比,Oracle可以通过单块传输从索引中提取更多的行。这将导致更高的查询吞吐量。

    从中可以看到第一个计数是最慢的,最后一个计数是Oracle中最快的。