首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Oracle 10:使用HEXTORAW填充blob数据

Oracle 10:使用HEXTORAW填充blob数据
EN

Stack Overflow用户
提问于 2013-08-08 01:41:42
回答 5查看 14.3K关注 0票数 7

我们在Oracle中有一个表,其中有一个BLOB列,需要填充少量的任意字节数据--我们永远不会输入超过4000字节的数据。

我正在使用现有的基于C++ OCI的基础设施,这使得在特定的上下文中使用绑定变量非常困难,所以我需要只使用一个简单的查询来填充这个BLOB列。(我们正在努力使其现代化,但这不是今天的选择。)

我们在这样的查询中取得了一些进展:

代码语言:javascript
复制
UPDATE MyTable
   SET blobData = HEXTORAW('0EC1D7FA6B411DA5814...lots of hex data...0EC1D7FA6B411DA5814')
 WHERE ID = 123;

起初,这件事做得很好。然而,最近我们遇到了一个情况,我们需要输入超过2000字节的数据。此时,我们遇到了一个Oracle,ORA-01704: string literal too long,因为传递给HEXTORAW的字符串超过了4000个字符。我试着拆分字符串,然后与||连接,但这并没有避免错误。

因此,我需要一种方法来更新这个列,并使用一个简单的查询来填充超过2000字节的数据。有可能吗?

(我知道,如果我有绑定变量,这将是微不足道的--事实上,其他与这个表交互的应用程序使用了精确的技术--但不幸的是,我无法在这里重构DB内核。)只需将数据放入表中即可。)

编辑:

一个很有希望的方法没有奏效,那就是连接RAWs:

代码语言:javascript
复制
UTL_RAW.CONCAT(HEXTORAW('...'), HEXTORAW('...'), HEXTORAW('...'))

这避开了字符串长度限制,但似乎甲骨文对RAW长度也有匹配的内部2000字节限制。所以我不能用RAW填充blob。也许有一个函数将多个RAW连接到一个BLOB中。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-08-08 16:51:36

显然,如果使用PL/SQL,则可以超出这些限制。如果您直接在HEXTORAW语句中执行UPDATE语句,它也不起作用--它需要在单独的语句中完成,如下所示:

代码语言:javascript
复制
DECLARE
  buf RAW(4000); 
BEGIN
  buf := HEXTORAW('C2B97041074...lots of hex...0CC00CD00');
  UPDATE MyTable
     SET blobData = buf
   WHERE ID = 462;
END;

在我的一生中,我永远不会理解Oracle的一些局限性。好像每件事都有它自己的小特例。

票数 5
EN

Stack Overflow用户

发布于 2016-01-19 11:56:54

要更新大于16383字节的BLOB,可以使用这样的方法(每行的十六进制数为偶数,最多可达32766):

代码语言:javascript
复制
DECLARE
  buf BLOB; 
BEGIN
  dbms_lob.createtemporary(buf, FALSE);
  dbms_lob.append(buf, HEXTORAW('0EC1D7FA6B411DA58149'));
  --...lots of hex data...
  dbms_lob.append(buf, HEXTORAW('0EC1D7FA6B411DA58149'));
  UPDATE MyTable
     SET blobData = buf
   WHERE ID = 123;
END;

现在的限制仅仅是语句的大小,这可能是由操作环境(例如SQLPlus、Pro*C、VB、JDBC.)施加的。对于非常大的语句,PL/SQL可能也会失败,出现“出戴安娜节点”错误。

票数 6
EN

Stack Overflow用户

发布于 2016-12-10 19:53:01

这是基于mik的回答,但我在他的答案中发现了一个漏洞,在每个字符串的开头添加了多行十六进制字符,当您在每个附加行中使用HEXTORAW时。当你把这个十六进制从数据库中拉回来,并将它与你认为你正在投入的东西进行比较时,你就会看到这个。如果十六进制是图像,并且将这些图像字节绑定到Image.Source,则如果只有一条附加行,则忽略零,但如果有多行,则会为每个块引入这个额外的字节,从而损坏数据,并且无法显示图像。我想,同样的情况也发生在您想要上传的常规文件和其他数据上。

相反,我将所有的十六进制附加到CLOB中,CLOB将其保持为一个十六进制字符串,并具有与BLOB字段相同的4GB限制。因此,只有这个未损坏的字符串才是当十六进制字符串大于RAW时写入BLOB的内容。

代码语言:javascript
复制
DECLARE
  buf BLOB; 
  cBuf CLOB;
BEGIN
  dbms_lob.createtemporary(buf, FALSE);
  dbms_lob.createtemporary(cBuf, FALSE);
  dbms_lob.append(cBuf, '0EC1D7FA6B411DA5814');
  --...lots of hex data...
  dbms_lob.append(cBuf, '0EC1D7FA6B411DA5814');
  -- now we append the CLOB of hex to the BLOB as RAW
  dbms_lob.append(buf, HEXTORAW(cBuf));
  UPDATE MyTable
     SET blobData = buf
     WHERE ID = 123;
END;

在我的场景中,我基本上使用SQLite作为备份数据库,但我仍然需要一种方法来保持Oracle (我的主数据库)在上传文档时保持同步,并且可以重新建立到它的连接。

作为如何以编程方式构建这个SQL的更完整的答案,我想我应该展示这一点,因为我是用我的应用程序这样做的。我的C#应用程序中的代码会将文件的字节放入十六进制中,然后我将使用上面的SQL创建一个字符串变量,并将其写入文件,然后当连接返回时,一个服务将使用该变量更新Oracle。这就是我如何将我的十六进制输入到这个SQL字符串和文件(以及稍后的Oracle)中的方式:

代码语言:javascript
复制
// This is all staged so someone can see how you might go from file
// to bytes to hex
string filePath = txtFilePath.Text; // example of getting file path after
    // OpenFileDialog places ofd.FileName in a textbox called txtFilePath
byte[] byteArray = File.ReadAllBytes(filePath);
string hexString = getHexFromBytes(byteArray); // Google: bytes to hex

// Here is the meat...
if (hexString.Length > 0)
{
    string sqlForOracle = "DECLARE buf BLOB; " + 
        "cBuf CLOB; " +
        "BEGIN " + 
            "dbms_lob.createtemporary(buf, FALSE); " + 
            "dbms_lob.createtemporary(cBuf, FALSE); "; + 
            "dbms_lob.open(buf, dbms_lob.lob_readwrite); ";

    int chunkSize = 32766;
    if (hexString.Length > chunkSize)
    {
        sqlForOracle += "dbms_lob.open(cBuf, dbms_lob.lob_readwrite); ";

        int startIdx = 0;
        decimal hexChunks = decimal.Divide(hexString.Length / chunkSize);
        for (int i = 0; i < hexChunks; i++)
        {
            int remainingHex = hexString.Length - (i * chunkSize);
            if (remainingHex > chunkSize)
                sqlForOracle += "dbms_lob.append(cBuf, '" + hexString.Substring(startIdx, chunkSize + "'); ";
            else
                sqlForOracle += "dbms_lob.append(cBuf, '" + hexString.Substring(startIdx, remainingHex) + "'); ";

            startIdx = startIdx + chunkSize;
        }

        sqlForOracle += "dbms_lob.close(cBuf); ";

        // Now we append the CLOB to the BLOB
        sqlForOracle += "dbms_lob.append(buf, HEXTORAW(cBuf)); ";
    }
    else  // write it straight to BLOB as we are below our chunk limit
        sqlForOracle += "dbms_lob.append(buf, HEXTORAW('" + hexString + "')); ";

    sqlForOracle += "dbms_lob.close(buf); ";
    sqlForOracle += "UPDATE MyTable SET blobDate = buf WHERE ID = 123; END;";
}

sqlForOracle稍后使用FileStreamStreamWriter写入文件,服务查看文件是否存在、读取文件并使用它更新Oracle。

更新

Mik的回答其实很好,因为如果你在你的块中使用偶数,那么如果你不需要使用奇数块的话,我的答案实际上不必要地增加了一个步骤。因此,一个更大的文件(它必须与您的RAM相媲美)在转换之前被写入内存两次(CLOB,然后是BLOB),将不必要地影响性能,所以请注意,但是我确实想在C#中展示块是如何被分解的,以及如何编程地编写SQL。如果您只想使用buf,只需将所有cBuf变量替换为buf,除了只需要一条dbms_lob.createtemporary()语句,而且显然只需要一组.open().close()标记。

因此,关于这些标记,我还读到了AskTom在Oracle.com上的论坛,他们说在lob附加上执行dbms_lob.open().close()是更好的选择,而当处理> 2000 (或2000 * 32766 = 65.532 MB)的附加数时,性能会更好,这几乎是完成时间(178.19%)的两倍,而且还会变得更糟:当然,这取决于所处理的文件大小是否对您有用。我把它们加在上面。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18116634

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档