🌟 前言
在数据库的世界中,数据的增删改查(CRUD)是最核心的操作。掌握这些基本操作,就像掌握了与数据库沟通的语言。本文将深入探讨MySQL数据表的插入、修改和删除操作,带你从基础到高级,全面掌握数据操作的精髓!
📥 一、表记录的插入
1.1 基本插入操作
1.1.1 单行插入
sql
bash
-- 最基本的插入语法
INSERT INTO 表名 (列1, 列2, 列3, ...)
VALUES (值1, 值2, 值3, ...);
bash
-- 示例:插入一个学生记录
INSERT INTO students (name, age, gender, email, enrollment_date)
VALUES ('张三', 20, '男', 'zhangsan@example.com', '2024-03-15');
-- 插入时省略列名(必须提供所有列的值)
INSERT INTO students
VALUES (NULL, '李四', 21, '女', 'lisi@example.com', '2024-03-15', CURRENT_TIMESTAMP);
1.1.2 多行插入
sql
-- 一次插入多行数据(高效)
INSERT INTO students (name, age, gender, email)
VALUES
('王五', 19, '男', 'wangwu@example.com'),
('赵六', 22, '女', 'zhaoliu@example.com'),
('钱七', 20, '男', 'qianqi@example.com');
-- 使用SELECT的结果插入
INSERT INTO students_archive (name, age, email)
SELECT name, age, email
FROM students
WHERE enrollment_date < '2023-01-01';
1.2 插入的高级用法
1.2.1 插入时使用函数
bash
sql
-- 使用MySQL函数生成值
INSERT INTO users (username, password_hash, created_at, last_login)
VALUES (
'admin',
SHA2('secure_password123', 256),
NOW(),
NULL
);
-- 使用UUID生成唯一标识
INSERT INTO sessions (session_id, user_id, created_at)
VALUES (
UUID(),
1001,
CURRENT_TIMESTAMP
);
-- 插入时进行计算
INSERT INTO orders (product_id, quantity, unit_price, total_price)
VALUES (
1001,
3,
299.99,
3 * 299.99 * 0.9 -- 计算折扣后的总价
);
1.2.2 INSERT IGNORE与INSERT ... ON DUPLICATE KEY UPDATE
bash
sql
-- INSERT IGNORE:忽略重复键错误
INSERT IGNORE INTO unique_users (username, email)
VALUES ('john_doe', 'john@example.com');
-- ON DUPLICATE KEY UPDATE:存在则更新
INSERT INTO product_inventory (product_id, quantity)
VALUES (1001, 10)
ON DUPLICATE KEY UPDATE
quantity = quantity + VALUES(quantity),
last_updated = NOW();
-- 复杂更新逻辑
INSERT INTO user_scores (user_id, game_id, score, play_count)
VALUES (123, 456, 5000, 1)
ON DUPLICATE KEY UPDATE
score = GREATEST(score, VALUES(score)), -- 保留最高分
play_count = play_count + 1,
last_played = NOW();
1.2.3 插入大量数据
bash
sql
-- 使用LOAD DATA INFILE快速导入CSV文件
LOAD DATA INFILE '/path/to/students.csv'
INTO TABLE students
FIELDS TERMINATED BY ',' -- 字段分隔符
ENCLOSED BY '"' -- 字段包围符
LINES TERMINATED BY '\n' -- 行结束符
IGNORE 1 LINES -- 忽略标题行
(name, age, gender, @var_email, @var_date)
SET
email = NULLIF(@var_email, ''),
enrollment_date = STR_TO_DATE(@var_date, '%Y-%m-%d');
-- 批量插入性能优化(事务包装)
START TRANSACTION;
INSERT INTO log_entries (level, message, created_at) VALUES (...);
INSERT INTO log_entries (level, message, created_at) VALUES (...);
-- ... 更多插入
COMMIT;
-- 设置bulk_insert_buffer_size优化
SET bulk_insert_buffer_size = 1024 * 1024 * 64; -- 64MB
1.3 插入验证与错误处理
bash
sql
-- 插入前验证数据
INSERT INTO products (name, price, stock)
SELECT
@product_name,
@product_price,
@stock_quantity
WHERE
@product_price > 0
AND @stock_quantity >= 0;
-- 使用存储过程进行验证插入
DELIMITER $$
CREATE PROCEDURE insert_student_safely(
IN p_name VARCHAR(100),
IN p_age INT,
IN p_email VARCHAR(100)
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT '插入失败,数据验证不通过' AS error;
END;
START TRANSACTION;
-- 验证逻辑
IF p_age < 0 OR p_age > 120 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '年龄必须在0-120之间';
END IF;
IF p_email NOT LIKE '%@%.%' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '邮箱格式不正确';
END IF;
-- 执行插入
INSERT INTO students (name, age, email)
VALUES (p_name, p_age, p_email);
COMMIT;
SELECT '插入成功' AS result;
END$$
DELIMITER ;
✏️ 二、表记录的修改
2.1 基本修改操作
2.1.1 更新指定记录
bash
sql
-- 基本UPDATE语法
UPDATE 表名
SET 列1 = 值1, 列2 = 值2, ...
WHERE 条件;
-- 示例:更新单个学生的年龄
UPDATE students
SET age = 21, updated_at = NOW()
WHERE id = 1001;
-- 批量更新:所有学生年龄加1
UPDATE students
SET age = age + 1,
updated_at = CURRENT_TIMESTAMP;
-- 基于表达式更新
UPDATE products
SET
price = price * 0.9, -- 打9折
discount_rate = 0.1,
last_discounted = NOW()
WHERE category_id = 5
AND price > 100;
2.1.2 使用子查询更新
bash
sql
-- 根据其他表的数据更新
UPDATE employees e
JOIN departments d ON e.department_id = d.id
SET
e.salary = e.salary * 1.1,
e.performance_bonus = d.bonus_rate * e.salary
WHERE d.name = '技术部'
AND e.performance_rating >= 4.0;
-- 使用子查询作为更新值
UPDATE products p
SET average_rating = (
SELECT AVG(rating)
FROM product_reviews r
WHERE r.product_id = p.id
GROUP BY r.product_id
)
WHERE EXISTS (
SELECT 1
FROM product_reviews r
WHERE r.product_id = p.id
);
-- 相关子查询更新
UPDATE customers c
SET total_orders = (
SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.id
),
last_order_date = (
SELECT MAX(order_date)
FROM orders o
WHERE o.customer_id = c.id
);
2.2 更新的高级技巧
2.2.1 CASE条件更新
bash
sql
-- 根据条件进行不同更新
UPDATE employees
SET
salary = CASE
WHEN performance_rating >= 4.5 THEN salary * 1.2
WHEN performance_rating >= 4.0 THEN salary * 1.1
WHEN performance_rating >= 3.0 THEN salary * 1.05
ELSE salary * 1.02
END,
bonus = CASE
WHEN department = '销售部' THEN salary * 0.15
WHEN department = '技术部' THEN salary * 0.10
ELSE salary * 0.05
END
WHERE status = '在职';
-- 更新状态标志
UPDATE orders
SET
priority_level = CASE
WHEN total_amount > 5000 THEN '高'
WHEN total_amount > 1000 THEN '中'
ELSE '低'
END,
needs_review = CASE
WHEN payment_method = '货到付款' THEN TRUE
WHEN customer_rating < 3 THEN TRUE
ELSE FALSE
END;
2.2.2 使用LIMIT更新
bash
sql
-- 只更新前N条记录(MySQL特有)
UPDATE large_table
SET processed = TRUE,
processed_at = NOW()
WHERE processed = FALSE
ORDER BY created_at ASC
LIMIT 1000; -- 每次只处理1000条
-- 分批处理大表更新
SET @batch_size = 1000;
SET @processed = 0;
REPEAT
UPDATE large_dataset
SET status = 'processed'
WHERE status = 'pending'
LIMIT @batch_size;
SET @processed = ROW_COUNT();
SELECT CONCAT('已处理 ', @processed, ' 条记录') AS progress;
-- 短暂延迟,避免锁表时间过长
DO SLEEP(0.1);
UNTIL @processed = 0 END REPEAT;
2.2.3 JOIN更新多个表
bash
sql
-- 同时更新多个相关表
UPDATE
orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
SET
o.total_amount = o.total_amount - oi.quantity * oi.unit_price,
p.stock_quantity = p.stock_quantity + oi.quantity,
oi.status = 'cancelled'
WHERE o.id = 12345
AND oi.status = 'active';
-- 使用LEFT JOIN安全更新
UPDATE users u
LEFT JOIN user_profiles up ON u.id = up.user_id
SET
u.last_login = NOW(),
up.login_count = COALESCE(up.login_count, 0) + 1,
up.last_activity = NOW()
WHERE u.id = 1001;
2.3 更新性能优化
bash
sql
-- 1. 使用索引优化WHERE条件
-- 确保更新的列有合适的索引
EXPLAIN UPDATE users SET status = 'inactive' WHERE last_login < '2023-01-01';
-- 2. 批量更新的最佳实践
-- 方法A:使用主键范围
UPDATE large_table
SET flag = 'Y'
WHERE id BETWEEN 1 AND 10000;
-- 方法B:使用临时表
CREATE TEMPORARY TABLE temp_ids (id INT PRIMARY KEY);
INSERT INTO temp_ids
SELECT id FROM source_table WHERE condition;
UPDATE large_table lt
JOIN temp_ids tmp ON lt.id = tmp.id
SET lt.status = 'updated';
-- 3. 监控更新性能
SET profiling = 1;
UPDATE ...;
SHOW PROFILES;
SET profiling = 0;
-- 4. 使用低优先级更新
UPDATE LOW_PRIORITY users
SET status = 'inactive'
WHERE last_activity < DATE_SUB(NOW(), INTERVAL 1 YEAR);
🗑️ 三、表记录的删除
3.1 基本删除操作
3.1.1 删除指定记录
bash
sql
-- 基本DELETE语法
DELETE FROM 表名 WHERE 条件;
-- 删除单个记录
DELETE FROM students WHERE id = 1001;
-- 删除满足条件的所有记录
DELETE FROM log_entries
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)
AND log_level = 'INFO';
-- 删除重复记录
DELETE t1 FROM duplicate_data t1
JOIN duplicate_data t2
WHERE
t1.id > t2.id
AND t1.email = t2.email;
3.1.2 使用子查询删除
bash
sql
-- 基于子查询结果删除
DELETE FROM inactive_users
WHERE id IN (
SELECT user_id
FROM login_records
WHERE last_login < DATE_SUB(NOW(), INTERVAL 365 DAY)
);
-- 使用EXISTS删除
DELETE FROM products p
WHERE NOT EXISTS (
SELECT 1
FROM inventory i
WHERE i.product_id = p.id
AND i.quantity > 0
);
-- 删除关联记录
DELETE o, oi
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.status = 'cancelled'
AND o.cancelled_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
3.2 删除的高级管理
3.2.1 安全删除策略
bash
sql
-- 1. 使用DELETE ... LIMIT(MySQL特有)
DELETE FROM large_table
WHERE condition
ORDER BY id
LIMIT 1000; -- 每次只删1000条
-- 2. 软删除模式(推荐)
ALTER TABLE users
ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE,
ADD COLUMN deleted_at TIMESTAMP NULL;
-- 标记删除而不是物理删除
UPDATE users
SET
is_deleted = TRUE,
deleted_at = NOW()
WHERE id = 1001;
-- 查询时排除已删除记录
SELECT * FROM users WHERE is_deleted = FALSE;
-- 3. 归档后删除
-- 步骤1:归档到历史表
INSERT INTO users_archive
SELECT * FROM users
WHERE last_login < '2020-01-01';
-- 步骤2:删除已归档数据
DELETE FROM users
WHERE last_login < '2020-01-01';
3.2.2 批量删除优化
bash
sql
-- 大表删除的分批处理
DELIMITER $$
CREATE PROCEDURE batch_delete_old_records()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE batch_size INT DEFAULT 1000;
DECLARE deleted_count INT;
REPEAT
-- 开启事务,避免锁表时间过长
START TRANSACTION;
-- 删除一批记录
DELETE FROM old_data
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR)
LIMIT batch_size;
SET deleted_count = ROW_COUNT();
COMMIT;
-- 显示进度
IF deleted_count > 0 THEN
SELECT CONCAT('已删除 ', deleted_count, ' 条记录') AS progress;
END IF;
-- 短暂暂停,减少服务器压力
DO SLEEP(0.5);
UNTIL deleted_count = 0 END REPEAT;
SELECT '删除完成' AS result;
END$$
DELIMITER ;
3.2.3 TRUNCATE vs DELETE
bash
sql
-- TRUNCATE:快速清空整个表
TRUNCATE TABLE temp_data; -- 不能回滚,不能加WHERE条件
-- DELETE:逐行删除,可回滚
DELETE FROM temp_data; -- 可以回滚,支持WHERE条件
-- 对比总结
/*
| 特性 | TRUNCATE | DELETE |
|---|---|---|
| 速度 | 更快 | 较慢 |
| 可回滚 | 否 | 是 |
| 支持WHERE | 否 | 是 |
| 重置自增值 | 是 | 否 |
| 触发器 | 不触发 | 触发 |
| 行数统计 | 立即更新 | 延迟更新 |
| */ |
-- 根据需求选择
-- 需要快速清空大表且不需要回滚:TRUNCATE
-- 需要条件删除或需要回滚:DELETE
3.3 删除的权限与安全
bash
sql
-- 1. 权限检查
-- 查看当前用户的删除权限
SHOW GRANTS;
-- 2. 删除前的安全检查
CREATE PROCEDURE safe_delete_record(
IN p_table_name VARCHAR(64),
IN p_id INT
)
BEGIN
DECLARE v_count INT;
DECLARE v_message VARCHAR(255);
-- 检查记录是否存在
SET @sql = CONCAT('SELECT COUNT(*) INTO @count FROM ', p_table_name, ' WHERE id = ', p_id);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET v_count = @count;
IF v_count = 0 THEN
SET v_message = CONCAT('记录不存在: ID=', p_id);
ELSE
-- 检查外键约束
SELECT COUNT(*) INTO @fk_count
FROM information_schema.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = p_table_name;
IF @fk_count > 0 THEN
SET v_message = CONCAT('存在外键约束,无法直接删除');
ELSE
-- 执行删除
SET @sql = CONCAT('DELETE FROM ', p_table_name, ' WHERE id = ', p_id);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET v_message = CONCAT('成功删除记录: ID=', p_id);
END IF;
END IF;
SELECT v_message AS result;
END;
3.4 删除操作的监控与审计
bash
sql
-- 创建删除审计表
CREATE TABLE deletion_audit (
id INT AUTO_INCREMENT PRIMARY KEY,
table_name VARCHAR(64) NOT NULL,
record_id VARCHAR(100),
deleted_data JSON,
deleted_by VARCHAR(50) DEFAULT (CURRENT_USER()),
deleted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45),
reason TEXT,
INDEX idx_table_time (table_name, deleted_at)
) ENGINE=InnoDB;
-- 创建删除触发器进行审计
DELIMITER $$
CREATE TRIGGER audit_student_deletion
BEFORE DELETE ON students
FOR EACH ROW
BEGIN
INSERT INTO deletion_audit
(table_name, record_id, deleted_data, ip_address, reason)
VALUES (
'students',
OLD.id,
JSON_OBJECT(
'name', OLD.name,
'age', OLD.age,
'email', OLD.email,
'enrollment_date', OLD.enrollment_date
),
CONNECTION_ID(),
'用户主动删除'
);
END$$
DELIMITER ;
-- 查看删除审计日志
SELECT
table_name,
COUNT(*) as deletion_count,
DATE(deleted_at) as deletion_date,
deleted_by
FROM deletion_audit
WHERE deleted_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY table_name, DATE(deleted_at), deleted_by
ORDER BY deletion_date DESC;
🚀 四、综合实战案例
4.1 电商订单管理示例
bash
sql
-- 1. 插入新订单
START TRANSACTION;
-- 插入订单主表
INSERT INTO orders (
order_number, customer_id, total_amount,
shipping_address, payment_method
) VALUES (
CONCAT('ORD', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'), LPAD(FLOOR(RAND() * 10000), 4, '0')),
1001,
0, -- 初始为0,下面会计算
'北京市海淀区中关村大街1号',
'alipay'
);
SET @order_id = LAST_INSERT_ID();
-- 插入订单商品
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES
(@order_id, 5001, 2, 2999.00),
(@order_id, 6003, 1, 999.00);
-- 更新订单总金额
UPDATE orders
SET total_amount = (
SELECT SUM(quantity * unit_price)
FROM order_items
WHERE order_id = @order_id
)
WHERE id = @order_id;
-- 更新商品库存
UPDATE products p
JOIN order_items oi ON p.id = oi.product_id
SET p.stock_quantity = p.stock_quantity - oi.quantity,
p.updated_at = NOW()
WHERE oi.order_id = @order_id;
COMMIT;
-- 2. 修改订单状态(发货)
UPDATE orders
SET
status = 'shipped',
shipping_number = CONCAT('SF', UUID_SHORT()),
shipped_at = NOW(),
estimated_delivery = DATE_ADD(NOW(), INTERVAL 3 DAY)
WHERE id = @order_id;
-- 3. 删除已取消的订单(保留90天)
DELETE FROM orders
WHERE status = 'cancelled'
AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
4.2 用户数据清理示例
sql
-- 定期清理不活跃用户数据
DELIMITER $$
CREATE PROCEDURE cleanup_inactive_users()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE batch_size INT DEFAULT 500;
DECLARE deleted_count INT;
DECLARE total_deleted INT DEFAULT 0;
DECLARE start_time DATETIME DEFAULT NOW();
-- 创建临时表存储要删除的用户ID
CREATE TEMPORARY TABLE temp_user_ids (
user_id INT PRIMARY KEY
) ENGINE=MEMORY;
-- 查找不活跃用户(超过1年未登录)
INSERT INTO temp_user_ids
SELECT id
FROM users
WHERE last_login < DATE_SUB(NOW(), INTERVAL 1 YEAR)
AND is_active = TRUE
ORDER BY last_login ASC;
-- 分批删除
REPEAT
START TRANSACTION;
-- 标记用户为不活跃(软删除)
UPDATE users u
JOIN temp_user_ids tmp ON u.id = tmp.user_id
SET
u.is_active = FALSE,
u.deactivated_at = NOW(),
u.deactivation_reason = '自动清理:长时间未登录'
WHERE u.is_active = TRUE
LIMIT batch_size;
SET deleted_count = ROW_COUNT();
-- 从临时表中删除已处理的ID
DELETE FROM temp_user_ids
WHERE user_id IN (
SELECT user_id
FROM users
WHERE is_active = FALSE
AND deactivated_at >= start_time
);
COMMIT;
SET total_deleted = total_deleted + deleted_count;
IF deleted_count > 0 THEN
SELECT CONCAT('已处理 ', total_deleted, ' 个不活跃用户') AS progress;
DO SLEEP(0.1); -- 短暂暂停
END IF;
UNTIL deleted_count = 0 END REPEAT;
-- 清理临时表
DROP TEMPORARY TABLE temp_user_ids;
SELECT CONCAT('清理完成,共处理 ', total_deleted, ' 个用户') AS result;
五.核心要点回顾
插入操作:
使用批量插入提高性能
考虑使用INSERT IGNORE或ON DUPLICATE KEY UPDATE
数据验证在插入前进行
修改操作:
使用索引优化WHERE条件
大数据量更新时使用LIMIT分批次
使用CASE语句实现条件更新
删除操作:
优先考虑软删除而不是物理删除
大表删除使用LIMIT分批次
实现删除审计和回收站机制