SQLite数据插入 #

一、INSERT语句概述 #

1.1 基本语法 #

sql
-- INSERT 基本语法
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

-- 示例表结构
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE,
    age INTEGER,
    status INTEGER DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

1.2 插入方式 #

sql
-- SQLite 支持多种插入方式:
-- 1. 指定列插入
-- 2. 全列插入
-- 3. 批量插入
-- 4. 查询插入
-- 5. 默认值插入

二、基本插入 #

2.1 指定列插入 #

sql
-- 指定列名插入
INSERT INTO users (name, email, age)
VALUES ('John', 'john@example.com', 25);

-- 查看结果
SELECT * FROM users;

-- id | name | email              | age | status | created_at
-- ---|------|--------------------|-----|--------|------------------
-- 1  | John | john@example.com   | 25  | 1      | 2024-01-15 10:00:00

2.2 全列插入 #

sql
-- 插入所有列(按表定义顺序)
INSERT INTO users
VALUES (NULL, 'Jane', 'jane@example.com', 30, 1, CURRENT_TIMESTAMP);

-- AUTOINCREMENT 列使用 NULL
INSERT INTO users
VALUES (NULL, 'Bob', 'bob@example.com', 28, 1, datetime('now'));

2.3 使用DEFAULT关键字 #

sql
-- 使用默认值
INSERT INTO users (name, email, age, status)
VALUES ('Alice', 'alice@example.com', 22, DEFAULT);

-- 省略有默认值的列
INSERT INTO users (name, email, age)
VALUES ('Charlie', 'charlie@example.com', 35);

2.4 使用表达式 #

sql
-- 使用表达式
INSERT INTO users (name, email, age, created_at)
VALUES (
    'David',
    lower('DAVID@EXAMPLE.COM'),
    20 + 5,
    datetime('now', '+1 day')
);

-- 使用子查询
INSERT INTO users (name, email, age)
SELECT 'Eve', 'eve@example.com', AVG(age) FROM users;

三、批量插入 #

3.1 多行VALUES #

sql
-- 一次插入多行
INSERT INTO users (name, email, age) VALUES
    ('User1', 'user1@example.com', 20),
    ('User2', 'user2@example.com', 25),
    ('User3', 'user3@example.com', 30),
    ('User4', 'user4@example.com', 35);

-- 性能优势:
-- 1. 减少SQL解析次数
-- 2. 减少事务开销
-- 3. 批量写入优化

3.2 批量插入优化 #

sql
-- 大批量插入优化技巧

-- 1. 使用事务
BEGIN TRANSACTION;
INSERT INTO users (name, email, age) VALUES
    ('User1', 'user1@example.com', 20),
    ('User2', 'user2@example.com', 25),
    -- ... 更多数据
    ('User1000', 'user1000@example.com', 25);
COMMIT;

-- 2. 临时关闭同步
PRAGMA synchronous = OFF;
-- 批量插入
PRAGMA synchronous = NORMAL;

-- 3. 使用 WAL 模式
PRAGMA journal_mode = WAL;

四、从查询插入 #

4.1 INSERT … SELECT #

sql
-- 从其他表复制数据
CREATE TABLE users_archive (
    id INTEGER PRIMARY KEY,
    name TEXT,
    email TEXT,
    archived_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users_archive (id, name, email)
SELECT id, name, email FROM users WHERE status = 0;

-- 创建备份表
CREATE TABLE users_backup AS SELECT * FROM users;

-- 或先创建结构再插入
CREATE TABLE users_copy LIKE users;
INSERT INTO users_copy SELECT * FROM users;

4.2 复杂查询插入 #

sql
-- 从多表查询插入
CREATE TABLE order_summary (
    order_id INTEGER PRIMARY KEY,
    customer_name TEXT,
    total_amount REAL
);

INSERT INTO order_summary (order_id, customer_name, total_amount)
SELECT 
    o.id,
    c.name,
    SUM(oi.quantity * oi.price)
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id, c.name;

-- 使用子查询
INSERT INTO user_stats (user_id, order_count)
SELECT 
    u.id,
    (SELECT COUNT(*) FROM orders WHERE user_id = u.id)
FROM users u;

五、冲突处理 #

5.1 INSERT OR REPLACE #

sql
-- INSERT OR REPLACE:存在则替换,不存在则插入
INSERT OR REPLACE INTO users (id, name, email, age)
VALUES (1, 'John Updated', 'john@example.com', 26);

-- 注意:
-- 1. REPLACE 会删除旧行,插入新行
-- 2. AUTOINCREMENT 值会增加
-- 3. 会触发 DELETE 和 INSERT 触发器
-- 4. 其他列的值会丢失(除非在新行中指定)

5.2 INSERT OR IGNORE #

sql
-- INSERT OR IGNORE:忽略冲突,继续执行
INSERT OR IGNORE INTO users (id, name, email) VALUES
    (1, 'John', 'john@example.com'),   -- 已存在,忽略
    (2, 'Jane', 'jane@example.com'),   -- 插入成功
    (3, 'Bob', 'bob@example.com');     -- 插入成功

-- 查看结果:只有新数据被插入
SELECT * FROM users;

5.3 UPSERT (SQLite 3.24.0+) #

sql
-- UPSERT:更灵活的冲突处理
-- ON CONFLICT DO UPDATE

-- 基本语法
INSERT INTO users (id, name, email, age)
VALUES (1, 'John', 'john@example.com', 26)
ON CONFLICT(id) DO UPDATE SET
    name = excluded.name,
    age = excluded.age;

-- 使用 WHERE 条件
INSERT INTO users (id, name, email, age)
VALUES (1, 'John', 'john@example.com', 26)
ON CONFLICT(id) DO UPDATE SET
    name = excluded.name,
    age = excluded.age
WHERE excluded.age > users.age;

-- ON CONFLICT DO NOTHING
INSERT INTO users (id, name, email)
VALUES (1, 'John', 'john@example.com')
ON CONFLICT(id) DO NOTHING;

-- 多列冲突
INSERT INTO users (email, name, age)
VALUES ('john@example.com', 'John', 26)
ON CONFLICT(email) DO UPDATE SET
    name = excluded.name,
    age = excluded.age;

5.4 冲突处理对比 #

sql
-- 对比各种冲突处理方式

-- 1. INSERT OR REPLACE
-- 删除旧行,插入新行
-- 所有未指定的列会变为NULL或默认值

-- 2. INSERT OR IGNORE
-- 忽略冲突行,继续插入其他行
-- 已存在的行保持不变

-- 3. UPSERT (推荐)
-- 存在则更新指定列,不存在则插入
-- 可以精确控制更新哪些列
-- 可以添加WHERE条件

六、获取插入ID #

6.1 last_insert_rowid() #

sql
-- 获取最后插入的 ROWID
INSERT INTO users (name, email) VALUES ('Test', 'test@example.com');
SELECT last_insert_rowid();

-- 在应用程序中使用
-- Python: cursor.lastrowid
-- PHP: $pdo->lastInsertId()
-- Java: Statement.getGeneratedKeys()

6.2 RETURNING子句 (SQLite 3.35.0+) #

sql
-- RETURNING:插入后返回数据
INSERT INTO users (name, email, age)
VALUES ('John', 'john@example.com', 25)
RETURNING id, name, created_at;

-- 批量插入返回
INSERT INTO users (name, email) VALUES
    ('User1', 'user1@example.com'),
    ('User2', 'user2@example.com')
RETURNING id, name;

-- 返回所有列
INSERT INTO users (name, email)
VALUES ('Alice', 'alice@example.com')
RETURNING *;

七、特殊数据插入 #

7.1 插入NULL值 #

sql
-- 插入NULL
INSERT INTO users (name, email, age)
VALUES ('Test', 'test@example.com', NULL);

-- 省略列(使用默认值或NULL)
INSERT INTO users (name, email)
VALUES ('Test', 'test@example.com');
-- age 列为 NULL

7.2 插入日期时间 #

sql
-- 插入日期时间
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    name TEXT,
    event_date DATE,
    event_time TIME,
    event_datetime DATETIME
);

-- 使用字符串格式
INSERT INTO events (name, event_date, event_time, event_datetime)
VALUES (
    'Meeting',
    '2024-01-15',
    '10:30:00',
    '2024-01-15 10:30:00'
);

-- 使用函数
INSERT INTO events (name, event_datetime)
VALUES ('Event1', datetime('now'));

INSERT INTO events (name, event_date)
VALUES ('Event2', date('now'));

INSERT INTO events (name, event_time)
VALUES ('Event3', time('now'));

-- 使用时间计算
INSERT INTO events (name, event_datetime)
VALUES ('Event4', datetime('now', '+7 days'));

7.3 插入JSON数据 #

sql
-- 插入JSON数据
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    name TEXT,
    attributes TEXT  -- JSON存储为TEXT
);

-- 插入JSON字符串
INSERT INTO products (name, attributes)
VALUES ('Laptop', '{"brand": "Dell", "ram": "16GB", "storage": "512GB"}');

-- 使用JSON函数
INSERT INTO products (name, attributes)
VALUES ('Phone', json_object('brand', 'Apple', 'model', 'iPhone 15'));

-- 插入JSON数组
INSERT INTO products (name, attributes)
VALUES ('Tablet', json_array('iPad', 'Samsung Tab', 'Surface'));

7.4 插入BLOB数据 #

sql
-- 插入二进制数据
CREATE TABLE files (
    id INTEGER PRIMARY KEY,
    name TEXT,
    data BLOB,
    mime_type TEXT
);

-- 插入十六进制数据
INSERT INTO files (name, data, mime_type)
VALUES ('test', X'010203040506070809', 'application/octet-stream');

-- 使用 zeroblob
INSERT INTO files (name, data, mime_type)
VALUES ('empty', zeroblob(1024), 'application/octet-stream');

-- 从文件加载(需要扩展)
-- INSERT INTO files (name, data)
-- SELECT 'file.pdf', readfile('/path/to/file.pdf');

八、性能优化 #

8.1 批量插入性能对比 #

sql
-- 性能对比(插入10000条记录)

-- 方法1:单条插入(最慢)
-- 约 10-30 秒
BEGIN;
INSERT INTO users (name, email) VALUES ('User1', 'user1@example.com');
INSERT INTO users (name, email) VALUES ('User2', 'user2@example.com');
-- ... 重复10000次
COMMIT;

-- 方法2:多行VALUES(较快)
-- 约 1-3 秒
BEGIN;
INSERT INTO users (name, email) VALUES
    ('User1', 'user1@example.com'),
    ('User2', 'user2@example.com'),
    -- ... 更多数据
    ('User10000', 'user10000@example.com');
COMMIT;

-- 方法3:事务 + 批量(推荐)
-- 约 0.5-1 秒
BEGIN;
-- 每1000条一个INSERT
INSERT INTO users (name, email) VALUES ('User1', ...) ... ('User1000', ...);
INSERT INTO users (name, email) VALUES ('User1001', ...) ... ('User2000', ...);
-- ...
COMMIT;

8.2 插入优化配置 #

sql
-- 优化配置
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -10000;  -- 10MB

-- 大批量插入
PRAGMA temp_store = MEMORY;

-- 执行批量插入
BEGIN TRANSACTION;
-- 插入操作
COMMIT;

九、安全考虑 #

9.1 防止SQL注入 #

sql
-- 错误:直接拼接SQL
-- INSERT INTO users (name) VALUES ('John'); DROP TABLE users;--')

-- 正确:使用参数化查询
-- Python
-- cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", (name, email))

-- PHP
-- $stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
-- $stmt->execute([$name, $email]);

-- Java
-- PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users VALUES (?, ?)");
-- pstmt.setString(1, name);
-- pstmt.setString(2, email);

9.2 输入验证 #

sql
-- 使用 CHECK 约束验证数据
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL CHECK(length(name) >= 2),
    email TEXT CHECK(email LIKE '%@%.%'),
    age INTEGER CHECK(age >= 0 AND age <= 150)
);

-- 插入无效数据会失败
INSERT INTO users (name, email, age) VALUES ('A', 'invalid', 200);
-- Error: CHECK constraint failed

十、常见问题 #

10.1 主键冲突 #

sql
-- 错误:UNIQUE constraint failed
INSERT INTO users (id, name) VALUES (1, 'John');

-- 解决方案1:使用 UPSERT
INSERT INTO users (id, name) VALUES (1, 'John')
ON CONFLICT(id) DO UPDATE SET name = excluded.name;

-- 解决方案2:使用 INSERT OR REPLACE
INSERT OR REPLACE INTO users (id, name) VALUES (1, 'John');

-- 解决方案3:使用 INSERT OR IGNORE
INSERT OR IGNORE INTO users (id, name) VALUES (1, 'John');

10.2 唯一约束冲突 #

sql
-- 错误:UNIQUE constraint failed: users.email
INSERT INTO users (name, email) VALUES ('John', 'john@example.com');

-- 解决方案
INSERT INTO users (name, email) VALUES ('John', 'john@example.com')
ON CONFLICT(email) DO UPDATE SET name = excluded.name;

10.3 外键约束错误 #

sql
-- 启用外键
PRAGMA foreign_keys = ON;

-- 错误:FOREIGN KEY constraint failed
INSERT INTO orders (user_id, total) VALUES (999, 100);

-- 解决方案:确保外键值存在
INSERT INTO users (id, name) VALUES (999, 'User999');
INSERT INTO orders (user_id, total) VALUES (999, 100);

10.4 NOT NULL约束错误 #

sql
-- 错误:NOT NULL constraint failed
INSERT INTO users (email) VALUES ('test@example.com');

-- 解决方案:提供所有必需的值
INSERT INTO users (name, email) VALUES ('Test', 'test@example.com');

-- 或使用默认值
ALTER TABLE users ADD COLUMN name TEXT NOT NULL DEFAULT 'Anonymous';

十一、总结 #

插入方法对比 #

方法 语法 说明
单行插入 INSERT INTO … VALUES 基本插入
批量插入 INSERT INTO … VALUES (…), (…) 多行插入
查询插入 INSERT INTO … SELECT 从查询插入
冲突更新 ON CONFLICT DO UPDATE 存在则更新
冲突忽略 INSERT OR IGNORE 忽略错误
替换插入 INSERT OR REPLACE 删除后插入

最佳实践 #

  1. 使用批量插入提高性能
  2. 使用事务包装批量操作
  3. 使用 UPSERT 处理冲突
  4. 使用参数化查询防止注入
  5. 使用 RETURNING 获取插入数据

下一步,让我们学习数据更新操作!

最后更新:2026-03-27