# 第三章 SQL聚合函数 COUNT(一) 返回表或指定列中的行数的聚合函数。 # 大纲 ```sql COUNT(*) COUNT([ALL | DISTINCT [BY(col-list)]] expression [%FOREACH(col-list)] [%AFTERHAVING]) ``` # 参数 - `*` - 指定应计算所有行以返回指定表中的总行数。 `COUNT(*)`不接受其他参数,不能与`ALL`或`DISTINCT`关键字一起使用。 `COUNT(*)`不接受表达式参数,也不使用任何特定列的信息。 `COUNT(*)`返回指定表或视图中的行数,但不消除重复项。 它分别计数每一行,包括包含`NULL`值的行。 - `ALL` - 可选-指定`COUNT`返回表达式中所有值的计数。 如果没有指定关键字,这是默认值。 - `DISTINCT` - 可选-一个`DISTINCT`子句,指定`COUNT`返回表达式的不同(唯一)值的计数。 不能与流字段一起使用。 `DISTINCT`可以指定`BY(colo -list)`子句,其中`colo -list`可以是单个列名,也可以是用逗号分隔的列名列表。 - `expression` - 任何有效的表达式。 通常是包含要计算的数据值的列的名称。 - `%FOREACH(col-list)` - 可选-列名或以逗号分隔的列名列表。 - `%AFTERHAVING` - 可选-应用在`HAVING`子句中的条件。 `COUNT`返回`BIGINT`数据类型。 # 描述 `COUNT`聚合函数有两种形式: - `COUNT(expression)`以整数形式返回表达式中值的数目的计数。 通常,表达式是查询返回的多行中字段的名称(或包含一个或多个字段名称的表达式)。 `COUNT(表达式)`不计算`NULL`值。 它可以选择计数或不计数重复的字段值。 `COUNT`总是返回数据类型`BIGINT,` `xDBC`长度为`8`,精度为`19`,刻度为`0`。 - `COUNT(*)`以整数形式返回表中行数的计数。 `COUNT(*)`计数所有行,无论是否存在重复的字段值或`NULL`值。 `COUNT`可以在引用表或视图的`SELECT`查询或子查询中使用。 `COUNT`可以在`SELECT`列表或`HAVING`子句中与普通字段值一起出现。 `COUNT`不能用于`WHERE`子句。 `COUNT`不能在`JOIN`的`ON`子句中使用,除非`SELECT`是子查询。 与所有聚合函数一样,`COUNT(expression)`可以接受一个可选的`DISTINCT`子句。 `DISTINCT`子句只计算那些具有不同(唯一)值的列。 什么是一个不同的值取决于字段的排序; 当字段具有默认的排序规则`%SQLUPPER`时,字母大小写不同的值将不作为不同的值计算。 要将每个字母大小写变量作为一个不同的值进行计数,请使用`count (distinct (%EXACT(field)))`。 `COUNT DISTINCT`不将`NULL`视为一个不同的值。 `COUNT(DISTINCT BY(col2) col1)`计数不同的`col2`值的`col1`值; 但是,不同的`col2`值可以包含一个`NULL`作为不同的值。 `ALL`关键字统计所有非`null`值,包括所有重复值。 如果没有指定关键字,`ALL`是默认行为。 # 没有行返回 如果没有选择行,`COUNT`返回`0`或`NULL`,这取决于查询: - 如果除了提供给聚合函数的字段之外,选择列表不包含对`FROM`子句表中的字段的任何引用,那么`COUNT`返回`0`。 只有`COUNT`聚合函数返回`0`; 其他聚合函数返回`NULL`。 该查询返回`%ROWCOUNT`为`1`。 如下示例所示: ```java ClassMethod Count() { s myquery = 3 s myquery(1) = "SELECT COUNT(*) AS Recs,COUNT(Name) AS People," s myquery(2) = "AVG(Age) AS AvgAge,MAX(Age) AS MaxAge,CURRENT_TIMESTAMP AS Now" s myquery(3) = " FROM Sample.Employee WHERE Name %STARTSWITH 'ZZZ'" s tStatement = ##class(%SQL.Statement).%New() s qStatus = tStatement.%Prepare(.myquery) if qStatus'=1 { w "%Prepare failed:" d $System.Status.DisplayError(qStatus) q } s rset = tStatement.%Execute() d rset.%Display() w !,"Rowcount:",rset.%ROWCOUNT } ``` ```java DHC-APP> d ##class(PHA.TEST.SQLCommand).Count() Recs People AvgAge MaxAge Now 0 0 2021-12-20 20:58:17 1 Rows(s) Affected Rowcount:1 ``` - 如果`select-list`包含对`FROM`子句表中某个字段的任何直接引用,或者如果指定了`TOP 0`,那么`COUNT`返回`NULL`。 该查询返回`%ROWCOUNT`为`0`。 以下示例不返回`COUNT`值,因为`%ROWCOUNT`值为`0`: ```java ClassMethod Count1() { s myquery = 2 s myquery(1) = "SELECT COUNT(*) AS Recs,COUNT(Name) AS People,$LENGTH(Name) AS NameLen" s myquery(2) = " FROM Sample.Employee WHERE Name %STARTSWITH 'ZZZ'" s tStatement = ##class(%SQL.Statement).%New() s qStatus = tStatement.%Prepare(.myquery) if qStatus'=1 { w "%Prepare failed:" d $System.Status.DisplayError(qStatus) q } s rset = tStatement.%Execute() d rset.%Display() w !,"Rowcount:",rset.%ROWCOUNT } ``` ```java DHC-APP> d ##class(PHA.TEST.SQLCommand).Count1() Recs People NameLen 0 Rows(s) Affected Rowcount:0 ``` - 如果没有指定表,`COUNT(*)`返回1。 该查询返回`%ROWCOUNT`为1。 如下示例所示: ```java ClassMethod Count2() { s myquery = "SELECT COUNT(*) AS Recs" s tStatement = ##class(%SQL.Statement).%New() s qStatus = tStatement.%Prepare(myquery) if qStatus'=1 { w "%Prepare failed:" d $System.Status.DisplayError(qStatus) q } s rset = tStatement.%Execute() d rset.%Display() w !,"Rowcount:",rset.%ROWCOUNT } ``` ```java DHC-APP> d ##class(PHA.TEST.SQLCommand).Count2() Recs 1 1 Rows(s) Affected Rowcount:1 ``` # 流字段 可以使用`COUNT`(表达式)来计数流字段值,但有一些限制。 `COUNT(streamfield)`计算所有非`null`值。 它不会检查重复的值。 当`expression`是一个流字段时,不能指定`COUNT`函数的`DISTINCT`关键字。 试图在流字段中使用`DISTINCT`关键字会导致`SQLCODE -37`错误。 不能在`%FOREACH`冒号列表中指定流字段。 尝试这样做会导致`SQLCODE -37`错误。 下面的例子显示了`COUNT`函数的有效使用,其中`Title`是字符串字段,`Notes`和`Picture`是流字段: ```sql SELECT DISTINCT Title,COUNT(Notes),COUNT(Picture %FOREACH(Title)) FROM Sample.Employee ``` 当`Title`为字符串字段,`Notes`和`Picture`为流字段时,以下示例无效: ```sql -- Invalid: DISTINCT keyword with stream field SELECT Title,COUNT(DISTINCT Notes) FROM Sample.Employee ``` ```sql -- Invalid: %FOREACH col-list contains stream field SELECT Title,COUNT(Notes %FOREACH(Picture)) FROM Sample.Employee ```