如何用PLSQL UTL_FILE包生成无重复数据的每日CSV文件
解决PL/SQL用UTL_FILE生成CSV时避免重复数据的问题
情况1:避免CSV文件内出现重复数据
如果是原表查询结果本身存在重复行,直接在游标对应的SQL中做去重处理即可,两种常用实现方式:
方式1:用DISTINCT过滤完全重复行
适合需要去掉所有完全相同的记录场景:
DECLARE v_file UTL_FILE.FILE_TYPE; CURSOR c_data IS SELECT DISTINCT col1, col2, col3 -- 替换为你的实际列名 FROM your_table; -- 替换为你的表名 v_col1 your_table.col1%TYPE; v_col2 your_table.col2%TYPE; v_col3 your_table.col3%TYPE; BEGIN v_file := UTL_FILE.FOPEN('YOUR_DIRECTORY', 'daily_data.csv', 'W', 32767); -- 写入CSV表头 UTL_FILE.PUT_LINE(v_file, '列1,列2,列3'); OPEN c_data; LOOP FETCH c_data INTO v_col1, v_col2, v_col3; EXIT WHEN c_data%NOTFOUND; -- 处理字段内的逗号转义,避免破坏CSV格式 UTL_FILE.PUT_LINE(v_file, REPLACE(v_col1, ',', '\,') || ',' || REPLACE(v_col2, ',', '\,') || ',' || REPLACE(v_col3, ',', '\,') ); END LOOP; CLOSE c_data; UTL_FILE.FCLOSE(v_file); EXCEPTION WHEN OTHERS THEN IF UTL_FILE.IS_OPEN(v_file) THEN UTL_FILE.FCLOSE(v_file); END IF; RAISE; END; /
方式2:用GROUP BY按指定列去重
适合需要基于特定列(比如主键、唯一标识)去重,同时聚合其他字段的场景:
CURSOR c_data IS SELECT col1, MAX(col2) AS col2, MAX(col3) AS col3 -- 根据业务需求选择聚合函数 FROM your_table GROUP BY col1; -- 按需要去重的核心列分组
情况2:避免每日生成的CSV包含历史已导出数据
如果需要每日仅导出新增数据,可通过标记字段或日志表实现:
方案1:给原表添加导出标记字段
第一步:添加字段(若原表无相关字段)
ALTER TABLE your_table ADD exported_date DATE;
第二步:导出未标记数据并更新状态
DECLARE v_file UTL_FILE.FILE_TYPE; CURSOR c_unexported IS SELECT id, col1, col2, col3 -- id为原表主键 FROM your_table WHERE exported_date IS NULL; v_id your_table.id%TYPE; v_col1 your_table.col1%TYPE; v_col2 your_table.col2%TYPE; v_col3 your_table.col3%TYPE; BEGIN v_file := UTL_FILE.FOPEN('YOUR_DIRECTORY', 'daily_new_data.csv', 'W', 32767); UTL_FILE.PUT_LINE(v_file, 'ID,列1,列2,列3'); OPEN c_unexported; LOOP FETCH c_unexported INTO v_id, v_col1, v_col2, v_col3; EXIT WHEN c_unexported%NOTFOUND; -- 写入数据行 UTL_FILE.PUT_LINE(v_file, v_id || ',' || REPLACE(v_col1, ',', '\,') || ',' || REPLACE(v_col2, ',', '\,') || ',' || REPLACE(v_col3, ',', '\,') ); -- 更新导出标记 UPDATE your_table SET exported_date = SYSDATE WHERE id = v_id; END LOOP; CLOSE c_unexported; COMMIT; UTL_FILE.FCLOSE(v_file); EXCEPTION WHEN OTHERS THEN IF UTL_FILE.IS_OPEN(v_file) THEN UTL_FILE.FCLOSE(v_file); END IF; ROLLBACK; RAISE; END; /
方案2:单独创建导出日志表(无法修改原表时使用)
第一步:创建日志表
CREATE TABLE export_log ( record_id NUMBER, -- 对应原表主键 export_date DATE DEFAULT SYSDATE );
第二步:导出未记录数据并写入日志
DECLARE v_file UTL_FILE.FILE_TYPE; CURSOR c_unexported IS SELECT t.id, t.col1, t.col2, t.col3 FROM your_table t LEFT JOIN export_log el ON t.id = el.record_id WHERE el.record_id IS NULL; v_id your_table.id%TYPE; v_col1 your_table.col1%TYPE; v_col2 your_table.col2%TYPE; v_col3 your_table.col3%TYPE; BEGIN v_file := UTL_FILE.FOPEN('YOUR_DIRECTORY', 'daily_new_data.csv', 'W', 32767); UTL_FILE.PUT_LINE(v_file, 'ID,列1,列2,列3'); OPEN c_unexported; LOOP FETCH c_unexported INTO v_id, v_col1, v_col2, v_col3; EXIT WHEN c_unexported%NOTFOUND; UTL_FILE.PUT_LINE(v_file, v_id || ',' || REPLACE(v_col1, ',', '\,') || ',' || REPLACE(v_col2, ',', '\,') || ',' || REPLACE(v_col3, ',', '\,') ); -- 写入导出日志 INSERT INTO export_log (record_id) VALUES (v_id); END LOOP; CLOSE c_unexported; COMMIT; UTL_FILE.FCLOSE(v_file); EXCEPTION WHEN OTHERS THEN IF UTL_FILE.IS_OPEN(v_file) THEN UTL_FILE.FCLOSE(v_file); END IF; ROLLBACK; RAISE; END; /
注意事项
- 确保
YOUR_DIRECTORY是已创建并授权的数据库目录,授权语句:GRANT READ, WRITE ON DIRECTORY YOUR_DIRECTORY TO your_user; - 若字段含双引号、换行符等特殊字符,需额外转义,比如用
REPLACE(v_col, '"', '""')处理双引号,符合CSV规范 - 数据量较大时,建议用
BULK COLLECT批量获取数据,提升处理效率
内容的提问来源于stack exchange,提问作者Utkarsh Ranjan
相关产品推荐
相关产品推荐

