[特殊字符] MySQL数据表操作完全指南:增删改查的艺术

🌟 前言

在数据库的世界中,数据的增删改查(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分批次

实现删除审计和回收站机制

相关推荐
安然无虞2 小时前
「MongoDB数据库」初见
数据库·mysql·mongodb
一起养小猫2 小时前
Flutter for OpenHarmony 实战:番茄钟应用完整开发指南
开发语言·jvm·数据库·flutter·信息可视化·harmonyos
Mr_Xuhhh2 小时前
MySQL视图详解:虚拟表的创建、使用与实战
数据库·mysql
AI_56782 小时前
MySQL索引优化全景指南:从慢查询诊断到智能调优
数据库·mysql
老虎06272 小时前
Redis入门,配置,常见面试题总结
数据库·redis·缓存
一起养小猫2 小时前
Flutter for OpenHarmony 实战:数据持久化方案深度解析
网络·jvm·数据库·flutter·游戏·harmonyos
codeRichLife2 小时前
TimescaleDB保存100万条设备采集数据的两种存储方案对比分析
数据库
J&Lu3 小时前
[DDD大营销-Redis]
数据库·redis·缓存
咚咚?3 小时前
麒麟操作系统达梦数据集群安装(一主多从)
数据库