一、select 1 from table;
与 select anycol(目的表集合中的任意一行) from tab;
与 select * from tab;
从作用上来说是没有差别的,都是查看是否有记录,一般是作条件查询用的。select 1 from tab 中的 1 是一常量(可以为任意数值),查到的所有行的值都是它,但从效率上来说,1>anycol>*,因为不用查字典表。
注意:
当只关心数据表有多少记录行而不需要知道具体的字段值时,类似“select 1 from tab”是一个很不错的 SQL 语句写法,它通常用于子查询。
这样可以减少系统开销,提高运行效率。因为这样写的 SQL 语句,数据库引擎就不会去检索数据表里每条具体的记录和每条记录里每个具体的字段值并将它们放到内存里,而是根据查询到有多少记录行存在就输出多少个“1”,每个“1”代表有 1 行记录,同时选用数字 1 还因为它所占用的内存空间最小,当然用数字 0 的效果也一样。
测试:
select 1 from tab
增加临时列,每行的列值是写在 select 后的数。这条 sql 语句中是 1。select count(1) from tab
不管 count(a) 的 a 值如何变化,得出的值总是 tab 表的行数。select sum(1) from tab
计算临时列的和。
Oracle 中用 1 测试,结果如下:
- 测试结果,得出一个行数和 tab 表行数一样的临时列,每行的列值是 1;
- 得出一个数,该数是 tab 表的行数;
- 得出一个数,该数是 tab 表的行数;
然后又用 2 测试,结果如下:
- 得出一个行数和 tab 表行数一样的临时列,每行的列值是 2;
- 得出一个数,该数是 tab 表的行数;
- 得出一个数,该数是 tab 表的行数 ×2 的数。
再用不同的数测试:
- 得出一个行数和 tab 表行数一样的临时列,每行的列值是写在 select 后的数;
- 还是得出一个数,该数是 tab 表的行数;
- 得出一个数,该数是 tab 表的行数 × 写在 select 后的数。
综上所述:第一种的写法是增加临时列,每行的列值是写在 select 后的数;第二种是不管 count(a) 的 a 值如何变化,得出的值总是 tab 表的行数;第三种是计算临时列的和。
二、count(列名) 、count(常量) 、count(*)
【强制】不要使用 count(列名) 或 count(常量) 来替代 count(*)。count(*) 是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*) 会统计值为 NULL 的行,而 count(列名) 不会统计此列为 NULL 值的行。
【强制】 count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col 1, col 2 ) 如果其中一列全为 NULL ,那么即使另一列有不同的值,也返回为 0。
【强制】当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为 NULL ,因此使用 sum() 时需注意 NPE 问题。
正例:可以使用如下方式来避免 sum 的 NPE 问题:
select if ( isNull ( sum(g) ) ,0, sum(g) ) from table;
1️⃣count(1) and count(*)
当表的数据量大些时,对表作分析之后,使用 count(1) 还要比使用 count(*) 用时多!
从执行计划来看,count(1) 和 count(*) 的效果是一样的。但是在表做过分析之后,count(1) 会比 count(*) 的用时少些(1w以内数据量),不过差不了多少。
如果 count(1) 是聚集索引时,那肯定是 count(1) 快,但是差的很小。因为 count(*),自动会优化指定到那一个字段。所以没必要去 count(1),用 count(*),sql 会完成优化的。因此:在有聚集索引时 count(1) 和 count(*) 基本没有差别!
2️⃣count(1) and count(字段)主要区别
- count(1) 会统计表中的所有的记录数,包含字段为 null 的记录。
- count(字段) 会统计该字段在表中出现的次数,忽略字段为 null 的情况。即不统计字段为 null 的记录。
3️⃣count(*) 和 count(1)和count(列名)区别
执行效果上:
- count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
- count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
- count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
执行效率上:
- 列名为主键,count(列名) 会比 count(1) 快
- 列名不为主键,count(1) 会比 count(列名) 快
- 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)
- 如果有主键,则 select count(主键) 的执行效率是最优的
如果表只有一个字段,则 select count(*) 最优。
4️⃣实例分析
create table tab
(name char(1),
age char(2));
insert into tab values
('a', '14'),
('a', '15'),
('a', '15'),
('b', NULL),
('b', '16'),
('c', '17'),
('d', null),
('e', '');
select
name,count(name),count(1),count(*),count(age),count(distinct(age))
from tab
group by name;
三、SQL 查找是否“存在”
根据条件从数据库表中查询『有』与『没有』,只有两种状态。
1️⃣常见写法
业务代码中,需要根据一个或多个条件,查询是否存在记录,不关心有多少条记录。普遍的 SQL 写法如下:
select count(*) from table where a = 1 AND b = 2;
业务代码:
##### Java写法:
int num = countDao.countDataByCondition(params);
if ( num > 0 ) {
//当存在时,执行这里的代码
} else {
//当不存在时,执行这里的代码
}
2️⃣优化之后
select 1 from table where a = 1 AND b = 2 limit 1;
业务代码:
##### Java写法:
Integer exist = existDao.existDataByCondition(params);
if ( exist != NULL ) {
//当存在时,执行这里的代码
} else {
//当不存在时,执行这里的代码
}
sql 不再使用 count,而是改用 limit 1,让数据库查询时遇到一条就返回,不要再继续查找还有多少条。明知只有或者只要一条查询结果,使用 “limit 1”,可以避免全表扫描,找到对应结果就不会再继续扫描了。