【可复制运行】全面解析 SQL 的 COUNT 函数:从基础到进阶
在开发网站或应用程序时,经常会遇到这样的问题:“用户总数是多少?”、“某个商品分类中有多少种商品?”这时候,SQL 的 COUNT() 函数就派上用场了。
COUNT() 是一个用于统计表中记录(行)数量的聚合函数。乍一看很简单,但根据使用方式的不同,它可以提取出各种信息,是一个非常强大的函数。本文将结合大量“可复制运行”的代码示例,从基础用法到实用技巧,帮助初学者轻松掌握,迈向 Web 开发之路。
本文的目标是让你体验到“原来 SQL 写出来真的能动!”的喜悦。复杂的理论先放一边!先复制代码、运行查看结果,感受 SQL 的乐趣吧!
热身运动:准备示例数据
要尝试 SQL,需要先有一张含有数据的表。本文将创建一张简单的用户列表表 users,以下 SQL 用于创建表并插入测试数据。
本文后续的所有示例代码,都是基于这张 users 表来演示的。
-- 如果表存在就删除(方便多次测试)
DROP TABLE IF EXISTS users;
-- 创建 users 表
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
prefecture TEXT NOT NULL,
email TEXT
);
-- 插入数据
INSERT INTO users (id, name, prefecture, email) VALUES
(1, 'Taro Yamada', 'Tokyo', 'yamada@example.com'),
(2, 'Hanako Suzuki', 'Osaka', 'suzuki@example.com'),
(3, 'Jiro Sato', 'Tokyo', NULL),
(4, 'Sakura Ito', 'Fukuoka', 'ito@example.com'),
(5, 'Saburo Watanabe', 'Hokkaido', 'watanabe@example.com'),
(6, 'Shiro Takahashi', 'Tokyo', 'takahashi@example.com'),
(7, 'Misaki Tanaka', 'Osaka', NULL);
该表包含 ID、姓名、地区和电子邮件字段。注意:Sato(ID 3)和 Tanaka(ID 7)的 email 是 NULL(即为空),这将在后文讲解 COUNT() 的行为时发挥重要作用。
【基础 1】统计所有记录数量:`COUNT(*)`
首先介绍最基本的用法:COUNT(*)。星号(*)表示“所有列”,COUNT(*) 会返回表中所有记录(行)的总数。当你想要简单地问:“这张表里有几条数据?”时,就可以使用这个语法。
现在来统计 users 表中所有用户的数量。
SELECT COUNT(*) FROM users;
执行结果:
7
我们刚才插入了 7 条数据,因此结果返回“7”是正确的。这就是 COUNT() 最基本的用法。
【基础 2】指定列来统计:`COUNT(column_name)`
接下来我们来看如何使用 COUNT(column_name) 来统计某一列中“有值”的记录数量。
重点来了:COUNT(column_name) 会忽略 NULL 值。也就是说,如果某行在该列是空值,则不会被统计在内。
现在来统计 users 表中,已填写电子邮件地址的用户数量。
SELECT COUNT(email) FROM users;
执行结果:
5
结果是“5”,比 COUNT(*) 的“7”要少。这是因为有 2 条记录(Sato 和 Tanaka)在 email 字段为 NULL,因此未被统计。
请记住:COUNT(*) 统计的是总行数,而 COUNT(column_name) 统计的是该列中“非空”的行数。
【进阶 1】为统计结果命名:`AS`
默认情况下,COUNT() 的结果列名显示为 COUNT(*) 或 COUNT(email),可读性不高。
这时可以使用 AS 来为结果列指定一个易懂的别名。
SELECT COUNT(*) AS total_users FROM users;
执行结果:
total_users
-----------
7
通过 AS total_users,结果列被命名为 total_users,一目了然这表示的是用户总数。在程序中也可以通过 result['total_users'] 来访问,非常方便。
【进阶 2】排除重复值:`COUNT(DISTINCT column_name)`
接下来是分析类操作中常用的 COUNT(DISTINCT)。DISTINCT 表示“去重”,可用于统计唯一值的数量。
比如我们想知道:“网站用户来自多少个不同的地区?”
在 users 表中,Tokyo 有 3 人,Osaka 有 2 人。我们来统计地区(prefecture)的唯一值数量。
SELECT COUNT(DISTINCT prefecture) AS unique_prefectures FROM users;
执行结果:
unique_prefectures
------------------
4
结果是“4”。表中包含 Tokyo、Osaka、Fukuoka、Hokkaido 共 4 个不同的地区,统计无误。
这在电商网站统计商品分类数、日志中统计独立访客数量等场景中都有广泛应用。
【进阶 3】按组统计:`GROUP BY`
COUNT() 在与 GROUP BY 子句搭配使用时能发挥最大价值。GROUP BY 会将某列中相同值的记录分组,然后对每个分组使用 COUNT() 进行统计。
比如:“想知道每个地区有多少用户”——这正是 GROUP BY 派上用场的地方。
SELECT
prefecture,
COUNT(*) AS user_count
FROM
users
GROUP BY
prefecture;
执行结果:
prefecture | user_count
-----------|------------
Osaka | 2
Hokkaido | 1
Tokyo | 3
Fukuoka | 1
成功地列出了按地区分组的用户数量!使用 prefecture 列进行分组,并对每组使用 COUNT(*) 统计记录数。
这是网站分析中的重要技巧,比如判断哪个地区的用户最多。
【扩展】为统计结果添加条件:`HAVING`
有时我们希望对 GROUP BY 聚合后的结果添加筛选条件。例如:“只显示用户数在 2 人以上的地区”。
需要注意的是:WHERE 子句在 GROUP BY 聚合之前执行,因此不能用于筛选聚合结果(如 user_count)。
若要对聚合结果进行筛选,应使用 HAVING 子句。
SELECT
prefecture,
COUNT(*) AS user_count
FROM
users
GROUP BY
prefecture
HAVING
user_count >= 2;
执行结果:
prefecture | user_count
-----------|------------
Osaka | 2
Tokyo | 3
通过添加 HAVING user_count >= 2 条件,仅返回了用户数量不少于 2 人的地区:Osaka 和 Tokyo。
请记住:WHERE 用于聚合前的单条记录筛选,而 HAVING 用于 GROUP BY 聚合后的分组结果筛选。
[互动体验区] 在浏览器中运行 SQL 吧!
感谢等待!我们已为您准备好了可以亲自运行所学 SQL 的环境。
请将以下 HTML 代码整块复制,保存为如 sql_test.html 的文件,并在浏览器中打开。本文介绍的 users 表已准备就绪,您可以尝试各种 SQL 语句,尽情享受查看结果变化的乐趣!
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用于 COUNT() 的 SQL 运行环境</title>
<style>
body { font-family: sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
h1 { color: #444; }
textarea { width: 100%; height: 150px; font-family: monospace; font-size: 16px; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #007bff; color: white; border: none; padding: 10px 20px; font-size: 16px; border-radius: 4px; cursor: pointer; }
button:hover { background-color: #0056b3; }
button:disabled { background-color: #ccc; cursor: not-allowed; }
#result-container { margin-top: 2rem; border: 1px solid #ddd; padding: 1rem; border-radius: 4px; background: #f9f9f9; min-height: 50px; }
#error-message { color: #d9534f; font-weight: bold; }
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/sql-wasm.js"></script>
</head>
<body>
<h1>试试 SQL 吧!</h1>
<p>在下面的文本区域输入 SQL,然后点击“运行”按钮。试用本文介绍的各种 SQL 示例!</p>
<textarea id="sql-input">SELECT prefecture, COUNT(*) AS user_count
FROM users
GROUP BY prefecture
HAVING user_count >= 2;</textarea>
<button id="execute-btn">运行</button>
<div id="result-container">
<p id="error-message"></p>
<div id="result-output"></div>
</div>
<script>
const sqlInput = document.getElementById('sql-input');
const executeBtn = document.getElementById('execute-btn');
const errorMsg = document.getElementById('error-message');
const resultOutput = document.getElementById('result-output');
let db;
async function initDb() {
executeBtn.disabled = true;
executeBtn.textContent = '正在准备...';
try {
const SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/${file}`
});
db = new SQL.Database();
// 设置初始表和数据
const setupSql = `
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
prefecture TEXT NOT NULL,
email TEXT
);
INSERT INTO users (id, name, prefecture, email) VALUES
(1, 'Taro Yamada', 'Tokyo', 'yamada@example.com'),
(2, 'Hanako Suzuki', 'Osaka', 'suzuki@example.com'),
(3, 'Jiro Sato', 'Tokyo', NULL),
(4, 'Sakura Ito', 'Fukuoka', 'ito@example.com'),
(5, 'Saburo Watanabe', 'Hokkaido', 'watanabe@example.com'),
(6, 'Shiro Takahashi', 'Tokyo', 'takahashi@example.com'),
(7, 'Misaki Tanaka', 'Osaka', NULL);
`;
db.run(setupSql);
executeBtn.disabled = false;
executeBtn.textContent = '运行';
resultOutput.innerHTML = '<p>准备就绪!输入 SQL 后单击运行。</p>';
} catch (err) {
errorMsg.textContent = '数据库初始化失败:' + err.message;
console.error(err);
}
}
function executeSql() {
if (!db) return;
const sql = sqlInput.value;
errorMsg.textContent = '';
resultOutput.innerHTML = '';
try {
const results = db.exec(sql);
if (results.length === 0) {
resultOutput.innerHTML = '<p>查询成功,但没有返回结果集(例如 INSERT、UPDATE 等)。</p>';
return;
}
results.forEach(result => {
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
const headerRow = document.createElement('tr');
result.columns.forEach(colName => {
const th = document.createElement('th');
th.textContent = colName;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
result.values.forEach(row => {
const bodyRow = document.createElement('tr');
row.forEach(cellValue => {
const td = document.createElement('td');
td.textContent = cellValue === null ? 'NULL' : cellValue;
bodyRow.appendChild(td);
});
tbody.appendChild(bodyRow);
});
table.appendChild(thead);
table.appendChild(tbody);
resultOutput.appendChild(table);
});
} catch (err) {
errorMsg.textContent = 'SQL 错误:' + err.message;
console.error(err);
}
}
executeBtn.addEventListener('click', executeSql);
initDb();
</script>
</body>
</html>
【让我们试试看!】
- 试着统计没有注册邮箱地址的用户数量。(提示:
COUNT(*)和COUNT(email)的差异) - 仅统计籍贯为“东京”的用户数量。(提示:使用
WHERE子句) - 自己执行一条新的
INSERT语句添加数据,然后使用COUNT(*)检查数量是否增加。
需要注意的点与小知识
COUNT(*)vsCOUNT(1)vsCOUNT(column_name)-
有时可以看到
COUNT(1)的写法,它的意思是为每一行赋值 “1” 并进行计数。在大多数数据库中,COUNT(*)与COUNT(1)的行为和性能几乎没有区别,都统计所有行。而COUNT(column_name)不统计NULL值,请再次确认其目的是否不同。如有疑问,建议使用语义明确的COUNT(*)(统计所有行)或COUNT(column_name)(统计非 NULL 值)。 - 性能方面
-
如果在拥有数百万甚至上千万条记录的巨大表上执行
COUNT(*),可能会比较耗时。尤其是当不指定任何条件时,需要扫描整张表的数据。如果你频繁需要获取总记录数,可以考虑其他方法(例如准备汇总表),但只要掌握基本用法就没有问题。
相关功能:其他聚合函数
学会了 COUNT() 之后,其它聚合函数也能很容易掌握。与 GROUP BY 结合使用时,效果尤为强大。
SUM(column_name):计算数值列的总和(例如:总销售额)AVG(column_name):计算数值列的平均值(例如:平均年龄、平均单价)MAX(column_name):获取列中的最大值(例如:最高温度、最高得分)MIN(column_name):获取列中的最小值(例如:最低价格)
例如,如果你想知道用户 ID 的最大值,可以这样写:
SELECT MAX(id) AS latest_user_id FROM users;
总结
本文从基础到进阶,快速介绍了如何使用 COUNT() 函数来统计记录数量。
COUNT(*):统计表中所有记录的数量COUNT(column_name):统计指定列中非NULL的记录数量COUNT(DISTINCT column_name):统计不重复的唯一值数量AS alias:为结果列指定易于理解的别名GROUP BY:与COUNT()结合使用,对每组数据进行汇总
COUNT() 是分析数据并获取洞察的第一步。请多尝试本文的代码,掌握“统计数据”的感觉。当你能熟练运用 SQL 时,你将看到一个全新的世界。让我们继续愉快地学习吧!