• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    Oracle批量投入数据方法总结

    发布者: 土豆服务器 | 发布时间: 2025-6-18 09:24| 查看数: 119| 评论数: 0|帖子模式

    零. 待投入数据的表结构
    1. create table DB_USER."PERSON_TABLE" (
    2.   ID NUMBER not null
    3.   , NAME VARCHAR2(50)
    4.   , AGE NUMBER
    5.   , EMAIL VARCHAR2(100)
    6.   , CREATED_DATE DATE
    7. )
    复制代码


    一. INSERT INTO ... SELECT投入数据

    1. INSERT INTO ... SELECT
    复制代码
    的这种方式相当于把数据加载到内存中之后再插入数据库,只适合投入小规模的数据。

    1.1 普通的方式投入数据

    当数据量不是很多的时候,可以使用这种方式

    • 先从DUAL虚拟表中检索后造出指定条数的数据后,再插入到指定的表中。
    • 除了主键之类的关键字段之外,其余字段写固定值即可。
    1. INSERT INTO PERSON_TABLE
    2.         SELECT
    3.                 -- 因为该字段为字符串形式,所以使用TO_CHAR转换
    4.                 -- TO_CHAR(100000000 + LEVEL) || 'TEST_ID' AS id,
    5.                 LEVEL AS id,
    6.                 'Name_' || ROWNUM AS name,
    7.         TRUNC(DBMS_RANDOM.VALUE(18, 60)) AS age,
    8.         'user' || ROWNUM || '@example.com' AS email,
    9.         SYSDATE - DBMS_RANDOM.VALUE(0, 365) AS created_date
    10.         FROM
    11.                 DUAL
    12.         CONNECT BY LEVEL <= 1000000;
    复制代码
    1.2 并行插入(Parallel Insert)投入数据
    1. ALTER SESSION ENABLE PARALLEL DML;
    复制代码
    1. INSERT /*+ PARALLEL(PERSON_TABLE, 4) */ INTO PERSON_TABLE
    2. SELECT LEVEL AS id,
    3.        'Name_' || ROWNUM AS name,
    4.        TRUNC(DBMS_RANDOM.VALUE(18, 60)) AS age,
    5.        'user' || ROWNUM || '@example.com' AS email,
    6.        SYSDATE - DBMS_RANDOM.VALUE(0, 365) AS created_date
    7. FROM DUAL
    8. CONNECT BY LEVEL <= 1000000;
    复制代码
    二. PL/SQL 循环投入数据


    2.1 脚本介绍


    • 灵活,支持动态生成数据,适合
      1. 中小数据量
      复制代码

    • 数据量大时性能较差,容易导致
      1. 上下文切换开销
      复制代码

    1. BEGIN
    2.     FOR i IN 1..5000000 LOOP
    3.         INSERT INTO PERSON_TABLE (id, name, age, email, created_date)
    4.         VALUES (
    5.             i,
    6.             'Name_' || i,
    7.             -- 随机年龄
    8.             TRUNC(DBMS_RANDOM.VALUE(18, 60)),
    9.             'user' || i || '@example.com',
    10.             -- 随机日期
    11.             SYSDATE - DBMS_RANDOM.VALUE(0, 365)
    12.         );

    13.         -- 每 100000 条提交一次
    14.         IF MOD(i, 100000) = 0 THEN
    15.             COMMIT;
    16.         END IF;
    17.         
    18.     END LOOP;
    19.    
    20.     COMMIT;
    21. END;
    22. /
    复制代码
    2.2 效果

    投入500万条数据,耗时5分钟。


    三. PL/SQL FORALL 批量操作


    3.1 脚本介绍


    • 这种方式可以
      1. 减少上下文切换
      复制代码
      ,性能比普通的循环插入要好。
    1. DECLARE
    2.     TYPE person_array IS TABLE OF PERSON_TABLE%ROWTYPE;
    3.     v_data person_array := person_array();
    4. BEGIN
    5.     FOR i IN 1..5000000 LOOP
    6.         v_data.EXTEND;
    7.         v_data(v_data.COUNT).id := i;
    8.         v_data(v_data.COUNT).name := 'Name_' || i;
    9.         v_data(v_data.COUNT).age := TRUNC(DBMS_RANDOM.VALUE(18, 60));
    10.         v_data(v_data.COUNT).email := 'user' || i || '@example.com';
    11.         v_data(v_data.COUNT).created_date := SYSDATE - DBMS_RANDOM.VALUE(0, 365);

    12.         -- 每 100000 条批量插入一次
    13.         IF MOD(i, 100000) = 0 THEN
    14.             FORALL j IN 1..v_data.COUNT
    15.                 INSERT INTO PERSON_TABLE VALUES v_data(j);
    16.             COMMIT;
    17.             v_data.DELETE; -- 清空数组
    18.         END IF;
    19.     END LOOP;

    20.     -- 插入剩余数据
    21.     FORALL j IN 1..v_data.COUNT
    22.         INSERT INTO PERSON_TABLE VALUES v_data(j);
    23.     COMMIT;
    24. END;
    25. /
    复制代码
    3.2 效果

    投入500万条数据,耗时1分钟18秒。


    四. SQL*Loader 工具加载外部文件

    写一个PowerShell脚本,根据数据库的表结构来生成csv文件

    • 该脚本执行后,会在桌面上生成一个csv文件。
    1. # 文件名称
    2. $file_name = 'person_data.csv'
    3. # 路径
    4. $outputFile = "$Home\Desktop\$file_name"
    5. # csv 文件的总行数
    6. $rows = 5000000
    7. # 并行线程数
    8. $threadCount = 4
    9. # 每个线程生成的记录数量
    10. $chunkSize = [math]::Ceiling($rows / $threadCount)

    11. # 判断文件是否存在,存在的话就删除
    12. if (Test-Path -Path $outputFile) {
    13.     Remove-Item -Path $outputFile -Force
    14. }

    15. # 写入 CSV 表头
    16. # "`"ID`",`"NAME`",`"AGE`",`"EMAIL`",`"CREATED_DATE`"" | Out-File -FilePath $outputFile -Encoding UTF8 -Append

    17. # 定义脚本块
    18. $scriptblock = {
    19.    
    20.     param($startRow, $endRow, $tempFile)

    21.     # 在后台作业中定义 Generate-Chunk 函数
    22.     function Generate-Chunk {
    23.         
    24.         param (
    25.             [int]$startRow,
    26.             [int]$endRow,
    27.             [string]$filePath
    28.         )

    29.         $random = [System.Random]::new()
    30.         $currentDate = Get-Date
    31.         $sb = [System.Text.StringBuilder]::new()

    32.                 # 循环生成csv数据
    33.         for ($i = $startRow; $i -le $endRow; $i++) {
    34.                        
    35.                         # =========================对应数据库的各字段值=========================
    36.             $id = $i
    37.             $name = "Name_$i"
    38.             $age = $random.Next(18, 60)
    39.             $email = "user$i@example.com"
    40.             $createdDate = $currentDate.AddDays(- $random.Next(0, 365)).ToString("yyyy/MM/dd HH:mm:ss")
    41.             # =========================对应数据库的各字段值=========================
    42.                        
    43.                         # =========================一行csv=========================
    44.             $line = "`"$id`",`"$name`",`"$age`",`"$email`",`"$createdDate`""
    45.             # =========================一行csv=========================

    46.             $sb.AppendLine($line) | Out-Null
    47.         }

    48.         <#
    49.             将生成的内容写入文件
    50.             -NoNewline 的作用是为了防止csv文件的最后一行被追加空行
    51.         #>
    52.         $sb.ToString() | Out-File -FilePath $filePath -Encoding UTF8 -Append -NoNewline
    53.     }

    54.     # 调用 Generate-Chunk 函数,多线程生成临时csv文件
    55.     Generate-Chunk -startRow $startRow -endRow $endRow -filePath $tempFile
    56. }

    57. # CSV文件合成
    58. function Merge-CSV {
    59.         
    60.         param (
    61.                 [string]$outputFile,
    62.                 [bool]$IsReadAllDataToMemory
    63.         )
    64.        
    65.         # 获取所有分段文件,按名称排序
    66.     $partFiles = Get-ChildItem -Path "$outputFile.*.part" | Sort-Object Name
    67.        
    68.         if ($IsReadAllDataToMemory) {
    69.                
    70.                 # 将所有内容加载到内存中,然后一次性写入
    71.                 $partFiles | ForEach-Object { Get-Content $_.FullName } | Out-File -FilePath $outputFile -Encoding UTF8 -Force
    72.                 # 删除所有分段文件
    73.                 $partFiles | ForEach-Object { Remove-Item $_.FullName }
    74.                
    75.                 return;
    76.         }
    77.        
    78.         $partFiles | ForEach-Object {
    79.         Get-Content -Path $_.FullName | Out-File -FilePath $outputFile -Encoding UTF8 -Append
    80.         Remove-Item -Path $_.FullName
    81.     }
    82. }
    83.    
    84. try {
    85.         # 定义job数组
    86.     $jobs = @()
    87.        
    88.         # 组装job
    89.     1..$threadCount | ForEach-Object {
    90.                
    91.         $startRow = ($_ - 1) * $chunkSize + 1
    92.         $endRow = [math]::Min($_ * $chunkSize, $rows)
    93.                
    94.                 # 临时csv文件
    95.         $tempFile = "$outputFile.$_.part"

    96.         $jobs += Start-Job -ScriptBlock $scriptblock -ArgumentList $startRow, $endRow, $tempFile
    97.     }
    98.        
    99.         # 统计生成csv文件所消耗的时间
    100.     $exec_time = Measure-Command {

    101.         Write-Host "临时csv文件开始生成..."

    102.         # 执行job,等待并收集所有执行结果
    103.         $jobs | ForEach-Object { Wait-Job -Job $_; Receive-Job -Job $_; Remove-Job -Job $_ }

    104.         # 合并所有并发生成的csv临时文件,组装成最终的总csv文件
    105.         Write-Host "临时csv文件生成完毕,开启合并..."
    106.         Merge-CSV -outputFile $outputFile -IsReadAllDataToMemory $False
    107.     }

    108.     Write-Host "csv文件生成完毕,共消耗$($exec_time.TotalSeconds)秒: $outputFile" -ForegroundColor Red
    109.        
    110. } catch {

    111.     # 当异常发生时,清空桌面上的临时csv文件
    112.     if (Test-Path -Path "$outputFile.*.part") {
    113.         Remove-Item -Path "$outputFile.*.part" -Force
    114.     }

    115.     Write-Host "脚本运行时发生异常: $_" -ForegroundColor Red
    116.     Write-Host "详细信息: $($_.Exception.Message)" -ForegroundColor Yellow
    117.     Write-Host "堆栈跟踪: $($_.Exception.StackTrace)" -ForegroundColor Gray
    118. }

    119. Read-Host "按 Enter 键退出..."
    复制代码
    创建控制文件
    1. control_file.ctl
    复制代码
    1. LOAD DATA
    2. INFILE 'person_data.csv'
    3. INTO TABLE PERSON_TABLE
    4. FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    5. (id, name, age, email, created_date "TO_DATE(:created_date, 'YYYY/MM/DD HH24:MI:SS')")
    复制代码
    使用
    1. SQL*Loader
    复制代码
    执行加载

    • 性能极高,适合大规模数据插入。
    • 支持多线程和并行加载。
    1. sqlldr db_user/oracle@SERVICE_XEPDB1_CLIENT control=control_file.ctl direct=true
    复制代码
    4.1 效果

    投入500万条数据,耗时居然不到10秒!

    到此这篇关于Oracle批量投入数据方法总结的文章就介绍到这了,更多相关Oracle投入数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:https://www.jb51.net/database/33418903j.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表