Oracle 批量提交,批量綁定 OCIBindByName 和OCIBindObject 的使用

窮遍所有OCI文檔找不出一個能綁定多行數據的說明和示例,自己嘗試快兩週解決了Oracle Spatial 批量綁定將Oracle的寫入效率提升到了5000行左右,以下是一點心得

Oracle OCI  基本操作 本文不多說,假設你會用基本的OCI操作數據,但是不知道怎麼用OCIBindByName  一次可以綁定級別上千行數據, 本文將對你有點用

 從Statement 說起,分以下三步

1: 準備sql:  

 OCIStmtPrepare (m_h,   //OCIStmt 指針

                             m_ErrorHandle,   //OCIError指針

                            (text *)sql, //sql 語句
                            (ub4)strlen((const char *)sql),  //SQL語句長度

                         OCI_NTV_SYNTAX, 
                         OCI_DEFAULT))),

2: 綁定寫入的數據,  可以是一行數據,也可以是多行數據

2.1非對象綁定:

一行數據:

OCIBindByName(stmt, &m_h, err, (const OraText*)name, strlen(name),
            BufferPtr(), BufferSize() , dty, indp, 0,0,0,0, (ub4)bindtyp))

單行數據BufferPtr 就是數據的指針,Oracle 可操作的就只有OCINumber,OCIDate,OCI*** 等oci自定義類型

BufferPtr的實際分配長度要比實際數據多1字節,

字符串的BufferSize() 需要比strlen求出來的長度大一,並且最後一位的值爲'\0'

一個示意圖: 

多行數據:

   OCIBindByName(stmt, &m_h, err, (const OraText*)name, strlen(name),
            (void*)Ptr(), maxLength, dty, (void*)(&m_indicator[0]), 0, 0, 0, 0, (ub4)bindtyp)))

  OCIBindArrayOfStruct(m_h, err, (ub4)maxLength, 0,0, 0);

Ptr() 指向的內存結構:

如果是字符串;內存結構如下, 當然這裏是一元數組, 圖中只是爲了對於關係:

上圖是將原始數據映射爲可給OCIBindbyName輸入的數據,

如果是綁定數值,OCIDate,OCINumber , 則這個Ptr數據內存就是OCIDate,OCINumber對象數組, maxlength 用sizeof(OCIDate)sizeof(OCINumber)表示. 不用特別處理.

maxLength 代碼每行元素中一個元素的最大長度, 不是Ptr() 這個指針內存的所有長度

m_indicator 是一個數組,代表Ptr()這個指針內存中每個對象的實際長度,  如果設置爲-1,  Oracle 將綁定NULL

設置其他>=0的值爲有效,(PS:這裏有個大坑誤區, 這裏沒有指明 這個實際數組的長度, Oracle如何知道我們數組的長度,我們一般API都要指明, 而OCI的數組長度都由 OCIStmtExecute的第四個參數指定)

        

2.2 對象綁定

先調用OCIBindByName  獲得OCIBInd 對象,再調用OCIBindObject  綁定一個對象或者多個對象

OCIBindByName 初始化的時候, dty參數爲SQLT_NTY,其他參數全爲0都可以, 因爲oci會忽略其他參數, 不管單個綁定還是多個,

單個Object 綁定        

簡單介紹下SDOGeometry這個對象是Oracle 自定義對象, 表示幾何對象, 裏面兩個數組存不固定的座標,以及固定長度的數據頭描述, 這種網上很多不過多介紹了.

OCIBindObject(bind->m_h, stmt->ErrorHandle(),
        m_pMetaTable->SDO_GEOMETRY_TYPE()->Type(), 
       (void**)sdogeometry.SDOGeometryPtrAdd(), 0,
         (void**)sdogeometry.SDOGeometryIndPtrAdd(), 0);

沒有任何可說明的, 傳對象和類型可搞定.

多個Object綁定

先包裝數組指針,   並且m_indszp參數必須是ub4數組(PS:OCI官網說是sb2, 我看Oracle寫文檔的真是sb, ),這個數組直接存每個對象的描述長度,這裏描述長度是倒數第二個參數裏面元素的sizeof長度, 我這是寫入Oracle Spatial 空間數據,所以每個m_indszp元素其實就是一個固定描述長度18.

 for (int i = 0; i < nCount; i++)
    {
        m_pGeomCache.push_back(sdoBuilder[i]->SDOGeometryPtr());
        m_pGeom_inCache.push_back(*sdoBuilder[i]->SDOGeometryIndPtrAdd());
        m_indszp.push_back(sizeof(sdo_geometry_ind));
    }

OCIBindObject(bind->m_h, stmt->ErrorHandle(),
        m_pMetaTable->SDO_GEOMETRY_TYPE()->Type(), 
        (void**)(&(m_pGeomCache[0])), 0,
        (void**)&m_pGeom_inCache[0], &m_indszp[0]);

這裏沒有研究blob 綁定, 這裏沒嘗試,簡單猜想跟普通blob 綁定應該類似

普通blob 綁定是在OCIStmtExecute後執行OCILobWrite完成的, 

OCILobWrite(pConn->m_h, err, 
        m_LobDesc.m_h, &len, 1, 
        PtrT<dvoid*>(), len, 
        OCI_ONE_PIECE, NULL, 0, 0, 0 )

這裏很可能是多次調用這個函數, 因爲OCI_ONE_PIECE 這個就是跟遊標類似的寫法.

 

3: 執行綁定

這裏只有一個參數需要注意, nArrayCount 這就是我們每個OCIBindByName綁定的數據條數了.

OCIStmtExecute(*pConn,
            m_h,
            m_ErrorHandle,
            (ub4)nArrayCount,
            (ub4)0,
            (CONST OCISnapshot *) 0,
            (OCISnapshot *)0,
            (ub4)OCI_BATCH_ERRORS)

 

另外想獲得那條出錯, 可以使用以下代碼:

        ub4   num_errs; 
    
        COCIErrorHandle  errhp2(m_EnvHandle);
        OCIAttrGet(m_h, OCI_HTYPE_STMT, &num_errs, 0, OCI_ATTR_NUM_DML_ERRORS, errhp2);
        if (num_errs) {
            GsString str;
            for (int i = 0; i < num_errs; i++)
            {
                OCIError* errortmp;
                sword h = OCIHandleAlloc((void *)m_EnvHandle, (void **)&errortmp,
                    (ub4)OCI_HTYPE_ERROR, 0, (void **)0);
            
                OCIParamGet(m_ErrorHandle.ErrorHandle(), OCI_HTYPE_ERROR, errhp2.ErrorHandle(), (void**)(&errortmp), i);
                sword errorcode = 0;
                int rowoffset = 0;
                OCIAttrGet(errortmp, OCI_HTYPE_ERROR, &rowoffset, 0, OCI_ATTR_DML_ROW_OFFSET, errhp2.ErrorHandle());
                text errbuf[512];
                (void)OCIErrorGet((dvoid *)errortmp, (ub4)1, (text *)NULL, &errorcode,
                    errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
                 str = GsEncoding::ToLocal((const char*)errbuf);
                 OCIHandleFree((dvoid *)errortmp, (ub4)OCI_HTYPE_ERROR);
            }
        }

如有問題和交流請加

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章