SQL154 插入记录(一)
描述
牛客后台会记录每个用户的试卷作答记录到exam_record表,现在有两个用户的作答记录详情如下:
- 用户1001在2021年9月1日晚上10点11分12秒开始作答试卷9001,并在50分钟后提交,得了90分;
- 用户1002在2021年9月4日上午7点1分2秒开始作答试卷9002,并在10分钟后退出了平台。
试卷作答记录表exam_record中,表已建好,其结构如下,请用一条语句将这两条记录插入表中。
Filed | Type | Null | Key | Extra | Default | Comment |
id | int(11) | NO | PRI | auto_increment | (NULL) | 自增ID |
uid | int(11) | NO | (NULL) | 用户ID | ||
exam_id | int(11) | NO | (NULL) | 试卷ID | ||
start_time | datetime | NO | (NULL) | 开始时间 | ||
submit_time | datetime | YES | (NULL) | 提交时间 | ||
score | tinyint(4) | YES | (NULL) | 得分 |
该题最后会通过执行SELECT uid, exam_id, start_time, submit_time, score FROM exam_record;来对比结果
INSERT INTOexam_record (uid, exam_id, start_time, submit_time, score)
VALUES('1001','9001','2021-09-01 22:11:12','2021-09-01 23:01:12','90');
INSERT INTOexam_record (uid, exam_id, start_time)
VALUES('1002','9002','2021-09-04 07:01:02');
SQL INSERT INTO
语句:插入记录的不同方式
INSERT INTO
语句是 SQL 中用于向数据库表中添加新记录(行)的核心命令。根据需要插入的数据完整性、目标列的指定方式以及数据来源,有多种使用方式。
1. 完整插入 (指定所有列)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
- 说明: 明确指定要插入数据的所有列名(或大部分关键列),并在
VALUES
子句中提供对应顺序的值。 - 优点: 清晰、安全、可读性强。即使表结构后续有变动(如新增列),只要新列有默认值或允许
NULL
,此语句通常仍能正常执行。 - 缺点: 需要写出所有列名,代码稍长。
- 分析: 明确指定了
exam_record
表的uid
,exam_id
,start_time
,submit_time
,score
这5个列,并提供了对应的值。这是一次完整的考试记录插入,包含了开始时间、提交时间和得分。 - 示例
INSERT INTOexam_record (uid, exam_id, start_time, submit_time, score) VALUES('1001','9001','2021-09-01 22:11:12','2021-09-01 23:01:12','90');
2. 部分插入 (指定部分列)
INSERT INTO table_name (column1, column2, ...) -- 只列出需要赋值的列
VALUES (value1, value2, ...);
- 说明: 只指定需要插入数据的部分列名。未指定的列将根据其定义被赋予默认值(如
DEFAULT
约束)、NULL
(如果允许),或者如果该列是AUTO_INCREMENT
主键,会自动生成下一个值。 - 优点: 灵活,当某些列的值可以由数据库自动生成或可以为空时,无需手动指定。
- 缺点: 需要清楚了解表结构和各列的约束(如
NOT NULL
、默认值等),否则可能因缺少必要值而插入失败。 - 分析: 只指定了
uid
,exam_id
,start_time
三个列。submit_time
和score
列未指定。 - 结果:
submit_time
很可能为NULL
(表示考试尚未提交),score
也为NULL
(或0,取决于表设计)。这通常用于记录用户开始考试的事件,最终成绩和提交时间将在考试结束后更新。 - 示例
INSERT INTOexam_record (uid, exam_id, start_time) VALUES('1002','9002','2021-09-04 07:01:02');
3. 省略列名列表的插入 (不推荐)
语法:
INSERT INTO table_name
VALUES (value1, value2, value3, ...); -- 值的顺序必须严格匹配表的列顺序
- 说明: 省略
()
中的列名列表。VALUES
中的值必须严格按照表定义的列顺序提供,且数量必须完全匹配。 - 优点: 代码最短。
- 缺点:
- 极易出错: 一旦表结构改变(如增删列、调整列序),此语句极可能失败或插入到错误的列。
- 可读性差: 无法直观看出每个值对应哪个列。
- 灵活性差: 必须为所有列提供值,即使是
NULL
或默认值也需要显式写出来。
- 结论: 强烈不推荐在生产环境或需要维护的代码中使用此方式。
4. 插入多行记录
语法:
INSERT INTO table_name (column1, column2, ...)
VALUES (value1a, value2a, ...),(value1b, value2b, ...),(value1c, value2c, ...);
- 说明: 在一个
INSERT
语句中,通过VALUES
后跟多组用逗号分隔的(值)
来一次性插入多条记录。 - 优点: 效率高,比执行多条单行
INSERT
语句更快,尤其是在处理大量数据时,减少了网络往返和事务开销。 - 注意: 所有行的列名列表必须相同。
- 示例:
INSERT INTO exam_record (uid, exam_id, start_time) VALUES ('1003', '9001', '2021-09-05 10:00:00'),('1004', '9001', '2021-09-05 10:05:00'),('1005', '9003', '2021-09-06 14:30:00');
5. 从其他表插入数据 (INSERT INTO ... SELECT
)
语法:
INSERT INTO table_name (column1, column2, ...)
SELECT column1, column2, ...
FROM source_table
WHERE condition;
- 说明: 将一个
SELECT
查询的结果集直接插入到目标表中。 - 优点: 强大且高效,适用于数据迁移、备份、根据条件复制数据等场景。
- 注意:
SELECT
查询返回的列数和数据类型必须与INSERT INTO
指定的列兼容。 - 示例 (假设要将已完成的考试记录归档):
INSERT INTO exam_archive (uid, exam_id, start_time, submit_time, score) SELECT uid, exam_id, start_time, submit_time, score FROM exam_record WHERE submit_time IS NOT NULL AND score IS NOT NULL; -- 假设已提交且有成绩
6. 替换插入 (REPLACE INTO
)
语法:
REPLACE INTO table_name [(column1, column2, ...)]
VALUES (value1, value2, ...);
- 说明: 这是 MySQL 特有的扩展语句。它尝试插入新行,如果新行与现有行在主键或唯一索引上发生冲突,则先删除旧行,再插入新行。
- 核心逻辑:
REPLACE INTO
=DELETE
(冲突行) +INSERT
(新行)。 - 优点:
- 简化逻辑: 一行代码实现“存在则替换,不存在则插入”。
- 原子性: 操作通常是原子的。
- 缺点:
- 性能开销: “删除+插入”比
UPDATE
开销大。 - 主键变更: 自增主键会获得新值,可能影响外键引用。
- 非标准: 仅 MySQL/MariaDB 支持,可移植性差。
- 性能开销: “删除+插入”比
- 示例:
REPLACE INTO examination_info VALUES(NULL, 9003, "SQL", "hard", 90, "2021-01-01 00:00:00");
- 分析: 假设
exam_id=9003
是唯一键。- 若
exam_id=9003
不存在:直接插入,自增主键获新值。 - 若
exam_id=9003
已存在:先删除旧记录,再插入新记录,自增主键会变成一个新值。
- 若
- 分析: 假设
总结与最佳实践
- 明确指定列名: 始终使用
INSERT INTO table_name (col1, col2, ...)
的形式,避免省略列名列表。这能确保代码的健壮性和可维护性。 - 利用部分插入: 当某些列的值可以由数据库自动处理(如
AUTO_INCREMENT
、DEFAULT
、NULL
)时,使用部分插入是合理且常见的做法(如你的第二个示例)。 - 批量插入: 当需要插入多条记录时,优先考虑使用单条
INSERT
语句插入多行,以提高性能。 - 数据来源: 除了直接提供值 (
VALUES
),也要熟悉INSERT ... SELECT
这种从查询结果插入数据的强大方式。 - 注意数据类型: 确保
VALUES
或SELECT
中的值与目标列的数据类型兼容。你的示例中使用了单引号包裹字符串和日期时间,这是正确的做法。对于数值类型(如score
),通常不需要引号,但加上引号(如'90'
)在多数数据库中也会被隐式转换,不过最好遵循数据类型规范。