一、基础
注意:MySQL语句对大小写不敏感!因此大写或者小写都可以!
1.变量
BOOLEAN #bool类型
INT #4bytes整数
FLOAT #4bytes单精度浮点数
DOUBLE #8bytes双精度浮点数
DECIMAL(M,D) #M为总数,D为小数位数
DATE #YYYY-MM-DD
TIME #HH:MM:SS
YEAR #YYYY
DATETIME #YYYY-MM-DD hh:mm:ss,8bytes不依赖时区
TIMESTAMP #YYYY-MM-DD hh:mm:ss,4bytes依赖时区
CHAR #定长字符串
VARCHAR #变长字符串
BLOB #二进制形式的长文本数据
--也可以这样注释
2.运算符
# 算数运算符(部分+ - *已省略)
/ 或 DIV 除法
% 或 MOD 取余
# 比较运算符(部分> < >= <=已省略)
= 等于 # 注意不是==
<>, != 不等于
BETWEEN 在两值之间 >=min&&<=max
NOT BETWEEN 不在两值之间
IN 在集合中
NOT IN 不在集合中
<=> 严格比较两个NULL值是否相等
LIKE 模糊匹配
REGEXP 正则式匹配
RLIKE 正则式匹配
IS NULL 为空
IS NOT NULL 不为空
# 逻辑运算符
NOT 或 ! 逻辑非
AND 逻辑与
OR 逻辑或
XOR 逻辑异或
二、数据库与表
1.创建数据库
CREATE DATABASE sql_tutorial; #创建数据库,sql_tutorial为名称
CREATE DATABASE `sql_tutorial`; #更加推荐的写法
SHOW DATABASES; #显示已有数据库
USE `sql_tutorial`;#使用该数据库
DROP DATABASE `sql_tutorial`; #删除数据库
2.表的增删与修改
表的组成(列):PRIMARY KEY + VALUES + FOREIGN KEY
其中,
PRIMARY KEY用于唯一标识对应行数据(类似哈希表的key),可以由多个量组合。
FOREIGN KEY用于链接其他表
(1)表的创建与删除
CREATE TABLE `student`( #创建表
`student_id` INT PRIMARY KEY, #名称 类型 特殊条件,将其指定为PRIMARY KEY
`name` VARCHAR(20),
`major` VARCHAR(20)
);
CREATE TABLE `student`( #创建表,更好的写法
`student_id` INT,
`name` VARCHAR(20),
`major` VARCHAR(20),
PRIMARY KEY(`student_id`)
);
DROP TABLE `student` # 表的删除
(2)表的修改
# 为表增加新的项GPA,并指定格式为DECIMAL(3,2)
ALTER TABLE `student` ADD `GPA` DECIMAL(3,2)
# 删除表中的项GPA
ALTER TABLE `student` DROP COLUMN `GPA`
# 在表中插入数据,位置要对应
INSERT INTO `student` VALUES(4,'qiqi',NULL)
# 制定顺序插入数据,空的位置会被记为NULL
INSERT INTO `student`(`name`,`major`,`student_id`) VALUES('play','cong',5)
INSERT INTO `student`(`major`,`student_id`) VALUES('hang',6)
(3)其他指令
DESCRIBE `student`; #显示表信息(数据类型等)
3.属性约束
示例:
CREATE TABLE `student`(
`student_id` INT AUTO_INCREMENT, # 自动递增+1
`name` VARCHAR(20) NOT NULL, # 要求不能为NULL
`major` VARCHAR(20) UNIQUE, # 要求数据不重复
`GPA` DECIMAL(3,2) DEFAULT 0.00 # 设定默认值
PRIMARY KEY(`student_id`)
);
4.数据操作
以下代码可能需要
SET SQL_SAFE_UPDATES = 0; #来禁用安全模式
(1)数据更新
场景:参考二、2中创建的表格。将表格student
中的列major
中的'英语'
,替换成为'外国语言文学'
。
# 示例1:
UPDATE `student` # 指定要更新的表格
SET `major` = '外国语言文学' # 指定新的数据
WHERE `major` = '英语'; # 条件
# 示例2:
UPDATE `student` # 指定要更新的表格
SET `major` = '外国语言文学' # 指定新的数据
WHERE `student_id` = '3'; # 条件
# 示例3:多个条件(或条件)
UPDATE `student` # 指定要更新的表格
SET `major` = '生化' # 指定新的数据
WHERE `major` = '生物' OR `major` = '化学'; # 条件,与条件AND同理
# 示例4:多个数据
UPDATE `student` # 指定要更新的表格
SET `name`='小芳', `major` = '物理' # 指定新的数据
WHERE `student_id` = '2'; # 条件
# 示例:无条件约束
UPDATE `student` # 指定要更新的表格
SET `major` = '物理' # 指定新的数据,所有数据都会被修改
(2)数据删除
# 示例1:
DELETE FROM `student`
WHERE `student_id` = 4;
# 示例2:
DELETE FROM `student`
WHERE `name` = '小惠' AND `major` = '物理';
# 示例3:
DELETE FROM `student`
WHERE `GPA` < 3.50;
# 示例4:清空表格
DELETE FROM `student`
(3)数据查找
# 示例1:显示所有表数据
SELECT * FROM `student`; #*表示全部
# 示例2:显示指定行(属性)的数据
SELECT `name`, `major` FROM `student`;
# 示例3:按照条件排序
SELECT *
FROM `student`
ORDER BY `GPA`; # 排序条件默认ASC升序,额外增加一个DESC在末尾变为降序
# 示例4:按照多个条件排序
SELECT *
FROM `student`
ORDER BY `GPA`, `student_id`; # 若GPA一致,则按照student_id排序
# 示例5:显示部分数据
SELECT *
FROM `student`
LIMIT 2; #只显示前两个数据
# 示例6:按条件显示数据
SELECT *
FROM `student`
WHERE `major` = '英语'; #只回传符合条件的数据
# 示例7:按条件显示数据IN
SELECT *
FROM `student`
WHERE `major` IN('英语','历史','语文'); #等同于OR
四、实例:公司资料库
1.初始化
(1)表格创建与关联
# 创建表格
CREATE TABLE `employee`(
`emp_id` INT PRIMARY KEY,
`name` VARCHAR(20),
`brith_date` DATE,
`sex` VARCHAR(1),
`salary` INT,
`branch_id` INT,
`sup_id` INT
);
# 创建表格
CREATE TABLE `branch`(
`branch_id` INT PRIMARY KEY,
`branch_name` VARCHAR(20),
`manager_id` INT,
FOREIGN KEY (`manager_id`) REFERENCES `employee`(`emp_id`) ON DELETE SET NULL # 建立foreign key
);
# 建立foreign key连接
ALTER TABLE `employee`
ADD FOREIGN KEY(`branch_id`)
REFERENCES `branch`(`branch_id`)
ON DELETE SET NULL;
# 建立foreign key连接
ALTER TABLE `employee`
ADD FOREIGN KEY(`sup_id`)
REFERENCES `employee`(`emp_id`)
ON DELETE SET NULL;
# 创建表格
CREATE TABLE `client`(
`client_id` INT PRIMARY KEY,
`client_name` VARCHAR(20),
`phone` VARCHAR(20)
);
CREATE TABLE `works_with`(
`emp_id` INT,
`client_id` INT,
`total_sales` INT,
PRIMARY KEY(`emp_id`,`client_id`),
FOREIGN KEY(`emp_id`) REFERENCES `employee`(`emp_id`) ON DELETE CASCADE,
FOREIGN KEY(`client_id`) REFERENCES `client`(`client_id`) ON DELETE CASCADE
);
(2)修改数据
branch表格与employee表格:
# 先修改branch表格(防止循环引用出错)
INSERT INTO `branch` VALUES(1,'研发',NULL);
INSERT INTO `branch` VALUES(2,'行政',NULL);
INSERT INTO `branch` VALUES(3,'咨询',NULL);
# 再往employee表格增加数据
INSERT INTO `employee` VALUES(206,'小黄','1998-10-08','F',50000,1,NULL);
INSERT INTO `employee` VALUES(207,'小绿','1998-10-08','M',29000,2,206);
INSERT INTO `employee` VALUES(208,'小黑','1998-10-08','M',35000,3,206);
INSERT INTO `employee` VALUES(209,'小白','1998-10-08','F',39000,3,207);
INSERT INTO `employee` VALUES(210,'小蓝','1998-10-08','F',84000,1,207);
# 最后修改branch表格的数据
UPDATE `branch`
SET `manager_id` = 206
WHERE `branch_id` = 1;
# 最后修改branch表格的数据
UPDATE `branch`
SET `manager_id` = 207
WHERE `branch_id` = 2;
# 最后修改branch表格的数据
UPDATE `branch`
SET `manager_id` = 208
WHERE `branch_id` = 3;
client表格与works_with表格:
# 往client表格新增数据
INSERT INTO `client` VALUES(400,'阿狗','2545156848');
INSERT INTO `client` VALUES(401,'阿猫','48915848');
INSERT INTO `client` VALUES(402,'旺来','2548994518');
INSERT INTO `client` VALUES(403,'露西','1219561');
INSERT INTO `client` VALUES(404,'艾瑞克','51911948');
# 往works_with表格新增数据
INSERT INTO `works_with` VALUES(206,400,'70000');
INSERT INTO `works_with` VALUES(207,401,'24000');
INSERT INTO `works_with` VALUES(208,402,'9800');
INSERT INTO `works_with` VALUES(208,403,'24000');
INSERT INTO `works_with` VALUES(210,404,'87940');
2.数据查找
- 取得所有员工资料
SELECT *
FROM `employee`;
- 取得所有客户资料
SELECT *
FROM `client`;
- 按薪水低到高取得员工资料
SELECT *
FROM `employee`
ORDER BY `salary`;
- 取得薪水前3高的员工
SELECT *
FROM `employee`
ORDER BY `salary` DESC
LIMIT 3;
- 取得所有员工的名字
SELECT `name`
FROM `employee`;
- 取得所有员工的性别(不重复)
SELECT DISTINCT `sex`
FROM `employee`;
3.数据聚合
- 取得员工人数
SELECT COUNT(*) FROM `employee`; # 返回员工总人数
SELECT COUNT(`sup_id`) FROM `employee`; # 返回非NULL的sup_id的数量
- 取得所有出生于:1978-01-01之后的女性员工人数
SELECT COUNT(*)
FROM `employee`
WHERE `birth_date` > '1970-01-01' AND `sex` = 'F';
- 取得所有员工的平均薪水
SELECT AVG(`salary`)
FROM `employee`
- 取得所有员工薪水的总和
SELECT SUM(`salary`)
FROM `employee`
- 取得薪水最高的员工
SELECT MAX(`salary`)
FROM `employee`
- 取得薪水最低的员工
SELECT MIN(`salary`)
FROM `employee`
4.通配符
% 表示多个通配符;
_ 表示一个通配符。
- 取得电话号码尾数是518的客户
SELECT *
FROM `client`
WHERE `phone` LIKE '%518';
- 取得姓艾的客户
SELECT *
FROM `client`
WHERE `client_name` LIKE '艾%';
- 取得生日在12月的员工
SELECT *
FROM `employee`
WHERE `brith_date` LIKE '_____12%';
5.数据联合 UNION
- 员工名字
union
客户名字
SELECT `name` # 变量属性要一致
FROM `employee`
UNION
SELECT `client_name`
FROM `client`;
- 员工id + 员工名子
union
客户id + 客户名字
SELECT `emp_id`,`name` # 变量属性要一致
FROM `employee`
UNION
SELECT `client_id`,`client_name`
FROM `client`;
- 员工薪水
union
销售金额
SELECT `salary` AS `total_money`
FROM `employee`
UNION
SELECT `total_sales`
FROM `works_with`;
注意:
union
重命名时,只需要将第一个表的表头重命名即可。- 若所连接的上下表为重复数据,
union
默认会只保留一个,这时候需要union all
。
6.数据连接 JOIN
用于连接不同表格。
- 取得所有部门经理的名字
SELECT `emp_id`,`name`,`branch_name`
FROM `employee`
JOIN `branch`
ON `emp_id` = `manager_id`;
# 精确写法,处理同属性名的情况
SELECT `emp_id`,`name`,`branch_name`
FROM `employee`
JOIN `branch`
ON `employee`.`emp_id` = `branch`.`manager_id`; # 精确写法
# 左表无条件显示,右表有条件显示
SELECT `emp_id`,`name`,`branch_name`
FROM `employee`
LEFT JOIN `branch`
ON `employee`.`emp_id` = `branch`.`manager_id`;
# 右表无条件显示,左表有条件显示
SELECT `emp_id`,`name`,`branch_name`
FROM `employee` RIGHT JOIN `branch` # 另一种写法
ON `employee`.`emp_id` = `branch`.`manager_id`;
# 起别名法
SELECT `a`.`emp_id`,`a`.`name`,`b`.`branch_name`
FROM `employee` `a`
RIGHT JOIN `branch` `b`
ON `a`.`emp_id` = `b`.`manager_id`;
7.子查询
- 找出研发部门的经理名字
SELECT `name`
FROM `employee`
WHERE `emp_id` = (
SELECT `manager_id`
FROM `branch`
WHERE `branch_name` = '研发'
);
- 找出对单一客户销售金额超出50000的员工的名字
SELECT `name`
FROM `employee`
WHERE `emp_id` = (
SELECT `emp_id`
FROM `work_with`
WHERE `total_sales` > 50000
);
8.ON DELETE
(1)ON DELETE SET NULL
ON DELETE SET NULL
含义解释,示例:
CREATE TABLE `branch`(
`branch_id` INT PRIMARY KEY,
`branch_name` VARCHAR(20),
`manager_id` INT,
FOREIGN KEY (`manager_id`) REFERENCES `employee`(`emp_id`) ON DELETE SET NULL # 建立foreign key
);
若employee表格中小绿的数据被删除,branch表格中对应数据置为NULL
。
(2)ON DELETE CASCADE
ON DELETE CASCADE
含义解释,示例:
CREATE TABLE `works_with`(
`emp_id` INT,
`client_id` INT,
`total_sales` INT,
PRIMARY KEY(`emp_id`,`client_id`),
FOREIGN KEY(`emp_id`) REFERENCES `employee`(`emp_id`) ON DELETE CASCADE,
FOREIGN KEY(`client_id`) REFERENCES `client`(`client_id`) ON DELETE CASCADE
);
若employee表格中小绿的数据被删除,works_with表格中对应数据同步删除。
注意:当一个属性同时为 primary key 和 foreign key 时,不能使用ON DELETE SET NULL
。
五、python连接MySQL
- 安装:
pip install mysql-connector-python
- python代码
import mysql.connector
connection = mysql.connector.connect(host = 'localhost',
port = '3306',
user = 'root',
password = 'password')
cursor = connection.cursor()
六、重点解析
1.常用关键字
(1)ON
- 用于
JOIN
子句中,指定两个表之间的连接条件。 - 连接条件用于确定如何将两个表的行配对。
JOIN
有多个条件时,先筛选再连接。
(2)WHERE
- 用于过滤查询结果集中的行。
- 可以用于任何查询,不仅限于连接。
JOIN
有多个条件时,先连接再筛选。- 相比
ON
效率更低,因此JOIN
优先使用ON
。
(3)AS
用来重命名,很方便,如 leetcode 181 题。
select `e1`.`name` as `Employee`
from `Employee` as `e1`
join `Employee` as `e2`
on `e1`.`managerId` = `e2`.`id` and e1.salary > e2.salary
; # 这里使用where代替on,结果一致
这里将Employee
复制一遍,并分别重命名为e1
和e2
,便于调取操作。
(4)GROUP BY
GROUP BY
子句用于将结果集按一个或多个列进行分组,通常与聚合函数(如 COUNT
、SUM
、AVG
、MAX
、MIN
等)一起使用,以对每个分组进行计算。
示例,假设有一个如下的数据表:
+----+---------+--------+
| id | product | amount |
+----+---------+--------+
| 1 | A | 100 |
| 2 | B | 150 |
| 3 | A | 200 |
| 4 | B | 250 |
| 5 | C | 300 |
+----+---------+--------+
如果想要计算每个产品的销售次数:
SELECT product, COUNT(*) as sales_count
FROM Sales
GROUP BY product;
结果:
+---------+-------------+
| product | sales_count |
+---------+-------------+
| A | 2 |
| B | 2 |
| C | 1 |
+---------+-------------+
(5)HAVING
HAVING
子句用于对分组后的结果集进行过滤,通常与 GROUP BY
子句和聚合函数
一起使用。HAVING
子句的作用类似于 WHERE
子句,但 WHERE
子句不能用于聚合函数,而 HAVING
子句可以。
继续(4)中的示例:
SELECT product
FROM Sales
GROUP BY product
HAVING COUNT(product) > 1;
结果:
+---------+
| product |
+---------+
| A |
| B |
+---------+
(6)DELETE
如 leetcode 196 删除重复的电子邮箱:
Person 表:
+----+------------------+
| id | email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+------------------+
delete `p1`
from `Person` as `p1`, `Person` as `p2`
where `p1`.`email` = `p2`.`email` and `p1`.`id` > `p2`.`id`
;
(7)OVER
OVER
子句用于窗口函数
(也称为分析函数或 OLAP 函数),这些函数允许你在不需要 GROUP BY
子句的情况下对结果集的某些部分进行计算。窗口函数在 SQL 中非常强大,可以用于各种复杂的分析和计算。
窗口函数:
ROW_NUMBER()
分配一个唯一的行号。RANK()
排名,如果有相同的值,它们将获得相同的排名,并且排名中会有跳跃。DENSE_RANK()
与 RANK() 类似,但排名中没有跳跃。NTILE(M)
将结果集划分为指定数量的桶,并为每一行分配一个桶号。桶的数量由参数指定。SUM()
计算分区内指定列的总和。AVG()
计算分区内指定列的平均值。COUNT()
计算分区内的行数。MAX()
计算分区内指定列的最大值。MIN()
计算分区内指定列的最小值。
示例:
窗口函数() OVER ([PARTITION BY 分区列] [ORDER BY 排序列]) # 基本用法
假设有一个名为 employees
的表,包含以下列:id、name、department 和 salary。
- 使用
ROW_NUMBER()
计算每个部门中员工的行号
SELECT
id,
name,
department,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num
FROM
employees;
- 使用
SUM()
计算每个部门的累计工资
SELECT
id,
name,
department,
salary,
SUM(salary) OVER (PARTITION BY department ORDER BY salary) AS cumulative_salary
FROM
employees;
- 使用
RANK()
计算每个部门中员工的排名
SELECT
id,
name,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank
FROM
employees;
注意:PARTITION BY
用来划分分区。比如说 RANK()
函数给高考排名,PARTITION BY
可以根据文理科来划分区域,文科和理科各自单独排名。ORDER BY
则是排名的依据。
(8)IF & IFNULL
用于select
的条件判断。
SELECT IF(condition, value_if_true, value_if_false);
IFNULL
函数用于检查一个表达式是否为 NULL,如果是 NULL,则返回指定的替代值。
SELECT IFNULL(expression, alt_value);
(9)DECLARE
用于声明变量,如 leetcode 177 题,定义一个 INT 类型的 m 变量。
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
DECLARE m INT; # 声明一个变量m
SET m = N-1; # 设置m的数值
RETURN (
# Write your MySQL query statement below.
SELECT IFNULL(
(SELECT DISTINCT salary
FROM Employee
ORDER BY salary DESC
LIMIT 1 OFFSET m), NULL)
);
END;
(10)CASE
类似 IF
,下表用于判断三角形是否成立。
SELECT
x,
y,
z,
CASE
WHEN x + y > z AND x + z > y AND y + z > x THEN 'Yes'
ELSE 'No'
END AS 'triangle'
FROM
triangle
;
(11)CAST
用于数据类型转换,示例:
SELECT CAST('123' AS UNSIGNED) AS int_value;
SELECT CAST(123.45 AS CHAR) AS char_value;
(12)ROUND
用于四舍五入(可用于小数)
SELECT ROUND(123.456, 2) AS rounded_value; -- 结果为 123.46
SELECT ROUND(123.456) AS rounded_value; -- 结果为 123
(13)WITH
WITH
子句(也称为公用表表达式,CTE)用于定义一个临时结果集,可以在后续的 SELECT
、INSERT
、UPDATE
或 DELETE
语句中引用。它的语法如下:
WITH cte_name AS (
-- 子查询
SELECT ...
)
SELECT ...
FROM cte_name
(14)USING
在 MySQL 中,USING
关键字通常用于 JOIN
操作中,以简化连接条件。它用于指定两个表之间的公共列,并且这些列在两个表中具有相同的名称。
假设有如下表:
CREATE TABLE employees (
employee_id INT,
employee_name VARCHAR(50),
department_id INT
);
CREATE TABLE departments (
department_id INT,
department_name VARCHAR(50)
);
SELECT employee_name, department_name
FROM employees
JOIN departments USING (department_id);
(15)NATURAL JOIN
在 MySQL 中,NATURAL JOIN
用于基于两个表中具有相同名称的所有列自动进行连接。它会自动检测并使用这些列作为连接条件。
CREATE TABLE employees (
employee_id INT,
employee_name VARCHAR(50),
department_id INT
);
CREATE TABLE departments (
department_id INT,
department_name VARCHAR(50)
);
SELECT employee_name, department_name
FROM employees
NATURAL JOIN departments;
(16)CONCAT
在 MySQL 中,CONCAT
函数用于将两个或多个字符串连接在一起。
CREATE TABLE employees (
employee_id INT,
first_name VARCHAR(50),
last_name VARCHAR(50)
);
SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM employees;
(17)BETWEEN…AND…
示例:
SELECT employee_name, salary
FROM employees
WHERE salary BETWEEN 3000 AND 5000;
(18)CROSS JOIN
CROSS JOIN
用于生成两个表的笛卡尔积,即每个表中的每一行与另一个表中的每一行组合在一起。
CREATE TABLE Students (
student_id INT,
student_name VARCHAR(50)
);
INSERT INTO Students (student_id, student_name) VALUES
(1, 'Alice'),
(2, 'Bob'),
(13, 'John'),
(6, 'Alex');
CREATE TABLE Subjects (
subject_name VARCHAR(50)
);
INSERT INTO Subjects (subject_name) VALUES
('Math'),
('Physics'),
('Programming');
SELECT s.student_id, s.student_name, sub.subject_name
FROM Students s
CROSS JOIN Subjects sub
ORDER BY s.student_id, sub.subject_name;
2.易错点
(1)IN 和 NOT IN
需要一个列表或子查询结果,而不是一个表的列。
示例:
Customers table:
+----+-------+
| id | name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders table:
+----+------------+
| id | customerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
下面展示正确写法和错误写法:
select customers.name as 'Customers'
from customers
where customers.id not in # 正确写法!!!
(
select customerid from orders
);
# 错误写法!!!
where customers.id not in orders.customerid;
(2)时间计算
与常规的整数计算不同。
# unit可以是SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR
TIMESTAMPDIFF(unit, datetime_expr1, datetime_expr2) # 计算两个时间的差值
TIMESTAMPADD(unit, interval, datetime_expr) # 计算该时间加上一定时间后的时间
DATEDIFF(date_expr1, date_expr2) # 计算两个时间的差值
DATE_ADD(date, INTERVAL expr unit) # 指定日期加上时间间隔
DATE_SUB(date, INTERVAL expr unit) # 指定日期减去时间间隔
ADDDATE(date, INTERVAL expr unit) # 指定日期加上时间间隔
SUBDATE(date, INTERVAL expr unit) # 指定日期减去时间间隔
(3)ASCII与强制类型转换
与其他语言如C++不同,字符串可以直接视为ASCII码。假如有个 ENUM
类型的 sex 数据,转换为ASCII码格式需要:
ASCII(sex)
其他类型同理,如将上述结果转换为CHAR
类型:
CHAR(ASCII(sex));
(4)COUNT(1) 与 COUNT(*)
count(1)
:这个函数会计算指定列(在这里是1)中非空值的数量。在实际应用中,通常使用 count(1) 来计算行数,因为它更简洁,而且不需要关心具体的列名。
count(*)
:这个函数会计算表中所有行的数量,包括含有NULL值的行。它会对表中的每一行进行计数,包括NULL值,因此有时候会比 count(1)
稍微慢一点。