窮遍所有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);
}
}
如有問題和交流請加