下载和处理要在Oracle中加载的超大型压缩MS Access文件的最佳方法

时间:2013-10-15 21:39:40

标签: c# java asp.net oracle oracle10g

我必须下载通过FTP加密的250mb~ZIP文件密码。下载后我必须用一个通用密码解压缩,Zip文件将包含一个1.5GB的MS Access数据库,我必须读取它并与我的数据库Oracle中的某些表进行一些连接,并将该数据转换并加载到该Oracle数据库中。

我正在寻找执行此过程的最佳方式。 我是ac#developer,所以我的第一个想法是使用c#,通过FtpClientFtpWebRequest下载文件,然后使用像DotNetZip这样的zip库并通过以下方式打开MS Access数据库ODBC并使用ODP将记录加载到Oracle.NEt,我认为这是我的“简单方法”,因为我知道该怎么做。

但是,由于这是一个大文件,我知道这可能需要很长时间,我关注时间和效率以及如何减少这个过程的时间。

所以我认为将所有请求直接处理到oracle(从那里下载FTP,在那里解压缩,直接处理信息会减少将记录从c#传递到oracle的时间减少)应该减少这个过程的时间,但我不确定这是否是正确的方法。

所以我开始研究来自oracle的图书馆,它可以做我想要做的事情,我找到了PLSQL-utils,看起来他们可以做我需要的一切,除了阅读MS Access数据库,我开始看着那个,找到了Heterogeneous Services,但我从未使用过它们,所以我对此一点也不知所措。

我曾经听说过我可以直接从Oracle使用Java,我知道java可以通过JDBC连接到MS Access。所以我搜索了一下,发现了Calling Java Methods in Oracle Database

的内容

这就是我到目前为止所做的,但我不知道应该使用哪种方法,我的意思是,据我所知,RDBMS用于处理数据,但不用于编写下载文件之类的东西,这就是为什么我们有OOP的语言。

作为附加信息,此过程将在一个月内执行一次或两次,因此我必须安排它,如果它在oracle中,可以使用计划作业轻松完成,或在c#中使用计划任务或Windows服务(这些是我所知道的工具)

我有一些限制

  • 我的客户端没有MS SQL Server,也没有可以为它购买许可证(所以我不能在这个过程中使用DTSX)。
  • 在Oracle生产服务器中可能我没有足够的权限来做所有事情,但如果他们是最适合这个过程的我可以遵守
  • 如果需要后端服务器(Java,c#托管在IIS或WebLogic或JBoss或anykind上),此服务器和Oracle服务器将是不同的
  • Unix服务器上托管的Oracle数据库

说到这一切,如果我使用.net并在Oracle DataBase中按记录加载记录,我怎样才能有效地完成所有这些过程?我应该在甲骨文中做些什么吗?或者这不是吗?有更好的方法吗?

7 个答案:

答案 0 :(得分:2)

我认为你正在使用C#控制台应用程序走上正轨,使其成为一个可重复的过程。 Here是一个很棒的免费库,我在很多项目中用过zip。

using (var client = new WebClient())
using (var stream = client.OpenRead(@"ftp://mysite.com/mydb.zip"))
using (var file = File.Create(@"c:\temp\mydb.zip"))
{
    stream.CopyTo(@"c:\temp\mydb.zip", 32000);
}

using (ZipFile zip = ZipFile.Read(@"c:\temp\mydb.zip"))
{
    ZipEntry e = zip["bigdb.mdb"];
    e.Password = "yourpassword";
    e.Extract("c:\temp\bigdb.mdb");
}

解压缩后,您可以创建与访问数据库和datareader对象的数据连接。然后使用dbreader读取行并写入平面文件(避免使用大型数据集导致内存不足)。

private constr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=yourdbfile.mdb;Jet OLEDB:Database Password=yourpassword;";
OleDbConnection conn = new OleDbConnection(constr);
string query = "SELECT * FROM [YourTable]";

OleDbCommand cmd = new OleDbCommand(query, conn);
OleDbDataReader reader = cmd.ExecuteReader();
int rowNum = 0;
StringBuilder sb = new StringBuilder(); 
while (reader.Read())
{
   // write rows to flat file in chunks of 10K rows.
   sb.Append(reader["FieldA"].ToString() + "|");
   sb.Append(reader["FieldB"].ToString() + "|");
   sb.Append(reader["FieldC"].ToString() + System.Environment.NewLine);

   if (rowNum % 10000 == 0)
   {
        File.AppendText(@"c:\temp\data.psv", sb.ToString());
        sb = new StringBuilder(); 
   }
   rowNum++;
}
File.AppendText(@"c:\temp\data.psv", sb.ToString());
reader.Close();

填好数据表后,可以将其导出为平面文件。 我不建议逐行插入数据,这将非常慢,并且会使您的Oracle数据库事务日志膨胀。我不相信Oracle 10g有一个支持批量加载的.Net驱动程序,所以您可能需要通过平面文件批量加载。

接下来,通过命令行you can invoke this from your C# console app导入Oracle。在执行此操作之前,您需要先创建一个control file, ctl.ldr,然后由Oracle用于批量加载操作。

options (skip=1)
load data
 INFILE 'c:\temp\data.psv'
 INTO table tblTest
 APPEND
 FIELDS TERMINATED BY "|" optionally enclosed by '"'      
 ( fielda,fieldb,etc...)

and then 
run it in as follows via command line

sqlldr username/pswd@oracle_sid control=ctl.ldr

希望这有帮助,祝你好运!

<强> [编辑]

您可能还要查看.Net Oracle Bulk copy class。它随Oracle 11g 客户端驱动程序一起提供。也许它仍然会对你的10g服务器起作用。一个潜在的问题是,同一应用服务器上的所有其他应用程序也需要使用这些较新的11g客户端驱动程序。另一种选择是构建一个使用支持批量加载的Jena framework的Java应用程序。

答案 1 :(得分:2)

结帐Pentaho。这基本上是一个类似Java的开源SSIS产品。下载并解压缩Access数据库后,此工具应完成其余工作以将数据加载到Oracle中。我以前在MySQL上使用它并且它工作得非常好,它现在有一个bulk Oracle db load component。 Pentaho也不需要在你的数据库服务器上运行,所以这可能是你想要的银弹。权衡的是,会有一些加速时间。

假设您的Oracle服务器正在运行Windows Server,并且您可以将Access文件放在该服务器上本地的某个位置或服务器可以读取它的位置,另一个可能的选项,您可以执行以下操作。

  1. 创建一个C#应用程序,以便将Access数据库下载,解压缩并复制到Oracle可访问的位置。
  2. 在数据库服务器上创建本地系统DSN(ODBC条目)到Access数据库。
  3. 配置并启动Oracle Listener,HS和连接设置。
  4. 添加数据库链接。
  5. 加入链接数据库和查询。
  6. This article概述了配置Oracle数据库链接以进行访问的整个过程。

答案 2 :(得分:1)

我将创建一个可以调用的应用程序或网页或WCF服务,它将下载文件,解压缩并将记录直接插入Oracle中。我会尝试尽可能多地保存Oracle中的所有数据。得到它并保存。一旦下载,我就不会乱用zip文件了。

基本上:

数据 - &gt;处理器应用程序 - &gt; Oracle

这样可以实现自动化。我会用C#做处理器。

答案 3 :(得分:1)

正如我在评论中提到的那样,可以从oracle创建另一个数据库的DBLink。所以我会创建一个应用程序来下载文件并将其解压缩到特定区域(oracle服务器可以读取的区域)。 然后我会按照这个指示设置一个DBLink到这个文件 https://forums.oracle.com/thread/407779或在asktom中 http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:4406709207206

然后,直接从读取msAccess db的oracle导入数据。

答案 4 :(得分:1)

我有两个 Powershell脚本,第一个帮我从中国下载 ftp 文件:

下载FTP脚本

# Parametros
param([string]$url, [string]$user, [string]$pass, [string]$folder, [string]$fecpro)


if (!$fecpro)
{
    $date = Get-Date
    $date = $date.AddDays(-1)
    $fecpro = '{0:yyyyMMdd}' -f $date
}

$FtpServer     = "ftp://$url/$fecpro"
$Username      = $user
$Password      = $pass
$RemoteFolder  = $FtpServer
if (!$folder)
{
    $LocalFolder   = $pwd.path + "\"
} 
else 
{
    $LocalFolder   = $folder + "\"
    if (-not(Test-Path -Path $LocalFolder))
    {
        New-Item -ItemType Directory -Force -Path $LocalFolder
    }
}
Write-Host "Searching *.zip files from $RemoteFolder..."

$existfiles = gci $LocalFolder | Where-Object { $_.name -like "*.zip" }

$cntFile = 0
$totFiles = 1
$listFiles = @(0)*$totfiles
$listFiles[1] = "cs2-" + $fecpro + "-6004-0000-1.zip"

$existfiles = gci $LocalFolder | Where-Object { $_.name -like "*.zip" }

do
{
    try
    {
        $f_exist = $false
        $cntFile = $cntFile + 1
        if ($existfiles) {
            foreach($file in $existfiles) {
                $tmpname = $file.name.ToString()
                $bool = $listFiles[$cntFile].CompareTo($file.name)                              
                if ($bool -eq 0) {
                    $f_exist = ($f_exist -or $true)
                } else {
                    $f_exist = ($f_exist -or $false)
                }
            }
        }

        if (!$f_exist -and $rescnt['CNT'] -eq 0) {
            $ftpfile = $RemoteFolder + "/" + $listFiles[$cntFile]
            $FTPRequest1 = [System.Net.FtpWebRequest]::Create($ftpfile)
            $FTPRequest1.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
            $FTPRequest1.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
            $FTPRequest1.UsePassive = $true
            $FTPRequest1.UseBinary = $true
            $FTPRequest1.KeepAlive = $false
            $destfile = $LocalFolder + $listFiles[$cntFile]
            $file = New-Object IO.FileStream ($destfile, [IO.FileMode]::Create)
            $FTPResponse = $FTPRequest1.GetResponse()
            $ResponseStream = $FTPResponse.GetResponseStream()
            [byte[]] $buffer = New-Object byte[] 64
            $FTPReader = $ResponseStream.Read($buffer, 0, 64)
            while($FTPReader -ne 0){
                $FTPReader = $ResponseStream.Read($buffer,0, 64)
                $file.Write($buffer,0,$FTPReader)
            }
            Write-Host "File $ftpfile is downloaded in local folder."       
            $file.Close()               
            $FTPReader.Close()
            $FTPResponse.Close()
            $ResponseStream.Close()

        } 
        else {
                Write-Host "The file $name already exists in local folder."
        }
    }
    catch
    {
        if ($file) {
            $file.Close()
        }

    }
}
while ($cntFile -lt $totFiles+1) 

我的第二个脚本使用 Oracle客户端附带的unzip.exe解压缩文件。 (%ORACLE_HOME%\产品\ 10.2.0 \的Client_1 \ BIN)

解压缩文件脚本

param([string]$folder_origen, [string]$folder_destino, [string]$fecpro)

$P_FOLDER = $folder_origen + "\"
$P_DESTINO = $folder_destino + "\"

if (!$fecpro)
{
    $date = Get-Date
    $date = $date.AddDays(-1)
    $fecpro = '{0:yyyyMMdd}' -f $date
}

Write-Host "Searching *.zip files in $P_FOLDER"

# Inicio del programa
$LocalFolder = $P_FOLDER + "*$fecpro*.zip"
$filelocation = gci $LocalFolder
foreach ($file in $filelocation){
    if (($file) -and ($file.length -gt 0kb))
    {
        $shell = new-object -com shell.application
        $filename = $file.name.ToString()
        $split = $filename.Split(".")
        $dest = $P_DESTINO + $split[0]
        If (!(Test-Path $dest))
        { 
            New-Item $dest -Type Directory
        }
        $orifile = $P_FOLDER + $filename
        $time = "{0:hh_mm_ss.ffff}" -f (get-date)
        $prefile = "d." + (get-date).ToShortDateString() + ".t." + $time
        $prefile = $prefile -replace "/", "_"
        $filename = $prefile + ".f." + $filename
        $P_DESTZIP = $P_FOLDER + "proc" + "\"
        If (!(Test-Path $P_DESTZIP))
        { 
            New-Item $P_DESTZIP -Type Directory
        }
        #Copy-Item $orifile "$P_DESTZIP$prefile_$filename"
        $zipname = $orifile
        #"$P_DESTZIP$prefile_$filename"
        $zip = $shell.NameSpace($zipname)
        $destn = $shell.Namespace($dest)

        unzip -o -qq $zipname "*$fecpro*afsbjdtl.*" "*$fecpro*afgnled.bin" -d $dest

        Write-Host "$orifile was renamed and unzipped."
    } else
    {
        Write-Host "Zip files not found in the folder."
    }
}

当我可以使用文件的大小名称这样的过滤器时,我搜索文件以模式进行提取扩展,而且速度非常快。

最后关于加载我也会获取命令 LOAD DATA 。它具有最佳性能。

答案 5 :(得分:1)

我会说你应该更好地使用你最熟悉的工具。 可以在java中执行相同的操作,然后将其加载到数据库中或作为单独的应用程序运行,但为什么要这么麻烦? 我相信你知道用C#下载/解压缩/打开访问数据库的有效方法。

因此,只缺少一件事,如何有效地将数据从访问数据库上传到oracle数据库。

简短回答是 - bulk inserts,可能与临时表的结合以及存储过程的一些帮助以进行进一步的批量处理。通过数据库/表自定义调整可以实现更快的速度,以更好地适应您的数据。

你也可以在java中做同样的事情,但我怀疑它会更快。与缓冲数据和批量加载相关的正确工作比任何其他因素都重要得多。之后,抓住一些DBA和bug来帮助您进行数据库调优。详情超出了这个问题的范围。

答案 6 :(得分:1)

打扰一下,如果我说的是显而易见的,但如果你的主要性能问题是msaccess.db的ftp / sftp传输速度,那么让访问db定期导出表/记录的子集可能是有意义的您实际需要访问权限,以便您对Oracle进行必要的更新。

几年前,我发现使用“ArrayDML”向Oracle添加记录的速度是一次添加单个记录的速度的10倍。 (不确定odp.net是否支持...)