Oracle数据插入 #
一、INSERT语句概述 #
1.1 基本语法 #
sql
-- INSERT语句基本语法
INSERT INTO table_name [(column1, column2, ...)]
VALUES (value1, value2, ...);
-- 或使用子查询
INSERT INTO table_name [(column1, column2, ...)]
SELECT column1, column2, ... FROM another_table;
1.2 示例表结构 #
sql
-- 创建示例表
CREATE TABLE employees (
employee_id NUMBER(6) PRIMARY KEY,
first_name VARCHAR2(50),
last_name VARCHAR2(50) NOT NULL,
email VARCHAR2(100) UNIQUE,
phone_number VARCHAR2(20),
hire_date DATE DEFAULT SYSDATE,
job_id VARCHAR2(10),
salary NUMBER(8,2),
commission_pct NUMBER(2,2),
manager_id NUMBER(6),
department_id NUMBER(4)
);
-- 创建序列
CREATE SEQUENCE seq_employee_id START WITH 1 INCREMENT BY 1;
二、单行插入 #
2.1 完整列插入 #
sql
-- 插入所有列
INSERT INTO employees VALUES (
seq_employee_id.NEXTVAL,
'John',
'Doe',
'john.doe@example.com',
'123-456-7890',
TO_DATE('2024-01-15', 'YYYY-MM-DD'),
'IT_PROG',
8000,
NULL,
100,
10
);
2.2 指定列插入 #
sql
-- 插入指定列
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date,
job_id,
salary,
department_id
) VALUES (
seq_employee_id.NEXTVAL,
'Jane',
'Smith',
'jane.smith@example.com',
SYSDATE,
'SA_REP',
6000,
20
);
-- 未指定的列使用默认值或NULL
2.3 使用DEFAULT关键字 #
sql
-- 使用DEFAULT关键字
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date,
job_id,
salary,
department_id
) VALUES (
seq_employee_id.NEXTVAL,
'Bob',
'Johnson',
'bob.johnson@example.com',
DEFAULT, -- 使用默认值SYSDATE
'IT_PROG',
7000,
10
);
2.4 插入NULL值 #
sql
-- 显式插入NULL
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
phone_number,
hire_date,
job_id,
salary,
commission_pct,
manager_id,
department_id
) VALUES (
seq_employee_id.NEXTVAL,
'Alice',
'Williams',
'alice.williams@example.com',
NULL, -- 显式NULL
SYSDATE,
'SA_REP',
5000,
NULL,
NULL,
30
);
-- 省略列(隐式NULL)
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date,
job_id,
salary,
department_id
) VALUES (
seq_employee_id.NEXTVAL,
'Charlie',
'Brown',
'charlie.brown@example.com',
SYSDATE,
'SA_REP',
5500,
30
);
-- phone_number, commission_pct, manager_id 为NULL
三、批量插入 #
3.1 使用INSERT ALL #
sql
-- INSERT ALL:无条件多行插入
INSERT ALL
INTO employees (employee_id, first_name, last_name, email, hire_date, job_id, salary, department_id)
VALUES (seq_employee_id.NEXTVAL, 'User1', 'Test1', 'user1@test.com', SYSDATE, 'IT_PROG', 5000, 10)
INTO employees (employee_id, first_name, last_name, email, hire_date, job_id, salary, department_id)
VALUES (seq_employee_id.NEXTVAL, 'User2', 'Test2', 'user2@test.com', SYSDATE, 'IT_PROG', 5500, 10)
INTO employees (employee_id, first_name, last_name, email, hire_date, job_id, salary, department_id)
VALUES (seq_employee_id.NEXTVAL, 'User3', 'Test3', 'user3@test.com', SYSDATE, 'IT_PROG', 6000, 10)
SELECT * FROM DUAL;
-- 注意:seq_employee_id.NEXTVAL在每行都会递增
3.2 使用子查询插入 #
sql
-- 从另一个表复制数据
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date,
job_id,
salary,
department_id
)
SELECT
seq_employee_id.NEXTVAL,
first_name,
last_name,
email,
hire_date,
job_id,
salary,
department_id
FROM temp_employees;
-- 复制整个表
INSERT INTO employees_backup
SELECT * FROM employees;
-- 条件复制
INSERT INTO high_salary_employees
SELECT * FROM employees WHERE salary > 10000;
3.3 使用INSERT FIRST #
sql
-- INSERT FIRST:条件多行插入
-- 满足第一个条件后不再检查后续条件
INSERT FIRST
WHEN salary > 15000 THEN
INTO high_salary_employees (employee_id, first_name, last_name, salary)
VALUES (employee_id, first_name, last_name, salary)
WHEN salary > 10000 THEN
INTO medium_salary_employees (employee_id, first_name, last_name, salary)
VALUES (employee_id, first_name, last_name, salary)
ELSE
INTO low_salary_employees (employee_id, first_name, last_name, salary)
VALUES (employee_id, first_name, last_name, salary)
SELECT employee_id, first_name, last_name, salary
FROM employees;
3.4 使用INSERT ALL条件插入 #
sql
-- INSERT ALL:条件多行插入
-- 所有条件都会检查
INSERT ALL
WHEN salary > 10000 THEN
INTO high_salary_employees (employee_id, first_name, last_name, salary)
VALUES (employee_id, first_name, last_name, salary)
WHEN department_id = 10 THEN
INTO it_department_employees (employee_id, first_name, last_name, department_id)
VALUES (employee_id, first_name, last_name, department_id)
SELECT employee_id, first_name, last_name, salary, department_id
FROM employees;
四、多表插入 #
4.1 多表插入概述 #
sql
-- 多表插入类型
-- INSERT ALL:无条件插入所有目标表
-- INSERT ALL WHEN:条件插入多个表
-- INSERT FIRST WHEN:条件插入第一个满足的表
4.2 多表插入示例 #
sql
-- 创建目标表
CREATE TABLE sales_2023 AS SELECT * FROM sales WHERE 1=0;
CREATE TABLE sales_2024 AS SELECT * FROM sales WHERE 1=0;
CREATE TABLE sales_other AS SELECT * FROM sales WHERE 1=0;
-- 按年份分表插入
INSERT FIRST
WHEN TO_CHAR(sale_date, 'YYYY') = '2023' THEN
INTO sales_2023 VALUES (sale_id, customer_id, sale_date, amount)
WHEN TO_CHAR(sale_date, 'YYYY') = '2024' THEN
INTO sales_2024 VALUES (sale_id, customer_id, sale_date, amount)
ELSE
INTO sales_other VALUES (sale_id, customer_id, sale_date, amount)
SELECT sale_id, customer_id, sale_date, amount
FROM sales_temp;
4.3 旋转插入 #
sql
-- 将行数据旋转为列数据插入
-- 源数据
CREATE TABLE sales_by_quarter (
year NUMBER,
q1_sales NUMBER,
q2_sales NUMBER,
q3_sales NUMBER,
q4_sales NUMBER
);
-- 目标表
CREATE TABLE sales_monthly (
year NUMBER,
month NUMBER,
sales NUMBER
);
-- 旋转插入
INSERT ALL
INTO sales_monthly (year, month, sales) VALUES (year, 1, q1_sales)
INTO sales_monthly (year, month, sales) VALUES (year, 4, q2_sales)
INTO sales_monthly (year, month, sales) VALUES (year, 7, q3_sales)
INTO sales_monthly (year, month, sales) VALUES (year, 10, q4_sales)
SELECT year, q1_sales, q2_sales, q3_sales, q4_sales
FROM sales_by_quarter;
五、特殊插入 #
5.1 使用序列 #
sql
-- 创建序列
CREATE SEQUENCE seq_employee_id START WITH 1 INCREMENT BY 1 NOCACHE;
-- 使用序列插入
INSERT INTO employees (employee_id, first_name, last_name, email)
VALUES (seq_employee_id.NEXTVAL, 'John', 'Doe', 'john.doe@example.com');
-- 批量使用序列
INSERT INTO employees (employee_id, first_name, last_name, email)
SELECT seq_employee_id.NEXTVAL, first_name, last_name, email
FROM temp_employees;
-- 获取刚插入的序列值
INSERT INTO employees (employee_id, first_name, last_name, email)
VALUES (seq_employee_id.NEXTVAL, 'Jane', 'Smith', 'jane.smith@example.com')
RETURNING employee_id INTO :v_emp_id;
5.2 使用函数 #
sql
-- 使用日期函数
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date
) VALUES (
seq_employee_id.NEXTVAL,
'John',
'Doe',
LOWER('John.Doe') || '@example.com',
TO_DATE('2024-01-15 10:30:00', 'YYYY-MM-DD HH24:MI:SS')
);
-- 使用字符串函数
INSERT INTO employees (
employee_id,
first_name,
last_name,
email
) VALUES (
seq_employee_id.NEXTVAL,
INITCAP('john'),
INITCAP('doe'),
LOWER('JOHN.DOE') || '@example.com'
);
-- 使用SYS_GUID()
INSERT INTO employees (
employee_id,
first_name,
last_name,
email
) VALUES (
TO_NUMBER(SUBSTR(SYS_GUID(), 1, 10), 'XXXXXXXXXX'),
'John',
'Doe',
'john.doe@example.com'
);
5.3 使用RETURNING子句 #
sql
-- 返回插入的值
VARIABLE v_emp_id NUMBER;
VARIABLE v_hire_date VARCHAR2(30);
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date
) VALUES (
seq_employee_id.NEXTVAL,
'John',
'Doe',
'john.doe@example.com',
SYSDATE
) RETURNING
employee_id,
TO_CHAR(hire_date, 'YYYY-MM-DD HH24:MI:SS')
INTO
:v_emp_id,
:v_hire_date;
PRINT v_emp_id;
PRINT v_hire_date;
-- PL/SQL中使用RETURNING
DECLARE
v_emp_id NUMBER;
v_hire_date DATE;
BEGIN
INSERT INTO employees (
employee_id,
first_name,
last_name,
email,
hire_date
) VALUES (
seq_employee_id.NEXTVAL,
'Jane',
'Smith',
'jane.smith@example.com',
SYSDATE
) RETURNING employee_id, hire_date INTO v_emp_id, v_hire_date;
DBMS_OUTPUT.PUT_LINE('Employee ID: ' || v_emp_id);
DBMS_OUTPUT.PUT_LINE('Hire Date: ' || TO_CHAR(v_hire_date, 'YYYY-MM-DD'));
END;
/
六、直接路径插入 #
6.1 使用APPEND提示 #
sql
-- 直接路径插入(绕过缓冲区缓存)
INSERT /*+ APPEND */ INTO employees_backup
SELECT * FROM employees;
-- 直接路径插入特点
-- 1. 绕过缓冲区缓存,直接写入数据文件
-- 2. 不使用现有空间,在高水位线之上写入
-- 3. 更快的批量插入
-- 4. 插入后需要提交才能查询
-- 使用APPEND_VALUES(11g+)
INSERT /*+ APPEND_VALUES */ INTO employees
VALUES (seq_employee_id.NEXTVAL, 'John', 'Doe', 'john.doe@example.com', SYSDATE, 'IT_PROG', 5000, NULL, NULL, 10);
6.2 直接路径插入注意事项 #
sql
-- 直接路径插入限制
-- 1. 表不能有触发器
-- 2. 表不能有参照完整性约束
-- 3. 表不能有检查约束
-- 4. 插入后需要提交才能查询
-- 检查表是否适合直接路径插入
SELECT
TABLE_NAME,
TRIGGER_NAME
FROM USER_TRIGGERS
WHERE TABLE_NAME = 'EMPLOYEES';
SELECT
TABLE_NAME,
CONSTRAINT_NAME,
CONSTRAINT_TYPE
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'EMPLOYEES';
七、插入性能优化 #
7.1 批量插入优化 #
sql
-- 1. 使用直接路径插入
INSERT /*+ APPEND */ INTO target_table
SELECT * FROM source_table;
-- 2. 禁用索引和约束
ALTER TABLE target_table DISABLE CONSTRAINT pk_target;
ALTER INDEX idx_target UNUSABLE;
INSERT INTO target_table SELECT * FROM source_table;
ALTER INDEX idx_target REBUILD;
ALTER TABLE target_table ENABLE CONSTRAINT pk_target;
-- 3. 使用并行插入
INSERT /*+ PARALLEL(target_table, 4) */ INTO target_table
SELECT * FROM source_table;
-- 4. 使用NOLOGGING减少日志
ALTER TABLE target_table NOLOGGING;
INSERT /*+ APPEND */ INTO target_table SELECT * FROM source_table;
ALTER TABLE target_table LOGGING;
7.2 使用SQL*Loader #
sql
-- SQL*Loader是Oracle提供的高速数据加载工具
-- 控制文件示例 (load.ctl)
/*
LOAD DATA
INFILE 'data.csv'
INTO TABLE employees
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
(
employee_id,
first_name,
last_name,
email,
hire_date DATE "YYYY-MM-DD",
job_id,
salary,
department_id
)
*/
-- 执行加载
-- sqlldr userid=username/password control=load.ctl direct=true
7.3 使用外部表 #
sql
-- 创建目录对象
CREATE DIRECTORY data_dir AS '/data/files';
-- 创建外部表
CREATE TABLE ext_employees (
employee_id NUMBER,
first_name VARCHAR2(50),
last_name VARCHAR2(50),
email VARCHAR2(100),
hire_date DATE,
job_id VARCHAR2(10),
salary NUMBER,
department_id NUMBER
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
)
LOCATION ('employees.csv')
);
-- 从外部表插入
INSERT INTO employees
SELECT * FROM ext_employees;
八、插入错误处理 #
8.1 使用DML ERROR LOGGING #
sql
-- 创建错误日志表
EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('employees', 'err_employees');
-- 使用LOG ERRORS子句
INSERT INTO employees
SELECT * FROM temp_employees
LOG ERRORS INTO err_employees REJECT LIMIT UNLIMITED;
-- 查看错误记录
SELECT * FROM err_employees;
-- 限制错误数量
INSERT INTO employees
SELECT * FROM temp_employees
LOG ERRORS INTO err_employees REJECT LIMIT 100;
8.2 处理约束违规 #
sql
-- 捕获唯一约束违规
BEGIN
INSERT INTO employees (employee_id, email)
VALUES (1, 'duplicate@example.com');
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('Duplicate email');
END;
/
-- 捕获检查约束违规
BEGIN
INSERT INTO employees (employee_id, salary)
VALUES (seq_employee_id.NEXTVAL, -100);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -2290 THEN
DBMS_OUTPUT.PUT_LINE('Check constraint violated');
ELSE
RAISE;
END IF;
END;
/
九、总结 #
插入数据要点:
| 插入方式 | 语法 | 适用场景 |
|---|---|---|
| 单行插入 | INSERT INTO … VALUES | 少量数据 |
| 批量插入 | INSERT INTO … SELECT | 大量数据 |
| 多行插入 | INSERT ALL | 多行固定数据 |
| 条件插入 | INSERT FIRST/ALL WHEN | 条件分发 |
| 直接路径 | INSERT /*+ APPEND */ | 大批量高性能 |
最佳实践:
- 使用序列生成主键
- 批量插入使用直接路径
- 大数据量禁用索引后重建
- 使用错误日志记录异常
- 合理使用并行处理
下一步,让我们学习数据更新!
最后更新:2026-03-27