当前位置: 首页 > news >正文

【Oracle】DML语言

在这里插入图片描述

个人主页:Guiat
归属专栏:Oracle

在这里插入图片描述

文章目录

  • 1. DML概述
    • 1.1 什么是DML?
    • 1.2 DML的核心功能
  • 2. INSERT语句详解
    • 2.1 基础插入操作
    • 2.2 子查询插入
    • 2.3 多表插入
    • 2.4 批量插入优化
  • 3. UPDATE语句详解
    • 3.1 基础更新操作
    • 3.2 关联更新
    • 3.3 批量更新优化
  • 4. DELETE语句详解
    • 4.1 基础删除操作
    • 4.2 关联删除
    • 4.3 批量删除优化
  • 5. MERGE语句详解
    • 5.1 MERGE基础语法
    • 5.2 复杂MERGE应用
  • 6. 事务控制
    • 6.1 事务基础
    • 6.2 复杂事务处理
  • 7. 性能优化技巧
    • 7.1 批量操作优化
    • 7.2 索引和查询优化

正文
DML(Data Manipulation Language)是Oracle数据库的"魔法师",专门负责数据的增删改查操作。如果说DDL是建房子的,那DML就是在房子里生活的——添置家具、整理物品、搬家换房。它让静态的数据结构变得生动起来,是我们与数据库数据打交道最频繁的语言!

1. DML概述

1.1 什么是DML?

DML就像是数据库的"生活管家",负责管理数据库中的实际数据。它不改变数据库的结构,而是专注于数据内容的操作。在Oracle这个数据王国里,DML是让数据"活"起来的关键工具。

DML数据操作语言
INSERT插入
UPDATE更新
DELETE删除
MERGE合并
SELECT查询
单行插入
批量插入
条件更新
批量更新
条件删除
批量删除
插入或更新
数据同步
简单查询
复杂查询

1.2 DML的核心功能

Oracle DML的功能体系就像一个完整的数据处理工厂:

Oracle DML功能体系
数据插入
数据更新
数据删除
数据合并
事务控制
性能优化
VALUES插入
子查询插入
多表插入
批量插入
简单更新
关联更新
条件更新
批量更新
条件删除
关联删除
批量删除
级联删除
MERGE语句
UPSERT操作
数据同步
COMMIT提交
ROLLBACK回滚
SAVEPOINT保存点
批量操作
并行处理
索引优化

2. INSERT语句详解

2.1 基础插入操作

INSERT就像往盒子里放东西,有很多种放法:

-- 最基本的插入
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date)
VALUES (1001, 'John', 'Doe', 'john.doe@company.com', SYSDATE);-- 插入所有列(按表结构顺序)
INSERT INTO employees
VALUES (1002, 'Jane', 'Smith', 'jane.smith@company.com', '555-1234', SYSDATE, 'IT_PROG', 5000, NULL, 100, 20);-- 插入部分列(其他列使用默认值或NULL)
INSERT INTO employees (employee_id, first_name, last_name, email)
VALUES (1003, 'Bob', 'Johnson', 'bob.johnson@company.com');-- 使用序列插入
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date)
VALUES (emp_seq.NEXTVAL, 'Alice', 'Brown', 'alice.brown@company.com', SYSDATE);-- 插入计算值
INSERT INTO order_summary (order_id, order_date, total_amount, tax_amount, final_amount)
VALUES (1001, SYSDATE, 1000, 1000 * 0.08, 1000 * 1.08);-- 插入函数结果
INSERT INTO audit_log (log_id, table_name, action_type, action_date, username)
VALUES (audit_seq.NEXTVAL, 'EMPLOYEES', 'INSERT', SYSTIMESTAMP, USER);

2.2 子查询插入

用子查询插入就像批量搬家,一次性处理大量数据:

-- 从其他表插入数据
INSERT INTO employees_backup
SELECT * FROM employees WHERE department_id = 20;-- 插入聚合数据
INSERT INTO department_summary (dept_id, dept_name, emp_count, avg_salary, total_salary)
SELECT d.department_id,d.department_name,COUNT(e.employee_id),AVG(e.salary),SUM(e.salary)
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;-- 插入复杂计算结果
INSERT INTO sales_analysis (region, year, month, total_sales, growth_rate)
SELECT region,EXTRACT(YEAR FROM sale_date) as year,EXTRACT(MONTH FROM sale_date) as month,SUM(amount) as total_sales,ROUND((SUM(amount) - LAG(SUM(amount)) OVER (PARTITION BY region ORDER BY EXTRACT(YEAR FROM sale_date), EXTRACT(MONTH FROM sale_date))) / LAG(SUM(amount)) OVER (PARTITION BY region ORDER BY EXTRACT(YEAR FROM sale_date), EXTRACT(MONTH FROM sale_date)) * 100, 2) as growth_rate
FROM sales_data
GROUP BY region, EXTRACT(YEAR FROM sale_date), EXTRACT(MONTH FROM sale_date);-- 条件插入
INSERT INTO high_performers (employee_id, performance_score, bonus_eligible)
SELECT employee_id,(salary * 0.1 + COALESCE(commission_pct * salary, 0)) as performance_score,CASE WHEN salary > 8000 THEN 'Y' ELSE 'N' END
FROM employees
WHERE hire_date < ADD_MONTHS(SYSDATE, -12)AND department_id IN (10, 20, 30);

2.3 多表插入

Oracle的多表插入功能就像是"一箭多雕",一次操作影响多个表:

-- 无条件多表插入
INSERT ALLINTO sales_summary (region, total_amount) VALUES (region, amount)INTO sales_detail (sale_id, customer_id, amount) VALUES (sale_id, customer_id, amount)INTO sales_audit (sale_id, insert_date) VALUES (sale_id, SYSDATE)
SELECT sale_id, region, customer_id, amount
FROM sales_data
WHERE sale_date = TRUNC(SYSDATE);-- 条件多表插入
INSERT ALLWHEN amount > 10000 THENINTO high_value_sales (sale_id, amount, customer_id) VALUES (sale_id, amount, customer_id)WHEN amount BETWEEN 1000 AND 10000 THENINTO medium_value_sales (sale_id, amount, customer_id) VALUES (sale_id, amount, customer_id)ELSEINTO low_value_sales (sale_id, amount, customer_id) VALUES (sale_id, amount, customer_id)
SELECT sale_id, amount, customer_id
FROM sales_data
WHERE sale_date >= TRUNC(SYSDATE);-- 旋转插入(数据透视)
INSERT ALLINTO quarterly_sales (year, quarter, region, amount) VALUES (year, 1, region, q1_sales)INTO quarterly_sales (year, quarter, region, amount) VALUES (year, 2, region, q2_sales)INTO quarterly_sales (year, quarter, region, amount) VALUES (year, 3, region, q3_sales)INTO quarterly_sales (year, quarter, region, amount) VALUES (year, 4, region, q4_sales)
SELECT year, region, q1_sales, q2_sales, q3_sales, q4_sales
FROM annual_sales_pivot;-- 复杂的条件多表插入
INSERT ALLWHEN department_id = 10 THENINTO dept_10_employees (employee_id, name, salary) VALUES (employee_id, first_name || ' ' || last_name, salary)INTO dept_10_salaries (employee_id, salary, bonus) VALUES (employee_id, salary, salary * 0.1)WHEN department_id = 20 THENINTO dept_20_employees (employee_id, name, salary) VALUES (employee_id, first_name || ' ' || last_name, salary)WHEN salary > 5000 THENINTO high_earners (employee_id, salary, department_id) VALUES (employee_id, salary, department_id)
SELECT employee_id, first_name, last_name, salary, department_id
FROM employees
WHERE status = 'ACTIVE';

2.4 批量插入优化

批量插入就像是用卡车运货,比一件件搬要高效得多:

-- 使用FORALL进行批量插入(PL/SQL)
DECLARETYPE emp_array IS TABLE OF employees%ROWTYPE;l_employees emp_array;
BEGIN-- 准备数据l_employees := emp_array();FOR i IN 1..10000 LOOPl_employees.EXTEND;l_employees(i).employee_id := emp_seq.NEXTVAL;l_employees(i).first_name := 'Employee' || i;l_employees(i).last_name := 'Test';l_employees(i).email := 'emp' || i || '@test.com';l_employees(i).hire_date := SYSDATE;l_employees(i).job_id := 'IT_PROG';l_employees(i).salary := 5000 + MOD(i, 3000);END LOOP;-- 批量插入FORALL i IN l_employees.FIRST..l_employees.LASTINSERT INTO employees VALUES l_employees(i);COMMIT;DBMS_OUTPUT.PUT_LINE('Inserted ' || l_employees.COUNT || ' employees');
END;
/-- 使用INSERT /*+ APPEND */ 提高性能
INSERT /*+ APPEND */ INTO employees_archive
SELECT * FROM employees WHERE hire_date < DATE '2020-01-01';-- 并行插入
ALTER SESSION ENABLE PARALLEL DML;
INSERT /*+ PARALLEL(employees_archive, 4) */ INTO employees_archive
SELECT * FROM employees WHERE department_id IN (10, 20, 30);-- 使用NOLOGGING减少日志
ALTER TABLE employees_temp NOLOGGING;
INSERT /*+ APPEND NOLOGGING */ INTO employees_temp
SELECT * FROM external_employee_data;
ALTER TABLE employees_temp LOGGING;

3. UPDATE语句详解

3.1 基础更新操作

UPDATE就像是重新装修房间,可以改变现有的数据:

-- 简单更新
UPDATE employees 
SET salary = 6000 
WHERE employee_id = 1001;-- 更新多个字段
UPDATE employees 
SET salary = salary * 1.1,commission_pct = 0.05,modified_date = SYSDATE
WHERE department_id = 20;-- 使用表达式更新
UPDATE employees 
SET salary = CASE WHEN job_id = 'IT_PROG' THEN salary * 1.15WHEN job_id = 'SA_REP' THEN salary * 1.10WHEN job_id = 'ST_CLERK' THEN salary * 1.05ELSE salary * 1.03
END,
email = LOWER(first_name || '.' || last_name || '@company.com')
WHERE hire_date < ADD_MONTHS(SYSDATE, -12);-- 使用函数更新
UPDATE products 
SET product_name = INITCAP(product_name),description = REGEXP_REPLACE(description, '\s+', ' '),modified_date = SYSTIMESTAMP
WHERE category_id = 5;-- 条件更新
UPDATE orders 
SET status = 'SHIPPED',shipped_date = SYSDATE,tracking_number = 'TRK' || LPAD(order_id, 10, '0')
WHERE status = 'PROCESSING' AND order_date < SYSDATE - 2;

3.2 关联更新

关联更新就像是根据其他房间的情况来调整当前房间:

-- 使用子查询更新
UPDATE employees e
SET salary = (SELECT AVG(salary) * 1.1FROM employees WHERE department_id = e.department_id
)
WHERE job_id = 'IT_PROG';-- 多列子查询更新
UPDATE employees e
SET (salary, commission_pct) = (SELECT j.max_salary * 0.8, 0.1FROM jobs jWHERE j.job_id = e.job_id
)
WHERE hire_date > ADD_MONTHS(SYSDATE, -6);-- 使用EXISTS的关联更新
UPDATE products p
SET discontinued = 'Y'
WHERE NOT EXISTS (SELECT 1 FROM order_details od WHERE od.product_id = p.product_id AND od.order_date > ADD_MONTHS(SYSDATE, -12)
);-- 复杂关联更新
UPDATE customers c
SET (credit_limit, customer_status) = (SELECT CASE WHEN total_orders > 100 THEN 50000WHEN total_orders > 50 THEN 25000WHEN total_orders > 10 THEN 10000ELSE 5000END,CASE WHEN total_orders > 50 THEN 'VIP'WHEN total_orders > 10 THEN 'REGULAR'ELSE 'NEW'ENDFROM (SELECT customer_id,COUNT(*) as total_orders,SUM(total_amount) as total_spentFROM ordersWHERE order_date > ADD_MONTHS(SYSDATE, -12)GROUP BY customer_id) order_statsWHERE order_stats.customer_id = c.customer_id
)
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);

3.3 批量更新优化

-- 使用MERGE进行高效更新
MERGE INTO employees e
USING (SELECT employee_id, salary * 1.1 as new_salaryFROM employeesWHERE performance_rating = 'EXCELLENT'
) perf
ON (e.employee_id = perf.employee_id)
WHEN MATCHED THENUPDATE SET salary = perf.new_salary,last_raise_date = SYSDATE;-- 分批更新大表
DECLAREv_batch_size NUMBER := 10000;v_total_updated NUMBER := 0;v_batch_updated NUMBER;
BEGINLOOPUPDATE employees SET status = 'INACTIVE'WHERE status = 'TEMP' AND hire_date < ADD_MONTHS(SYSDATE, -24)AND ROWNUM <= v_batch_size;v_batch_updated := SQL%ROWCOUNT;v_total_updated := v_total_updated + v_batch_updated;COMMIT;EXIT WHEN v_batch_updated = 0;DBMS_OUTPUT.PUT_LINE('Updated ' || v_batch_updated || ' rows');END LOOP;DBMS_OUTPUT.PUT_LINE('Total updated: ' || v_total_updated || ' rows');
END;
/-- 并行更新
ALTER SESSION ENABLE PARALLEL DML;
UPDATE /*+ PARALLEL(employees, 4) */ employees
SET salary = salary * 1.05
WHERE department_id IN (10, 20, 30, 40);
COMMIT;

4. DELETE语句详解

4.1 基础删除操作

DELETE就像是清理房间,移除不需要的物品:

-- 简单删除
DELETE FROM employees WHERE employee_id = 1001;-- 条件删除
DELETE FROM employees 
WHERE status = 'TERMINATED' AND termination_date < ADD_MONTHS(SYSDATE, -24);-- 使用子查询删除
DELETE FROM order_details
WHERE order_id IN (SELECT order_id FROM orders WHERE order_date < ADD_MONTHS(SYSDATE, -36)AND status = 'CANCELLED'
);-- 使用EXISTS删除
DELETE FROM products p
WHERE EXISTS (SELECT 1 FROM discontinued_products dp WHERE dp.product_id = p.product_id
);-- 复杂条件删除
DELETE FROM sales_data
WHERE sale_date < ADD_MONTHS(SYSDATE, -60)AND customer_id NOT IN (SELECT DISTINCT customer_id FROM orders WHERE order_date > ADD_MONTHS(SYSDATE, -12))AND amount < 100;

4.2 关联删除

-- 删除孤儿记录
DELETE FROM order_details od
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.order_id = od.order_id
);-- 基于统计信息删除
DELETE FROM customers c
WHERE customer_id NOT IN (SELECT DISTINCT customer_id FROM orders WHERE order_date > ADD_MONTHS(SYSDATE, -36)
)
AND created_date < ADD_MONTHS(SYSDATE, -48);-- 删除重复数据(保留最新的)
DELETE FROM employees e1
WHERE e1.rowid > (SELECT MIN(e2.rowid)FROM employees e2WHERE e2.email = e1.email
);-- 或者使用分析函数删除重复数据
DELETE FROM (SELECT rowid, ROW_NUMBER() OVER (PARTITION BY email ORDER BY hire_date DESC, employee_id DESC) as rnFROM employees
)
WHERE rn > 1;

4.3 批量删除优化

-- 分批删除避免锁表
DECLAREv_batch_size NUMBER := 5000;v_total_deleted NUMBER := 0;v_batch_deleted NUMBER;
BEGINLOOPDELETE FROM audit_log WHERE log_date < ADD_MONTHS(SYSDATE, -12)AND ROWNUM <= v_batch_size;v_batch_deleted := SQL%ROWCOUNT;v_total_deleted := v_total_deleted + v_batch_deleted;COMMIT;EXIT WHEN v_batch_deleted = 0;-- 给其他会话一些处理时间DBMS_LOCK.SLEEP(0.1);DBMS_OUTPUT.PUT_LINE('Deleted ' || v_batch_deleted || ' rows');END LOOP;DBMS_OUTPUT.PUT_LINE('Total deleted: ' || v_total_deleted || ' rows');
END;
/-- 使用TRUNCATE快速清空表(注意:不能回滚)
TRUNCATE TABLE temp_calculations;-- 删除分区数据
ALTER TABLE sales_data DROP PARTITION sales_2020_q1;-- 并行删除
ALTER SESSION ENABLE PARALLEL DML;
DELETE /*+ PARALLEL(old_transactions, 4) */ FROM old_transactions
WHERE transaction_date < ADD_MONTHS(SYSDATE, -84);
COMMIT;

5. MERGE语句详解

5.1 MERGE基础语法

MERGE就像是"智能搬家",能够根据情况决定是搬新家具还是更新现有家具:

-- 基本MERGE语法
MERGE INTO employees e
USING employee_updates eu
ON (e.employee_id = eu.employee_id)
WHEN MATCHED THENUPDATE SET salary = eu.new_salary,department_id = eu.new_department_id,modified_date = SYSDATE
WHEN NOT MATCHED THENINSERT (employee_id, first_name, last_name, email, hire_date, salary, department_id)VALUES (eu.employee_id, eu.first_name, eu.last_name, eu.email, SYSDATE, eu.new_salary, eu.new_department_id);-- 带条件的MERGE
MERGE INTO products p
USING (SELECT product_id, new_price, supplier_idFROM product_price_updatesWHERE effective_date = TRUNC(SYSDATE)
) ppu
ON (p.product_id = ppu.product_id)
WHEN MATCHED THENUPDATE SET unit_price = ppu.new_price,modified_date = SYSDATEWHERE p.supplier_id = ppu.supplier_id  -- 额外条件
WHEN NOT MATCHED THENINSERT (product_id, unit_price, supplier_id, created_date)VALUES (ppu.product_id, ppu.new_price, ppu.supplier_id, SYSDATE)WHERE ppu.new_price > 0;  -- 插入条件

5.2 复杂MERGE应用

-- 数据仓库ETL场景
MERGE INTO fact_sales fs
USING (SELECT s.sale_id,s.product_id,s.customer_id,s.sale_date,s.quantity,s.unit_price,s.total_amount,p.category_id,c.region_id,EXTRACT(YEAR FROM s.sale_date) as sale_year,EXTRACT(MONTH FROM s.sale_date) as sale_monthFROM staging_sales sJOIN products p ON s.product_id = p.product_idJOIN customers c ON s.customer_id = c.customer_idWHERE s.processed_flag = 'N'
) stage
ON (fs.sale_id = stage.sale_id)
WHEN MATCHED THENUPDATE SET quantity = stage.quantity,unit_price = stage.unit_price,total_amount = stage.total_amount,last_updated = SYSDATEWHERE fs.total_amount != stage.total_amount  -- 只更新变化的记录
WHEN NOT MATCHED THENINSERT (sale_id, product_id, customer_id, sale_date,quantity, unit_price, total_amount,category_id, region_id, sale_year, sale_month,created_date) VALUES (stage.sale_id, stage.product_id, stage.customer_id, stage.sale_date,stage.quantity, stage.unit_price, stage.total_amount,stage.category_id, stage.region_id, stage.sale_year, stage.sale_month,SYSDATE);-- 库存管理MERGE
MERGE INTO inventory i
USING (SELECT product_id,SUM(CASE WHEN transaction_type = 'IN' THEN quantity ELSE -quantity END) as net_changeFROM inventory_transactionsWHERE transaction_date = TRUNC(SYSDATE)AND processed_flag = 'N'GROUP BY product_id
) it
ON (i.product_id = it.product_id)
WHEN MATCHED THENUPDATE SET quantity_on_hand = quantity_on_hand + it.net_change,last_updated = SYSDATEWHERE it.net_change != 0
WHEN NOT MATCHED THENINSERT (product_id, quantity_on_hand, last_updated)VALUES (it.product_id, it.net_change, SYSDATE)WHERE it.net_change > 0;-- 客户360度视图更新
MERGE INTO customer_360 c360
USING (WITH customer_stats AS (SELECT customer_id,COUNT(*) as total_orders,SUM(total_amount) as total_spent,AVG(total_amount) as avg_order_value,MAX(order_date) as last_order_date,MIN(order_date) as first_order_dateFROM ordersWHERE order_date >= ADD_MONTHS(SYSDATE, -12)GROUP BY customer_id),customer_segments AS (SELECT customer_id,CASE WHEN total_spent > 10000 THEN 'VIP'WHEN total_spent > 5000 THEN 'PREMIUM'WHEN total_spent > 1000 THEN 'REGULAR'ELSE 'BASIC'END as segment,CASE WHEN last_order_date > SYSDATE - 30 THEN 'ACTIVE'WHEN last_order_date > SYSDATE - 90 THEN 'INACTIVE'ELSE 'DORMANT'END as statusFROM customer_stats)SELECT cs.customer_id,cs.total_orders,cs.total_spent,cs.avg_order_value,cs.last_order_date,cs.first_order_date,seg.segment,seg.statusFROM customer_stats csJOIN customer_segments seg ON cs.customer_id = seg.customer_id
) stats
ON (c360.customer_id = stats.customer_id)
WHEN MATCHED THENUPDATE SET total_orders = stats.total_orders,total_spent = stats.total_spent,avg_order_value = stats.avg_order_value,last_order_date = stats.last_order_date,customer_segment = stats.segment,customer_status = stats.status,last_updated = SYSDATE
WHEN NOT MATCHED THENINSERT (customer_id, total_orders, total_spent, avg_order_value,last_order_date, first_order_date, customer_segment, customer_status,created_date, last_updated) VALUES (stats.customer_id, stats.total_orders, stats.total_spent, stats.avg_order_value,stats.last_order_date, stats.first_order_date, stats.segment, stats.status,SYSDATE, SYSDATE);

6. 事务控制

6.1 事务基础

事务就像是一套组合拳,要么全部成功,要么全部失败:

Oracle事务控制
COMMIT提交
ROLLBACK回滚
SAVEPOINT保存点
事务隔离级别
永久保存更改
释放锁资源
撤销所有更改
回到事务开始
设置回滚点
部分回滚
READ COMMITTED
SERIALIZABLE
-- 基本事务控制
BEGININSERT INTO orders (order_id, customer_id, order_date, total_amount)VALUES (1001, 123, SYSDATE, 1500);INSERT INTO order_details (order_id, product_id, quantity, unit_price)VALUES (1001, 456, 2, 750);UPDATE inventory SET quantity_on_hand = quantity_on_hand - 2WHERE product_id = 456;COMMIT;  -- 提交所有更改
EXCEPTIONWHEN OTHERS THENROLLBACK;  -- 出错时回滚RAISE;
END;
/-- 使用保存点
DECLAREv_error_count NUMBER := 0;
BEGINSAVEPOINT start_processing;-- 第一批操作INSERT INTO batch_log (batch_id, status) VALUES (1, 'STARTED');SAVEPOINT batch1_complete;-- 第二批操作BEGINUPDATE products SET unit_price = unit_price * 1.1;INSERT INTO price_history (product_id, old_price, new_price, change_date)SELECT product_id, unit_price / 1.1, unit_price, SYSDATE FROM products;SAVEPOINT batch2_complete;EXCEPTIONWHEN OTHERS THENROLLBACK TO batch1_complete;  -- 只回滚第二批操作v_error_count := v_error_count + 1;END;-- 第三批操作BEGINDELETE FROM temp_calculations WHERE created_date < SYSDATE - 1;COMMIT;EXCEPTIONWHEN OTHERS THENROLLBACK TO batch2_complete;v_error_count := v_error_count + 1;END;IF v_error_count > 0 THENDBMS_OUTPUT.PUT_LINE('Completed with ' || v_error_count || ' errors');END IF;
END;
/

6.2 复杂事务处理

-- 银行转账事务示例
CREATE OR REPLACE PROCEDURE transfer_funds(p_from_account IN NUMBER,p_to_account IN NUMBER,p_amount IN NUMBER
)
ISv_from_balance NUMBER;v_to_balance NUMBER;insufficient_funds EXCEPTION;account_not_found EXCEPTION;
BEGINSAVEPOINT before_transfer;-- 锁定源账户并检查余额SELECT balance INTO v_from_balanceFROM accountsWHERE account_id = p_from_accountFOR UPDATE;IF v_from_balance < p_amount THENRAISE insufficient_funds;END IF;-- 锁定目标账户SELECT balance INTO v_to_balanceFROM accountsWHERE account_id = p_to_accountFOR UPDATE;-- 执行转账UPDATE accounts SET balance = balance - p_amount,last_transaction_date = SYSDATEWHERE account_id = p_from_account;UPDATE accounts SET balance = balance + p_amount,last_transaction_date = SYSDATEWHERE account_id = p_to_account;-- 记录交易历史INSERT INTO transactions (transaction_id, account_id, transaction_type, amount, balance_after)VALUES (trans_seq.NEXTVAL, p_from_account, 'DEBIT', -p_amount, v_from_balance - p_amount);INSERT INTO transactions (transaction_id, account_id, transaction_type, amount, balance_after)VALUES (trans_seq.NEXTVAL, p_to_account, 'CREDIT', p_amount, v_to_balance + p_amount);COMMIT;DBMS_OUTPUT.PUT_LINE('Transfer completed successfully');EXCEPTIONWHEN insufficient_funds THENROLLBACK TO before_transfer;RAISE_APPLICATION_ERROR(-20001, 'Insufficient funds');WHEN NO_DATA_FOUND THENROLLBACK TO before_transfer;RAISE_APPLICATION_ERROR(-20002, 'Account not found');WHEN OTHERS THENROLLBACK TO before_transfer;RAISE_APPLICATION_ERROR(-20003, 'Transfer failed: ' || SQLERRM);
END;
/-- 批量处理事务
CREATE OR REPLACE PROCEDURE process_daily_orders
ISCURSOR order_cursor ISSELECT order_id, customer_id, total_amountFROM pending_ordersWHERE order_date = TRUNC(SYSDATE)FOR UPDATE SKIP LOCKED;  -- 跳过被锁定的行v_processed_count NUMBER := 0;v_error_count NUMBER := 0;v_batch_size NUMBER := 100;BEGINFOR order_rec IN order_cursor LOOPBEGINSAVEPOINT process_order;-- 处理订单逻辑UPDATE inventory SET quantity_on_hand = quantity_on_hand - (SELECT SUM(quantity) FROM order_details WHERE order_id = order_rec.order_id)WHERE product_id IN (SELECT product_id FROM order_details WHERE order_id = order_rec.order_id);-- 更新订单状态UPDATE pending_orders SET status = 'PROCESSED',processed_date = SYSDATEWHERE order_id = order_rec.order_id;v_processed_count := v_processed_count + 1;-- 批量提交IF MOD(v_processed_count, v_batch_size) = 0 THENCOMMIT;DBMS_OUTPUT.PUT_LINE('Processed ' || v_processed_count || ' orders');END IF;EXCEPTIONWHEN OTHERS THENROLLBACK TO process_order;v_error_count := v_error_count + 1;-- 记录错误INSERT INTO error_log (error_date, error_message, order_id)VALUES (SYSDATE, SQLERRM, order_rec.order_id);END;END LOOP;COMMIT;  -- 提交剩余的更改DBMS_OUTPUT.PUT_LINE('Processing complete: ' || v_processed_count || ' processed, ' || v_error_count || ' errors');
END;
/

7. 性能优化技巧

7.1 批量操作优化

DML性能优化策略
批量操作
索引优化
并行处理
分区策略
FORALL批量DML
BULK COLLECT
MERGE语句
合适的索引
避免索引失效
函数索引
并行DML
并行查询
分区并行
分区消除
分区智能连接
分区索引
-- 高效的批量插入
DECLARETYPE number_array IS TABLE OF NUMBER;TYPE varchar_array IS TABLE OF VARCHAR2(100);TYPE date_array IS TABLE OF DATE;l_emp_ids number_array;l_names varchar_array;l_emails varchar_array;l_hire_dates date_array;v_batch_size NUMBER := 10000;
BEGIN-- 准备数据SELECT employee_id, first_name || ' ' || last_name, email, hire_dateBULK COLLECT INTO l_emp_ids, l_names, l_emails, l_hire_datesFROM external_employeesWHERE import_flag = 'Y';-- 批量插入FORALL i IN 1..l_emp_ids.COUNT SAVE EXCEPTIONSINSERT INTO employees (employee_id, full_name, email, hire_date)VALUES (l_emp_ids(i), l_names(i), l_emails(i), l_hire_dates(i));COMMIT;DBMS_OUTPUT.PUT_LINE('Inserted ' || l_emp_ids.COUNT || ' employees');EXCEPTIONWHEN OTHERS THENIF SQLCODE = -24381 THEN  -- FORALL with SAVE EXCEPTIONSFOR i IN 1..SQL%BULK_EXCEPTIONS.COUNT LOOPDBMS_OUTPUT.PUT_LINE('Error ' || i || ': ' || SQL%BULK_EXCEPTIONS(i).ERROR_CODE || ' at index ' ||SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);END LOOP;END IF;RAISE;
END;
/-- 优化的大表更新
CREATE OR REPLACE PROCEDURE optimize_large_table_update
ISCURSOR update_cursor ISSELECT rowid, employee_id, salaryFROM employeesWHERE last_review_date < ADD_MONTHS(SYSDATE, -12)AND status = 'ACTIVE';TYPE rowid_array IS TABLE OF ROWID;TYPE number_array IS TABLE OF NUMBER;l_rowids rowid_array;l_new_salaries number_array;v_batch_size NUMBER := 5000;v_total_updated NUMBER := 0;
BEGINOPEN update_cursor;LOOPFETCH update_cursor BULK COLLECT INTO l_rowids, l_new_salariesLIMIT v_batch_size;EXIT WHEN l_rowids.COUNT = 0;-- 计算新薪资FOR i IN 1..l_rowids.COUNT LOOPl_new_salaries(i) := l_new_salaries(i) * 1.05;END LOOP;-- 批量更新FORALL i IN 1..l_rowids.COUNTUPDATE employeesSET salary = l_new_salaries(i),last_review_date = SYSDATEWHERE rowid = l_rowids(i);v_total_updated := v_total_updated + l_rowids.COUNT;COMMIT;DBMS_OUTPUT.PUT_LINE('Updated ' || l_rowids.COUNT || ' rows');END LOOP;CLOSE update_cursor;DBMS_OUTPUT.PUT_LINE('Total updated: ' || v_total_updated || ' rows');
END;
/

7.2 索引和查询优化

-- 使用提示优化DML
-- 强制使用特定索引
UPDATE /*+ INDEX(employees, idx_emp_dept_id) */ employees
SET salary = salary * 1.1
WHERE department_id = 20;-- 并行DML
ALTER SESSION ENABLE PARALLEL DML;
INSERT /*+ PARALLEL(sales_archive, 4) */ INTO sales_archive
SELECT * FROM sales_data WHERE sale_date < ADD_MONTHS(SYSDATE, -36);-- 使用APPEND提示减少日志
INSERT /*+ APPEND */ INTO employees_backup
SELECT * FROM employees WHERE status = 'INACTIVE';-- 优化MERGE性能
MERGE /*+ USE_HASH(target source) */ INTO customer_summary target
USING (SELECT /*+ PARALLEL(orders, 2) */customer_id,COUNT(*) as order_count,SUM(total_amount) as total_spentFROM ordersWHERE order_date >= ADD_MONTHS(SYSDATE, -12)GROUP BY customer_id
) source
ON (target.customer_id = source.customer_id)
WHEN MATCHED THENUPDATE SET order_count = source.order_count,total_spent = source.total_spent,last_updated = SYSDATE
WHEN NOT MATCHED THENINSERT (customer_id, order_count, total_spent, last_updated)VALUES (source.customer_id, source.order_count, source.total_spent, SYSDATE);-- 分区智能操作
-- 只操作相关分区
INSERT INTO sales_data PARTITION (sales_2024_q1)
SELECT * FROM staging_sales 
WHERE sale_date BETWEEN DATE '2024-01-01' AND DATE '2024-03-31';-- 分区交换(快速数据移动)
ALTER TABLE sales_data EXCHANGE PARTITION sales_2023_q1 
WITH TABLE sales_2023_q1_archive;

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

相关文章:

  • 没有公网ip如何实现外网访问?本地内网服务器应用端口让公网连接使用方法
  • 基于RK3568/RK3588/全志H3/飞腾芯片/音视频通话程序/语音对讲/视频对讲/实时性好/极低延迟
  • 使用el-input数字校验,输入汉字之后校验取消不掉
  • PCB设计实践(三十)地平面完整性
  • Flume 自定义拦截器开发实战:添加时间戳与 JSON 处理
  • Vue 3.0 中的路由导航守卫详解
  • 头歌之动手学人工智能-Pytorch 之线性回归
  • python打卡训练营打卡记录day40
  • 电网即插即用介绍
  • mysql数据库基础命令总结常用10个
  • 2025年全国青少年信息素养大赛 scratch图形化编程挑战赛 小高组初赛 内部模拟试卷解析
  • 【技术支持】安卓11开机启动设置
  • 深入了解MCP基础与架构
  • 从图像处理到深度学习:直播美颜SDK的人脸美型算法详解
  • 用于工业设备的高精度仪表放大器“NL9620”开始上市~日本首家!高EMC性能的仪表放大器
  • 贪心算法实战3
  • torch.zeros()用法简介
  • DAY 40 训练和测试的规范写法
  • EMQX社区版5.8.5集群搭建踩坑记
  • 30V/150A MOSFET 150N03在无人机驱动动力系统中的性能边界与热设计挑战
  • 高权重网站发外链/2345网址导航
  • 路由器做网站/外贸营销渠道
  • 乐从做网站/网络游戏营销策略
  • 个人网站备案可以做博客吗/友链交易
  • 网站设计公司有哪些/百度推广广告收费标准
  • 网站建设需要下载哪些软件有哪些/石家庄百度seo代理