복붙노트

[SQL] 오라클 10 : BLOB 데이터에 채우기에 HEXTORAW 사용

SQL

오라클 10 : BLOB 데이터에 채우기에 HEXTORAW 사용

우리는 요구가 임의의 바이트 적은 양의 데이터로 채워질 수있는 BLOB 열이 오라클의 테이블을 - 우리는 데이터의 4000 바이트에 넣어하지 않습니다.

나는 단지 간단한 쿼리를 사용하여이 BLOB 열을 채울 필요가 그래서 매우 어려운 특정 상황에서 바인드 변수를 사용할 수있게 기존 C ++ OCI 기반 인프라와 함께 일하고 있습니다. (우리는 그것을 현대화하기 위해 노력하고 있지만이 옵션 오늘이 아니다)

우리는이 같은 쿼리와 약간의 행운이 있었다 :

UPDATE MyTable
   SET blobData = HEXTORAW('0EC1D7FA6B411DA5814...lots of hex data...0EC1D7FA6B411DA5814')
 WHERE ID = 123;

첫째, 이것은 좋은 일했다. 그러나, 최근에 우리는 우리의 데이터가 2000 개 이상의 바이트에 넣어해야 할 경우가 발생했습니다. 이 시점에서, 우리는 Oracle 오류에 충돌, ORA-01704 : 리터럴 너무 긴 문자열이 HEXTORAW에 전달되고 있기 때문에 문자열 이상 4000 자했다. 나는 문자열까지 분할 후 ||와 합치를 시도하지만,이 오류를 회피하지 않았다.

그래서,이 열을 업데이트하고 간단한 쿼리를 사용하여 데이터의 2000 명 이상의 바이트 '의 가치와 그것을 채울 수있는 방법이 필요합니다. 그것은 수 있습니까?

(나는 내 ​​처분에 바인드 변수가 있다면 사소한 것 알 -와 정확한 기술이이 테이블의 사용과 어떤 상호 작용 사실 다른 애플 리케이션 -하지만 불행히도 나는 여기에 DB의 내장을 리팩토링 할 수있는 위치에 아니에요 그냥 필요합니다. 테이블에 데이터를 얻을 수 있습니다.)

편집하다:

작동하지 않는 한 유망한 접근 방법은 RAW들이을 연결했다 :

UTL_RAW.CONCAT(HEXTORAW('...'), HEXTORAW('...'), HEXTORAW('...'))

이 문자열 길이 제한을 피했습니다,하지만 오라클은 또한 RAW의 길이에 일치하는 내부 2000 바이트 제한이 나타납니다. 그래서 나는 RAW와 방울을 채울 수 없습니다. 어쩌면 BLOB에 여러 RAW들이을 연결하는 기능이있다.

해결법

  1. ==============================

    1.16383를 사용하여이 5 월 같은 바이트보다 긴 BLOB를 업데이트하려면 (각 라인은 진수의 짝수 32,766까지 숫자가) :

    16383를 사용하여이 5 월 같은 바이트보다 긴 BLOB를 업데이트하려면 (각 라인은 진수의 짝수 32,766까지 숫자가) :

    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, 프로 *의 C, VB, JDBC ...) 운영 환경에 의해 부과 된 힘의 말씀의 크기 만이다. 매우 큰 문의 경우, PL / SQL은 오류 "다이애나 노드 중"실패 할 수 있습니다.

  2. ==============================

    2.당신은 PL / SQL을 사용하는 경우 분명히 당신은 이러한 제한을 초과 할 수 있습니다. :이처럼 별도의 성명에서 수행해야합니다 - 그것은 당신이 직접 UPDATE 문 내에서 HEXTORAW을 할 경우 작동하지 않습니다 중 하나

    당신은 PL / SQL을 사용하는 경우 분명히 당신은 이러한 제한을 초과 할 수 있습니다. :이처럼 별도의 성명에서 수행해야합니다 - 그것은 당신이 직접 UPDATE 문 내에서 HEXTORAW을 할 경우 작동하지 않습니다 중 하나

    DECLARE
      buf RAW(4000); 
    BEGIN
      buf := HEXTORAW('C2B97041074...lots of hex...0CC00CD00');
      UPDATE MyTable
         SET blobData = buf
       WHERE ID = 462;
    END;
    

    내 인생을 위해 나는 오라클의 몇 가지 한계를 결코 이해하지 못할 것입니다. 모두가 자신의 작은 특수 케이스입니다 것입니다.

  3. ==============================

    3.이것은 MIK의 답변에 따라,하지만 난에 구멍을 발견 그의 각 append 줄에 HEXTORAW를 사용할 때 진수 이상 한 줄을 추가하여, 각 문자열의 시작 부분에 추가 0 진수 문자를 소개합니다. 데이터베이스에서 그 진수 뒤로 당겨 당신이에 넣어 줄 알았는데 것과 비교하면이를 참조하십시오. 헥스는 이미지, 그리고 당신은 그 이미지가 하나 개의 라인 추가가 있으면 제로가 무시되고, Image.Source에 바이트,하지만 당신은 여러 줄이있는 경우, 그것은 각 청크이 추가 바이트를 소개하고 당신을 손상 결합하는 경우 데이터와 이미지를 표시 할 수 없습니다. 나는 동일 업로드 할 일반 파일 및 기타 데이터를 발생 상상한다.

    이것은 MIK의 답변에 따라,하지만 난에 구멍을 발견 그의 각 append 줄에 HEXTORAW를 사용할 때 진수 이상 한 줄을 추가하여, 각 문자열의 시작 부분에 추가 0 진수 문자를 소개합니다. 데이터베이스에서 그 진수 뒤로 당겨 당신이에 넣어 줄 알았는데 것과 비교하면이를 참조하십시오. 헥스는 이미지, 그리고 당신은 그 이미지가 하나 개의 라인 추가가 있으면 제로가 무시되고, Image.Source에 바이트,하지만 당신은 여러 줄이있는 경우, 그것은 각 청크이 추가 바이트를 소개하고 당신을 손상 결합하는 경우 데이터와 이미지를 표시 할 수 없습니다. 나는 동일 업로드 할 일반 파일 및 기타 데이터를 발생 상상한다.

    대신에, 나는 진수의 문자열로 유지하고 또한 BLOB 필드가하는 같은 4 기가 바이트 제한이있는 CLOB, 내 모든 진수를 추가. 그래서에만이 손상되지 않은 문자열은 16 진수 문자열은 32767 문자 / 바이트 한계보다 큰 RAW로 BLOB에 기록됩니다 것입니다 :

    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는 사용 된 위치 내 시나리오는했지만, 나는 아직도 그것에 연결이 다시 확립 될 수있을 때 문서를 업로드 할 때 동기화 오라클 (내 주요 데이터베이스)를 유지하는 방법이 필요했습니다.

    프로그램이 SQL을 구축하는 방법에 대한보다 완전한 답변으로, 나는 내 응용 프로그램과 함께 그렇게 때문에 내가이 표시되어야합니다 생각했다. 진수로 파일의 바이트를 둘 것입니다 내 C # 응용 프로그램의 코드는, 나는 내가 파일에 쓸 것이고, 서비스가 나중에 그 때 연결을 반환 오라클을 업데이트하는 데 사용할 것이라고 위의 SQL로 문자열 변수를했다. 나는이 SQL 문자열 및 파일로 내 진수를 공급하는 방법을 분쇄하는 방법이 그래서 (그리고 나중에, 오라클) :

    // 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 나중에하여 FileStream과 StreamWriter를 사용하여 파일에 기록되며, 서비스는 파일이 존재하는 경우, 그것을에서 읽고, 그것으로 오라클을 업데이트 본다.

    UPDATES

    미케의 대답은 당신이 당신의 덩어리와 짝수를 사용하는 경우 광산이 실제로 불필요하게 당신이 홀수 덩어리를 사용할 필요가없는 경우 추가 단계를 소개하고, 그래서,있는 그대로, 잘 사실이다. 이 변환하기 전에뿐만 아니라, 그래서 조심을 (CLOB, 다음 BLOB)를 두 번 메모리에 기록됩니다으로 더 큰 파일은 것 때문에 불필요하게 성능에 영향을 (그것은 비록 당신의 RAM을 경쟁 할 것),하지만 난에 표시 할 않았다 C # 그냥 덩어리가 깨진 얻을 것이다 방법과 SQL 실제로 프로그래밍 방법을 작성 얻을 것이다. 만 사용 버피하려면, 단지 분명히 하나의 .open 세트 ()와 .close () 태그를 하나만 dbms_lob.createtemporary () 문이 필요 제외하고, 버피와 모든 cBuf 변수를 대체합니다.

    그들이 당신의 로브 추기에 dbms_lob.open ()와 .close ()를 수행하는 것은, 때 거래 실적에 더 도움이 동안 선택이라고 말할 곳 그리고, 그 태그에 대해, 나는 또한 Oracle.com에 "AskTom"포럼을 읽고 물론,이 경우 처리되는 파일 크기에 따라 달라집니다 : 거의 완료 시간 (178.19 %)을 두 배, 거기에서 단지 악화 걸립니다 추가 수> 2000 (또는 2000 * 32766 = 65.532 MB)와 이것은 당신 또는 실제로 유용합니다. 나는 위를에 추가했다.

  4. ==============================

    4.여기에 도우미 테이블 유형 및 저장 기능을 사용하여 하나의 BLOB에 여러 행을 concating 내 솔루션입니다 :

    여기에 도우미 테이블 유형 및 저장 기능을 사용하여 하나의 BLOB에 여러 행을 concating 내 솔루션입니다 :

    create or replace type raws as table of raw(2000);
    
    create or replace function concat_raws(parts in raws) return blob
    is
        temp blob;
    begin
        if parts is null or parts.count = 0 then
           return null;
        end if;
        dbms_lob.createtemporary(temp, false, dbms_lob.CALL);
        for i in parts.first .. parts.last
        loop
            dbms_lob.append(temp, to_blob(parts(i)));
        end loop;
        return temp;
    end;
    
    -- usage example:
    select concat_raws(raws(hextoraw('CAFE'), hextoraw('BABE'))) from dual;
    

    SQL / JDBC에서 인라인 BLOB / BINARY 데이터 유형에 대한 내 다른 답변에서와 같이 이러한 접근 방식은 또한 자동화 된 SQL 생성을위한 편리합니다.

    CONCATENATE BLOB 필드 (오라클)에 대한 방법의 여러에 BLOB의 연결을 참조하십시오?

  5. ==============================

    5.오라클 12C 이후 또 다른 대안은 ALTER SYSTEM SET의 max_string_size = 확장 SCOPE = SPFILE을 사용하는 것입니다; 같은 https://docs.oracle.com/database/121/REFRN/GUID-D424D23B-0933-425F-BC69-9C0E6724693C.htm#REFRN10321에 설명.

    오라클 12C 이후 또 다른 대안은 ALTER SYSTEM SET의 max_string_size = 확장 SCOPE = SPFILE을 사용하는 것입니다; 같은 https://docs.oracle.com/database/121/REFRN/GUID-D424D23B-0933-425F-BC69-9C0E6724693C.htm#REFRN10321에 설명.

    이는 2000 년부터 32767 VARCHAR2와 RAW 최대 크기를 확장합니다.

    이 SYS 권한, DB를 다시 시작해야하고 어떤 개는을 포함합니다 : 오라클 12C가 지원 VARCHAR2로 확장> 4000 바이트 SYSDBA 아닌 사용자에 대한 작동하지 않습니다.

  6. from https://stackoverflow.com/questions/18116634/oracle-10-using-hextoraw-to-fill-in-blob-data by cc-by-sa and MIT license