一、项目架构设计
1.1 完整的学生成绩管理系统架构
-- 在students表基础上构建完整系统
-- 1. 创建院系维度表
CREATE TABLE departments AS
SELECT
ROWNUM as dept_id,
CASE
WHEN 学科编号 = 101 THEN '文学院'
WHEN 学科编号 = 102 THEN '理学院'
WHEN 学科编号 = 103 THEN '外语学院'
ELSE '通用学院'
END as dept_name,
CASE
WHEN 学科编号 = 101 THEN '文学类学科'
WHEN 学科编号 = 102 THEN '数理类学科'
WHEN 学科编号 = 103 THEN '语言类学科'
ELSE '综合类'
END as dept_type
FROM (SELECT DISTINCT 学科编号 FROM students WHERE ROWNUM <= 3);
-- 2. 创建时间维度表(模拟考试时间)
CREATE TABLE exam_periods AS
SELECT
ROWNUM as period_id,
ADD_MONTHS(DATE '2023-09-01', (ROWNUM-1)*3) as exam_date,
CASE ROWNUM
WHEN 1 THEN '第一学期期中'
WHEN 2 THEN '第一学期期末'
WHEN 3 THEN '第二学期期中'
WHEN 4 THEN '第二学期期末'
END as period_name
FROM dual CONNECT BY ROWNUM <= 4;
-- 3. 扩展主表(不修改原结构,通过视图扩展)
CREATE OR REPLACE VIEW v_students_comprehensive AS
SELECT
s.学号,
s.姓名,
s.性别,
s.学科编号,
s.学科,
s.成绩,
d.dept_name as 院系,
d.dept_type as 学科类型,
ep.period_name as 考试期次,
ep.exam_date as 考试日期,
-- 动态计算字段
CASE
WHEN s.成绩 >= 130 THEN '卓越'
WHEN s.成绩 >= 115 THEN '优秀'
WHEN s.成绩 >= 90 THEN '良好'
WHEN s.成绩 >= 60 THEN '及格'
ELSE '不及格'
END as 成绩等级,
-- 标准化分数
ROUND((s.成绩 - AVG(s.成绩) OVER (PARTITION BY s.学科)) /
STDDEV(s.成绩) OVER (PARTITION BY s.学科), 2) as 标准分
FROM students s
CROSS JOIN (SELECT * FROM exam_periods WHERE period_id = 1) ep
LEFT JOIN departments d ON s.学科编号 = d.dept_id;
二、高级分析函数实战
2.1 多层次分析查询
-- 学生成绩全景分析
WITH student_analysis AS (
SELECT
学号,
姓名,
性别,
学科,
成绩,
-- 个人层面分析
AVG(成绩) OVER (PARTITION BY 姓名) as 个人平均分,
COUNT(*) OVER (PARTITION BY 姓名) as 个人科目数,
-- 学科层面分析
AVG(成绩) OVER (PARTITION BY 学科) as 学科平均分,
MAX(成绩) OVER (PARTITION BY 学科) as 学科最高分,
MIN(成绩) OVER (PARTITION BY 学科) as 学科最低分,
-- 排名分析
ROW_NUMBER() OVER (PARTITION BY 学科 ORDER BY 成绩 DESC) as 学科内排名,
DENSE_RANK() OVER (ORDER BY AVG(成绩) OVER (PARTITION BY 姓名) DESC) as 总排名,
-- 趋势分析
LAG(成绩) OVER (PARTITION BY 姓名 ORDER BY 学科) as 前一科成绩,
LEAD(成绩) OVER (PARTITION BY 姓名 ORDER BY 学科) as 后一科成绩,
-- 分布分析
PERCENT_RANK() OVER (PARTITION BY 学科 ORDER BY 成绩) as 成绩百分位,
NTILE(4) OVER (PARTITION BY 学科 ORDER BY 成绩) as 成绩四分位,
-- 稳定性分析
STDDEV(成绩) OVER (PARTITION BY 姓名) as 成绩标准差,
MAX(成绩) OVER (PARTITION BY 姓名) - MIN(成绩) OVER (PARTITION BY 姓名) as 成绩极差
FROM students
)
SELECT
姓名,
学科,
成绩,
个人平均分,
学科平均分,
学科内排名,
总排名,
ROUND(成绩标准差, 2) as 稳定性指标,
CASE
WHEN 成绩 > 学科平均分 THEN '高于平均'
ELSE '低于平均'
END as 学科比较,
CASE
WHEN 成绩标准差 < 10 THEN '成绩稳定'
WHEN 成绩标准差 BETWEEN 10 AND 20 THEN '成绩波动一般'
ELSE '成绩波动较大'
END as 稳定性评价
FROM student_analysis
ORDER BY 总排名, 姓名, 学科;
2.2 移动窗口与累积分析
-- 复杂窗口函数应用
SELECT
姓名,
学科,
成绩,
-- 移动平均(3科移动窗口)
ROUND(AVG(成绩) OVER (
PARTITION BY 姓名
ORDER BY 学科
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
), 2) as 三科移动平均,
-- 累积统计
SUM(成绩) OVER (
PARTITION BY 姓名
ORDER BY 学科
ROWS UNBOUNDED PRECEDING
) as 累计总分,
AVG(成绩) OVER (
PARTITION BY 姓名
ORDER BY 学科
ROWS UNBOUNDED PRECEDING
) as 累计平均分,
-- 同比环比分析(模拟多期数据)
ROUND((成绩 - LAG(成绩, 1) OVER (PARTITION BY 姓名, 学科 ORDER BY 学号)) /
LAG(成绩, 1) OVER (PARTITION BY 姓名, 学科 ORDER BY 学号) * 100, 2) as 成绩增长率,
-- 排名变化趋势
RANK() OVER (PARTITION BY 学科 ORDER BY 成绩 DESC) -
LAG(RANK() OVER (PARTITION BY 学科 ORDER BY 成绩 DESC))
OVER (PARTITION BY 姓名 ORDER BY 学科) as 排名变化,
-- 累积分布
CUME_DIST() OVER (PARTITION BY 学科 ORDER BY 成绩) as 累积分布比例
FROM students
ORDER BY 姓名, 学科;
三、性能优化实战
3.1 执行计划深度优化
-- 复杂查询执行计划分析
EXPLAIN PLAN FOR
WITH subject_stats AS (
SELECT
学科,
AVG(成绩) as avg_score,
STDDEV(成绩) as std_score,
COUNT(*) as student_count
FROM students
GROUP BY 学科
),
student_performance AS (
SELECT
姓名,
AVG(成绩) as personal_avg,
COUNT(*) as subject_count,
MAX(成绩) as best_score,
MIN(成绩) as worst_score
FROM students
GROUP BY 姓名
HAVING COUNT(*) = 3
),
ranked_students AS (
SELECT
s.学号,
s.姓名,
s.学科,
s.成绩,
sp.personal_avg,
ss.avg_score as subject_avg,
(s.成绩 - ss.avg_score) / ss.std_score as z_score,
CASE
WHEN s.成绩 > ss.avg_score + ss.std_score THEN '超常发挥'
WHEN s.成绩 > ss.avg_score THEN '正常发挥'
ELSE '发挥失常'
END as performance_level
FROM students s
JOIN student_performance sp ON s.姓名 = sp.姓名
JOIN subject_stats ss ON s.学科 = ss.学科
WHERE s.成绩 > ss.avg_score
)
SELECT
rs.姓名,
rs.学科,
rs.成绩,
rs.personal_avg,
rs.subject_avg,
ROUND(rs.z_score, 2) as 标准分,
rs.performance_level as 发挥水平,
RANK() OVER (ORDER BY rs.z_score DESC) as 综合排名
FROM ranked_students rs
ORDER BY 综合排名, rs.姓名;
-- 显示执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 创建优化索引
CREATE INDEX idx_students_comprehensive ON students(姓名, 学科, 成绩);
CREATE INDEX idx_students_subject_score ON students(学科, 成绩);
CREATE INDEX idx_students_name_perf ON students(姓名, 成绩);
-- 使用提示强制优化
SELECT /*+ INDEX(s IDX_STUDENTS_COMPREHENSIVE) USE_NL(sp) */
s.姓名,
s.学科,
s.成绩
FROM students s
WHERE EXISTS (
SELECT 1 FROM students s2
WHERE s2.姓名 = s.姓名
GROUP BY s2.姓名
HAVING AVG(s2.成绩) > 100
);
3.2 物化视图实战
-- 创建学生成绩汇总物化视图
CREATE MATERIALIZED VIEW mv_student_analytics
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
ENABLE QUERY REWRITE
AS
SELECT
姓名,
性别,
学科,
COUNT(*) as 考试次数,
AVG(成绩) as 平均成绩,
MAX(成绩) as 最高成绩,
MIN(成绩) as 最低成绩,
STDDEV(成绩) as 成绩标准差,
ROUND(AVG(成绩) / MAX(AVG(成绩)) OVER (PARTITION BY 学科) * 100, 2) as 相对百分比,
RANK() OVER (PARTITION BY 学科 ORDER BY AVG(成绩) DESC) as 学科内平均分排名
FROM students
GROUP BY 姓名, 性别, 学科;
-- 创建物化视图日志
CREATE MATERIALIZED VIEW LOG ON students
WITH ROWID, SEQUENCE(学号, 姓名, 性别, 学科, 成绩)
INCLUDING NEW VALUES;
-- 自动查询重写示例
SELECT /*+ REWRITE_OR_ERROR */
姓名,
学科,
平均成绩,
成绩标准差
FROM mv_student_analytics
WHERE 学科 = '数学' AND 平均成绩 > 100;
-- 物化视图维护
BEGIN
DBMS_MVIEW.REFRESH('MV_STUDENT_ANALYTICS', 'C');
END;
/
-- 监控物化视图使用
SELECT
mview_name,
staleness,
last_refresh_date,
query
FROM user_mviews
WHERE mview_name = 'MV_STUDENT_ANALYTICS';
四、PL/SQL高级编程
4.1 面向对象编程实践
-- 创建对象类型
CREATE OR REPLACE TYPE student_obj AS OBJECT (
student_id NUMBER,
student_name VARCHAR2(20),
gender VARCHAR2(20),
subject_scores subject_score_list,
MEMBER FUNCTION get_average_score RETURN NUMBER,
MEMBER FUNCTION get_best_subject RETURN VARCHAR2,
MEMBER PROCEDURE display_info
);
/
CREATE OR REPLACE TYPE BODY student_obj AS
MEMBER FUNCTION get_average_score RETURN NUMBER IS
total NUMBER := 0;
count_subjects NUMBER := 0;
BEGIN
FOR i IN 1..subject_scores.COUNT LOOP
total := total + subject_scores(i).score;
count_subjects := count_subjects + 1;
END LOOP;
RETURN ROUND(total / count_subjects, 2);
END;
MEMBER FUNCTION get_best_subject RETURN VARCHAR2 IS
best_score NUMBER := 0;
best_subject VARCHAR2(20);
BEGIN
FOR i IN 1..subject_scores.COUNT LOOP
IF subject_scores(i).score > best_score THEN
best_score := subject_scores(i).score;
best_subject := subject_scores(i).subject;
END IF;
END LOOP;
RETURN best_subject;
END;
MEMBER PROCEDURE display_info IS
BEGIN
DBMS_OUTPUT.PUT_LINE('学生: ' || student_name);
DBMS_OUTPUT.PUT_LINE('平均分: ' || get_average_score);
DBMS_OUTPUT.PUT_LINE('优势学科: ' || get_best_subject);
END;
END;
/
-- 创建集合类型
CREATE OR REPLACE TYPE subject_score_obj AS OBJECT (
subject VARCHAR2(20),
score NUMBER
);
/
CREATE OR REPLACE TYPE subject_score_list AS TABLE OF subject_score_obj;
/
-- 使用对象类型
DECLARE
v_student student_obj;
v_scores subject_score_list := subject_score_list();
BEGIN
-- 构建学生成绩数据
SELECT subject_score_obj(学科, 成绩)
BULK COLLECT INTO v_scores
FROM students
WHERE 姓名 = '王小美';
-- 创建学生对象
v_student := student_obj(1, '王小美', '女', v_scores);
-- 调用对象方法
v_student.display_info;
DBMS_OUTPUT.PUT_LINE('平均分: ' || v_student.get_average_score);
DBMS_OUTPUT.PUT_LINE('最佳学科: ' || v_student.get_best_subject);
END;
/
4.2 高级错误处理与日志系统
-- 创建系统日志表
CREATE TABLE system_logs (
log_id NUMBER PRIMARY KEY,
log_time TIMESTAMP DEFAULT SYSTIMESTAMP,
log_level VARCHAR2(20),
module_name VARCHAR2(50),
message VARCHAR2(1000),
error_code NUMBER,
error_stack CLOB
);
CREATE SEQUENCE log_seq START WITH 1 INCREMENT BY 1;
-- 创建高级日志包
CREATE OR REPLACE PACKAGE advanced_logging_pkg AS
PROCEDURE log_info(p_module VARCHAR2, p_message VARCHAR2);
PROCEDURE log_error(p_module VARCHAR2, p_message VARCHAR2, p_error_code NUMBER DEFAULT NULL);
PROCEDURE log_debug(p_module VARCHAR2, p_message VARCHAR2);
FUNCTION get_recent_logs(p_minutes NUMBER DEFAULT 60) RETURN SYS_REFCURSOR;
END advanced_logging_pkg;
/
CREATE OR REPLACE PACKAGE BODY advanced_logging_pkg AS
PROCEDURE log_info(p_module VARCHAR2, p_message VARCHAR2) IS
BEGIN
INSERT INTO system_logs (log_id, log_level, module_name, message)
VALUES (log_seq.NEXTVAL, 'INFO', p_module, p_message);
COMMIT;
END;
PROCEDURE log_error(p_module VARCHAR2, p_message VARCHAR2, p_error_code NUMBER DEFAULT NULL) IS
BEGIN
INSERT INTO system_logs (log_id, log_level, module_name, message, error_code, error_stack)
VALUES (log_seq.NEXTVAL, 'ERROR', p_module, p_message, p_error_code, DBMS_UTILITY.FORMAT_ERROR_STACK);
COMMIT;
END;
PROCEDURE log_debug(p_module VARCHAR2, p_message VARCHAR2) IS
BEGIN
INSERT INTO system_logs (log_id, log_level, module_name, message)
VALUES (log_seq.NEXTVAL, 'DEBUG', p_module, p_message);
COMMIT;
END;
FUNCTION get_recent_logs(p_minutes NUMBER DEFAULT 60) RETURN SYS_REFCURSOR IS
v_cursor SYS_REFCURSOR;
BEGIN
OPEN v_cursor FOR
SELECT log_time, log_level, module_name, message, error_code
FROM system_logs
WHERE log_time >= SYSTIMESTAMP - (p_minutes/1440)
ORDER BY log_time DESC;
RETURN v_cursor;
END;
END advanced_logging_pkg;
/
-- 使用高级错误处理的存储过程
CREATE OR REPLACE PROCEDURE process_student_scores IS
CURSOR student_cursor IS
SELECT DISTINCT 姓名 FROM students;
v_total_processed NUMBER := 0;
v_errors_occurred NUMBER := 0;
BEGIN
advanced_logging_pkg.log_info('PROCESS_STUDENT_SCORES', '开始处理学生成绩数据');
FOR student_rec IN student_cursor LOOP
BEGIN
-- 模拟复杂处理逻辑
advanced_logging_pkg.log_debug('PROCESS_STUDENT', '处理学生: ' || student_rec.姓名);
-- 这里可以添加实际的业务逻辑
DBMS_LOCK.SLEEP(0.01); -- 模拟处理时间
v_total_processed := v_total_processed + 1;
-- 模拟随机错误(用于测试)
IF DBMS_RANDOM.VALUE < 0.1 THEN
RAISE_APPLICATION_ERROR(-20001, '模拟处理错误');
END IF;
EXCEPTION
WHEN OTHERS THEN
v_errors_occurred := v_errors_occurred + 1;
advanced_logging_pkg.log_error('PROCESS_STUDENT',
'处理学生 ' || student_rec.姓名 || ' 时发生错误', SQLCODE);
END;
END LOOP;
advanced_logging_pkg.log_info('PROCESS_STUDENT_SCORES',
'处理完成: 成功 ' || v_total_processed || ', 错误 ' || v_errors_occurred);
EXCEPTION
WHEN OTHERS THEN
advanced_logging_pkg.log_error('PROCESS_STUDENT_SCORES',
'处理过程中发生严重错误', SQLCODE);
RAISE;
END;
/
-- 测试错误处理
BEGIN
process_student_scores;
-- 查看日志
DECLARE
v_cursor SYS_REFCURSOR;
v_log_time TIMESTAMP;
v_log_level VARCHAR2(20);
v_module VARCHAR2(50);
v_message VARCHAR2(1000);
BEGIN
v_cursor := advanced_logging_pkg.get_recent_logs(10);
LOOP
FETCH v_cursor INTO v_log_time, v_log_level, v_module, v_message;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_log_level || ' | ' || v_module || ' | ' || v_message);
END LOOP;
CLOSE v_cursor;
END;
END;
/
五、高级事务与并发控制
5.1 分布式事务模拟
-- 创建模拟的分布式表
CREATE TABLE student_scores_local AS SELECT * FROM students;
CREATE TABLE student_scores_remote AS SELECT * FROM students WHERE 1=0;
-- 分布式事务管理
DECLARE
v_transaction_id VARCHAR2(100);
v_commit_count NUMBER := 0;
BEGIN
-- 开始分布式事务
v_transaction_id := DBMS_TRANSACTION.LOCAL_TRANSACTION_ID;
DBMS_OUTPUT.PUT_LINE('事务ID: ' || v_transaction_id);
-- 模拟分布式操作
SAVEPOINT before_distributed_ops;
-- 操作1:更新本地表
UPDATE student_scores_local
SET 成绩 = 成绩 + 5
WHERE 姓名 = '王小美';
-- 操作2:插入到远程表(模拟)
INSERT INTO student_scores_remote
SELECT * FROM student_scores_local
WHERE 姓名 = '王小美';
-- 两阶段提交准备
v_commit_count := SQL%ROWCOUNT;
-- 检查业务规则
IF v_commit_count > 0 THEN
-- 第一阶段:预提交
DBMS_OUTPUT.PUT_LINE('预提交检查通过');
-- 第二阶段:正式提交
COMMIT;
DBMS_OUTPUT.PUT_LINE('分布式事务提交成功');
ELSE
ROLLBACK TO before_distributed_ops;
DBMS_OUTPUT.PUT_LINE('事务回滚:无数据变更');
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('分布式事务失败: ' || SQLERRM);
END;
/
-- 高级锁管理
DECLARE
v_lock_handle VARCHAR2(128);
v_lock_result NUMBER;
BEGIN
-- 获取用户定义锁
v_lock_result := DBMS_LOCK.ALLOCATE_UNIQUE('STUDENT_SCORE_PROCESSING', v_lock_handle);
-- 请求排他锁(等待10秒)
v_lock_result := DBMS_LOCK.REQUEST(
lockhandle => v_lock_handle,
lockmode => DBMS_LOCK.X_MODE,
timeout => 10,
release_on_commit => TRUE
);
IF v_lock_result = 0 THEN
DBMS_OUTPUT.PUT_LINE('成功获取锁,开始处理数据...');
-- 执行需要锁保护的操作
UPDATE students SET 成绩 = 成绩 + 1 WHERE 学号 = 1;
COMMIT;
DBMS_OUTPUT.PUT_LINE('数据处理完成,锁已释放');
ELSE
DBMS_OUTPUT.PUT_LINE('获取锁失败,错误代码: ' || v_lock_result);
END IF;
END;
/
5.2 高级并发模式
-- 乐观并发控制实现
CREATE TABLE students_optimistic (
学号 NUMBER,
姓名 VARCHAR2(20),
性别 VARCHAR2(20),
学科编号 NUMBER,
学科 VARCHAR2(20),
成绩 NUMBER,
version_number NUMBER DEFAULT 1,
last_updated TIMESTAMP DEFAULT SYSTIMESTAMP,
last_updated_by VARCHAR2(50) DEFAULT USER
);
-- 插入测试数据
INSERT INTO students_optimistic
SELECT 学号, 姓名, 性别, 学科编号, 学科, 成绩, 1, SYSTIMESTAMP, USER
FROM students;
COMMIT;
-- 乐观锁更新函数
CREATE OR REPLACE FUNCTION update_student_optimistic(
p_student_id IN NUMBER,
p_subject IN VARCHAR2,
p_new_score IN NUMBER,
p_expected_version IN NUMBER
) RETURN VARCHAR2 AS
v_current_version NUMBER;
v_current_score NUMBER;
v_rows_updated NUMBER;
BEGIN
-- 检查版本号
SELECT version_number, 成绩
INTO v_current_version, v_current_score
FROM students_optimistic
WHERE 学号 = p_student_id AND 学科 = p_subject
FOR UPDATE; -- 行级锁
IF v_current_version != p_expected_version THEN
RETURN 'ERROR:数据已被其他用户修改,当前版本:' || v_current_version;
END IF;
-- 更新数据
UPDATE students_optimistic
SET 成绩 = p_new_score,
version_number = version_number + 1,
last_updated = SYSTIMESTAMP,
last_updated_by = USER
WHERE 学号 = p_student_id
AND 学科 = p_subject
AND version_number = p_expected_version;
v_rows_updated := SQL%ROWCOUNT;
IF v_rows_updated = 1 THEN
COMMIT;
RETURN 'SUCCESS:成绩从' || v_current_score || '更新为' || p_new_score;
ELSE
ROLLBACK;
RETURN 'ERROR:更新失败,可能版本号已变更';
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'ERROR:记录不存在';
WHEN OTHERS THEN
ROLLBACK;
RETURN 'ERROR:' || SQLERRM;
END;
/
-- 测试乐观锁
DECLARE
v_result VARCHAR2(200);
BEGIN
-- 第一次更新(应该成功)
v_result := update_student_optimistic(1, '语文', 118, 1);
DBMS_OUTPUT.PUT_LINE('第一次更新: ' || v_result);
-- 第二次更新(应该失败,因为版本号不匹配)
v_result := update_student_optimistic(1, '语文', 120, 1);
DBMS_OUTPUT.PUT_LINE('第二次更新: ' || v_result);
END;
/
-- 批量并发处理
CREATE OR REPLACE PROCEDURE process_students_concurrent IS
PRAGMA AUTONOMOUS_TRANSACTION;
CURSOR student_cursor IS
SELECT 学号, 姓名, 学科, 成绩, version_number
FROM students_optimistic
WHERE 成绩 < 100
FOR UPDATE SKIP LOCKED; -- 跳过已被锁定的行
v_processed NUMBER := 0;
v_updated NUMBER := 0;
BEGIN
FOR rec IN student_cursor LOOP
BEGIN
-- 模拟复杂处理
DBMS_LOCK.SLEEP(0.1);
-- 尝试更新
UPDATE students_optimistic
SET 成绩 = 成绩 + 10,
version_number = version_number + 1
WHERE 学号 = rec.学号
AND 学科 = rec.学科
AND version_number = rec.version_number;
v_processed := v_processed + 1;
IF SQL%ROWCOUNT = 1 THEN
v_updated := v_updated + 1;
END IF;
-- 每处理10条提交一次,减少锁持有时间
IF MOD(v_processed, 10) = 0 THEN
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('处理学生 ' || rec.姓名 || ' 时出错: ' || SQLERRM);
END;
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('并发处理完成: 处理' || v_processed || '条, 成功更新' || v_updated || '条');
END;
/
-- 在多个会话中并发执行测试
-- 会话1: BEGIN process_students_concurrent; END;
-- 会话2: BEGIN process_students_concurrent; END;
六、系统集成与自动化
6.1 数据导出与外部表
-- 创建目录对象
CREATE OR REPLACE DIRECTORY student_data_dir AS '/tmp/oracle_data';
GRANT READ, WRITE ON DIRECTORY student_data_dir TO PUBLIC;
-- 使用UTL_FILE包导出数据
DECLARE
v_file UTL_FILE.FILE_TYPE;
CURSOR student_cursor IS
SELECT 姓名, 学科, 成绩,
ROUND(AVG(成绩) OVER (PARTITION BY 姓名), 2) as 平均分
FROM students
ORDER BY 姓名, 学科;
BEGIN
-- 打开文件
v_file := UTL_FILE.FOPEN('STUDENT_DATA_DIR', 'student_scores_export.csv', 'W', 32767);
-- 写入表头
UTL_FILE.PUT_LINE(v_file, '姓名,学科,成绩,个人平均分,导出时间');
-- 写入数据
FOR rec IN student_cursor LOOP
UTL_FILE.PUT_LINE(v_file,
rec.姓名 || ',' ||
rec.学科 || ',' ||
rec.成绩 || ',' ||
rec.平均分 || ',' ||
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')
);
END LOOP;
-- 关闭文件
UTL_FILE.FCLOSE(v_file);
DBMS_OUTPUT.PUT_LINE('数据导出完成: student_scores_export.csv');
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(v_file) THEN
UTL_FILE.FCLOSE(v_file);
END IF;
RAISE;
END;
/
-- 创建外部表读取CSV文件
CREATE TABLE students_external (
姓名 VARCHAR2(20),
学科 VARCHAR2(20),
成绩 NUMBER,
个人平均分 NUMBER,
导出时间 VARCHAR2(20)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY student_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
SKIP 1 -- 跳过表头
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
(
姓名 CHAR(20),
学科 CHAR(20),
成绩 CHAR(10),
个人平均分 CHAR(10),
导出时间 CHAR(20)
)
)
LOCATION ('student_scores_export.csv')
)
REJECT LIMIT UNLIMITED;
-- 查询外部表
SELECT * FROM students_external;
6.2 自动化作业调度
-- 创建自动化作业
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'DAILY_STUDENT_ANALYSIS',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN
advanced_logging_pkg.log_info(''DAILY_JOB'', ''开始每日学生分析'');
-- 这里可以调用各种分析过程
process_student_scores;
advanced_logging_pkg.log_info(''DAILY_JOB'', ''每日学生分析完成'');
END;',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=0', -- 每天凌晨2点
enabled => TRUE,
comments => '每日学生成绩分析作业'
);
END;
/
-- 创建数据清理作业
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'WEEKLY_DATA_MAINTENANCE',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN
-- 清理30天前的日志
DELETE FROM system_logs
WHERE log_time < SYSTIMESTAMP - 30;
COMMIT;
-- 刷新物化视图
DBMS_MVIEW.REFRESH(''MV_STUDENT_ANALYTICS'', ''C'');
advanced_logging_pkg.log_info(''MAINTENANCE_JOB'', ''每周维护完成'');
END;',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=WEEKLY; BYDAY=SUN; BYHOUR=3', -- 每周日凌晨3点
enabled => TRUE,
comments => '每周数据维护作业'
);
END;
/
-- 监控作业运行状态
SELECT
job_name,
state,
last_start_date,
next_run_date,
run_count,
failure_count
FROM USER_SCHEDULER_JOBS
WHERE job_name LIKE '%STUDENT%' OR job_name LIKE '%MAINTENANCE%';
-- 创建监控视图
CREATE OR REPLACE VIEW v_system_monitoring AS
SELECT
'学生记录数' as metric_name,
TO_CHAR(COUNT(*)) as metric_value
FROM students
UNION ALL
SELECT
'平均成绩',
TO_CHAR(ROUND(AVG(成绩), 2))
FROM students
UNION ALL
SELECT
'系统日志数',
TO_CHAR(COUNT(*))
FROM system_logs
WHERE log_time >= SYSDATE - 1
UNION ALL
SELECT
'物化视图状态',
staleness
FROM user_mviews
WHERE mview_name = 'MV_STUDENT_ANALYTICS';
-- 查询系统监控
SELECT * FROM v_system_monitoring;
项目总结
本实战项目基于简单的students表,逐步构建了一个完整的学生成绩管理系统,涵盖了:
- 系统架构 – 维度建模和视图扩展
- 高级分析 – 多层次窗口函数和复杂统计
- 性能优化 – 执行计划分析和物化视图
- PL/SQL编程 – 面向对象和高级错误处理
- 并发控制 – 乐观锁和分布式事务
- 系统集成 – 数据导出和作业调度
这个项目展示了如何在有限的表结构基础上,通过Oracle数据库的高级特性构建出企业级的应用系统。