OCI編程高級(jí)篇(十七) 直接路徑裝載總結(jié)
訪問www.tomcoding.com網(wǎng)站,學(xué)習(xí)Oracle內(nèi)部數(shù)據(jù)結(jié)構(gòu),詳細(xì)文檔說明,下載Oracle的exp/imp,DUL,logminer,ASM工具的源代碼,學(xué)習(xí)高技術(shù)含量的內(nèi)容。
直接路徑裝載的過程其實(shí)跟普通插入沒有太大的區(qū)別,只是設(shè)置更復(fù)雜,步驟覺得雜亂,其實(shí)理解了它的原理,就能自然知道需要哪些步驟。我們先看看插入數(shù)據(jù)有哪些問題。
1. 要插入數(shù)據(jù)的表名稱是什么?表屬于哪個(gè)schema?
2. 要插入的表有哪些字段啊?字段名稱是什么?數(shù)據(jù)類型是什么?最大長(zhǎng)度是多少?
3. 程序中的數(shù)據(jù)怎樣和字段關(guān)聯(lián)起來啊?
4. 怎樣把程序數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)庫的數(shù)據(jù)?
5. 怎樣把數(shù)據(jù)庫數(shù)據(jù)存儲(chǔ)起來啊?
有了這些問題,我們看看直接路徑裝載是怎樣解決這些問題的。
首先要分配一個(gè)句柄來存儲(chǔ)表的信息,這個(gè)句柄叫做直接路徑上下文dpctx,分配句柄后要設(shè)置它的屬性,其中有表的schema信息,表的名稱信息,表的字段個(gè)數(shù)。然后從dpctx中得到字段列表描述符,針對(duì)每個(gè)字段設(shè)置字段名稱,數(shù)據(jù)類型,數(shù)據(jù)最大長(zhǎng)度這些屬性。接著要分配一個(gè)句柄來存放表的字段信息,這個(gè)句柄叫直接路徑字段數(shù)組dpca,這個(gè)句柄就能把程序中的數(shù)據(jù)和裝載的字段關(guān)聯(lián)起來,使用OCIDirPathColArrayEntrySet()函數(shù)設(shè)置每行每個(gè)字段的數(shù)據(jù)。最后分配一個(gè)代表數(shù)據(jù)庫數(shù)據(jù)的句柄,這個(gè)句柄叫做直接路徑流dpstr,這個(gè)句柄代表數(shù)據(jù)庫服務(wù)器端的數(shù)據(jù)。dpca代表OCI程序客戶端的數(shù)據(jù),通過OCIDirPathColArrayToStream()函數(shù)就能把客戶端的數(shù)據(jù)轉(zhuǎn)換成服務(wù)器端的數(shù)據(jù)格式,通過OCIDirPathLoadStream()函數(shù)就能指示服務(wù)器端的數(shù)據(jù)存儲(chǔ)到表的塊中。最后通過OCIDirPathFinish()函數(shù)提交裝載的數(shù)據(jù),相當(dāng)于一般插入后的commit操作。
下面我們把前幾節(jié)中的代碼片段組合成一個(gè)完整的例子,看看直接路徑裝載的完整過程。還是以表test_tab為例,表的屬主用戶(schema)是scott,字段是ID,NAME和ADDR三個(gè),裝載4次數(shù)據(jù),每次100行,看看循環(huán)設(shè)置數(shù)據(jù)入口和重置狀態(tài)都在哪些位置來操作。
OCIEnv *envhp = NULL; OCIError *errhp = NULL; OCIServer *svrhp = NULL; OCISession *usrhp = NULL; OCISvcCtx *svchp = NULL;
struct dp_columns { ub4 dtyp; /* 字段類型 */ ub4 clen; /* 字段最大長(zhǎng)度 */ char name[32]; /* 字段名稱 */ }; int dp_load(void){ int i, r; sword rc, status; ub4 buf_sz; ub4 ncol; ub4 rowcnt; ub4 rowoff; ub4 cvtcnt; OCIDirPathCtx *dpctx; OCIDirPathColArray *dpca; OCIDirPathStream *dpstr; OCIParam *colLst = NULL; OCIParam *colDsc = NULL; struct dp_columms col[3]; ub4 id[100]; char name[100][32]; char addr[100][32]; /* 分配直接路徑上下文句柄,父句柄是envhp */ rc = OCIHandleAlloc((void *)envhp, (void **)&dpctx, OCI_HTYPE_DIRPATH_CTX, 0, (void **)NULL); if (rc != OCI_SUCCESS) { fprintf(stderr, "Allocate direct path context error !\n"); return (-1); } /* 設(shè)置表的schema,在上下文句柄中設(shè)置 */ if (check_oci_error(errhp, OCIAttrSet((void *)dpctx, (ub4)OCI_HTYPE_DIRPATH_CTX, (void *)"scott", strlen("scott"), (ub4)OCI_ATTR_SCHEMA_NAME, errhp) ) < 0) return (-1); /* 設(shè)置表名稱,在上下文句柄中設(shè)置 */ if (check_oci_error(errhp, OCIAttrSet((void *)dpctx, (ub4)OCI_HTYPE_DIRPATH_CTX, (void *)"test_tab", strlen("test_tab"), (ub4)OCI_ATTR_NAME, errhp) ) < 0) return (-1); /* 設(shè)置轉(zhuǎn)換緩沖區(qū)的大小為2M,緩沖區(qū)大小要與字段數(shù)組相適應(yīng), * 太小的話一次轉(zhuǎn)換緩沖區(qū)不夠,會(huì)返回OCI_CONTINUE,需要再次轉(zhuǎn)換 */ buf_sz = 2 * 1024 * 1024; if (check_oci_error(errhp, OCIAttrSet((void *)dpctx, (ub4)OCI_HTYPE_DIRPATH_CTX, (void *)&buf_sz, 0, (ub4)OCI_ATTR_BUF_SIZE, errhp) ) < 0) return (-1); /* 設(shè)置表的字段個(gè)數(shù) */ ncol = 3; if (check_oci_error(errhp, OCIAttrSet((void *)dpctx, (ub4)OCI_HTYPE_DIRPATH_CTX, (void *)&ncol, 0, (ub4)OCI_ATTR_NUM_COLS, errhp) ) < 0) return (-1); /* 獲取字段列表描述符,設(shè)置字段信息 */ if (check_oci_error(errhp, OCIAttrGet((void *)dpctx, (ub4)OCI_HTYPE_DIRPATH_CTX, (void *)&colLst, (ub4)0, (ub4)OCI_ATTR_LIST_COLUMNS, errhp) ) < 0) return (-1); /* 為了方便設(shè)置,我們定義一個(gè)結(jié)構(gòu)存儲(chǔ)字段信息 */ col[0].dtyp = SQLT_INT; col[0].clen = 8; strcpy(col[0].name, "ID"); col[1].dtyp = SQLT_CHR; col[1].clen = 30; strcpy(col[1].name, "NAME"); col[2].dtyp = SQLT_CHR; col[2].clen = 200; strcpy(col[2].name, "ADDR"); for (i=0; i<ncol; i++) { /* 獲取字段描述符,這個(gè)描述符是隱式獲得,需要釋放,否則會(huì)造成內(nèi)存泄露 * 這里字段的位置從1開始編號(hào) */ if (check_oci_error(errhp, OCIParamGet((const void *)colLst, (ub4)OCI_DTYPE_PARAM, errhp, (void **)&colDsc, (ub4)(i+1)) ) < 0) return (-1); /* 設(shè)置字段的屬性,字段名稱 */ if (check_oci_error(errhp, OCIAttrSet((void *)colDsc, (ub4)OCI_DTYPE_PARAM, (void *)col[i].name, (ub4)strlen(col[i].name), (ub4)OCI_ATTR_NAME, errhp) ) < 0) return (-1); /* 設(shè)置字段數(shù)據(jù)類型 */ if (check_oci_error(errhp, OCIAttrSet((void *)colDsc, (ub4)OCI_DTYPE_PARAM, (void *)&col[i].dtyp, (ub4)0, (ub4)OCI_ATTR_DATA_TYPE, errhp) ) < 0) return (-1); /* 設(shè)置字段的數(shù)據(jù)最大長(zhǎng)度 */ if (check_oci_error(errhp, OCIAttrSet((void *)colDsc, (ub4)OCI_DTYPE_PARAM, (void *)&col[i].clen, (ub4)0, (ub4)OCI_ATTR_DATA_SIZE, errhp) ) < 0) return (-1); } /* 釋放掉字段描述符 */ OCIDescriptorFree((void *)colDsc, (ub4)OCI_DTYPE_PARAM); /* 設(shè)置完所有字段后,要把字段列表的描述符也釋放掉 */ OCIDescriptorFree((void *)colLst, (ub4)OCI_DTYPE_PARAM); /* 設(shè)置完屬性后,準(zhǔn)備直接路徑裝載 */ if (check_oci_error(errhp, OCIDirPathPrepare(dpctx, svchp, errhp)) < 0) return (-1);? /* 分配直接路徑字段數(shù)組句柄,父句柄是dpctx */ rc = OCIHandleAlloc((void *)dpctx, (void **)&dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY, 0, (void **)NULL); if (rc != OCI_SUCCESS) { fprintf(stderr, "Allocate direct path column array handle error !\n"); return (-1); } /* 分配直接路徑流句柄,父句柄是dpctx */ rc = OCIHandleAlloc((void *)dpctx, (void **)&dpstr, OCI_HTYPE_DIRPATH_STREAM, 0, (void **)NULL); if (rc != OCI_SUCCESS) { fprintf(stderr, "Allocate direct path stream handle error !\n"); return (-1); } /* 循環(huán)4次,每次裝載100行數(shù)據(jù) */ for (r=0; r<4; r++) { /* 這里生成100行數(shù)據(jù),在真實(shí)的環(huán)境中數(shù)據(jù)可能來自文件或其他數(shù)據(jù)源 * 為了演示方便,直接把name和addr設(shè)置成了相同的數(shù)據(jù) */ for (i=0; i<100; i++) { id = i + 100; /* id從100開始計(jì)數(shù) */ strcpy(name[i], "AAAAAAAAAA"); strcpy(addr[i], "BBBBBBBBBBBBBBBB"); } /* 循環(huán)設(shè)置每一行每一列的數(shù)據(jù)入口 */ for (i=0; i<100; i++) { /* 設(shè)置第一列ID的數(shù)據(jù)入口,列索引是從0開始的 */ if (check_oci_error(errhp, OCIDirPathColArrayEntrySet(dpca, errhp, (ub4)i, (ub2)0, (ub1*)&id[i], 4, OCI_DIRPATH_COL_COMPLETE) ) < 0) return (-1); /* 設(shè)置第二列NAME的數(shù)據(jù)入口,列索引是1 */ if (check_oci_error(errhp, OCIDirPathColArrayEntrySet(dpca, errhp, (ub4)i, (ub2)1, (ub1 *)name[i], (ub4)strlen(name[i]), OCI_DIRPATH_COL_COMPLETE) ) < 0) return (-1); /* 設(shè)置第三列ADDR的數(shù)據(jù)入口,列索引是2 */ if (check_oci_error(errhp, OCIDirPathColArrayEntrySet(dpca, errhp, (ub4)i, (ub2)2, (ub1 *)addr[i], (ub4)strlen(addr[i]), OCI_DIRPATH_COL_COMPLETE) ) < 0) return (-1); } /* 重置直接路徑字段數(shù)組的狀態(tài) */ if (check_oci_error(errhp, OCIDirPathColArrayReset(dpca, errhp)) < 0) return (-1); /* 重置直接路徑流的狀態(tài) */ if (check_oci_error(errhp, OCIDirPathStreamReset(dpstr, errhp)) < 0) return (-1); /* 轉(zhuǎn)換字段數(shù)組到流數(shù)據(jù),然后裝載流數(shù)據(jù),一共轉(zhuǎn)換100行數(shù)據(jù),從第0行開始 */ rowcnt = 100; rowoff = 0; while (1) { status = OCIDirPathColArrayToStream(dpca, dpctx, dpstr, errhp, (ub4)rowcnt, (ub4)rowoff); if (status == OCI_CONTINUE) { /* 轉(zhuǎn)換緩沖區(qū)過小,數(shù)據(jù)沒有轉(zhuǎn)換完,得到已經(jīng)轉(zhuǎn)換的行數(shù) */ if (check_oci_error(errhp, OCIAttrGet((const void *)dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY, (void *)(&cvtcnt), (ub4 *)0, OCI_ATTR_ROW_COUNT, errhp) ) < 0) return (-1); rowcnt -= cvtcnt; rowoff += cvtcnt; } else { if (check_oci_error(errhp, status) < 0) return (-1); } /* 裝載數(shù)據(jù) */ if (check_oci_error(errhp, OCIDirPathLoadStream(dpctx, dpstr, errhp)) < 0) return (-1); /* 如果前面的轉(zhuǎn)換全部完成,現(xiàn)在裝載也成功了,說明這100條數(shù)據(jù)裝載完成 * 退出while()循環(huán) */ if (status == OCI_SUCCESS) break; /* 程序到這里,說明還有未轉(zhuǎn)換完的數(shù)據(jù),重置直接路徑流的狀態(tài),進(jìn)行下一次轉(zhuǎn)換, * 下次轉(zhuǎn)換從新的rowoff位置開始,轉(zhuǎn)換條數(shù)為新的rowcnt條 */ if (check_oci_error(errhp, OCIDirPathStreamReset(dpstr, errhp)) < 0) return (-1); } /* while()循環(huán)的結(jié)束邊界 */ /* 裝載完了100條數(shù)據(jù),進(jìn)入下一循環(huán),裝載另外的100條數(shù)據(jù),或者全部裝載完退出循環(huán) */ } /* for()循環(huán)的結(jié)束邊界 */ /* 數(shù)據(jù)全部裝載完,提交裝載的數(shù)據(jù) */ if (check_oci_error(errhp, OCIDirPathFinish(dpctx, errhp)) < 0) return (-1); /* 釋放前面分配的句柄 */ OCIHandleFree((void *)dpca, OCI_HTYPE_DIRPATH_COLUMN_ARRAY); OCIHandleFree((void *)dpstr, OCI_HTYPE_DIRPATH_STREAM); OCIHandleFree((void *)dpctx, OCI_HTYPE_DIRPATH_CTX); return (0); }
到這里OCI的直接路徑裝載就介紹完了,上面的例子演示了一個(gè)完整的過程,包括數(shù)據(jù)轉(zhuǎn)換中遇到的一些問題的解決方法,多看幾遍就能對(duì)直接路徑裝載的流程全面掌握。

浙公網(wǎng)安備 33010602011771號(hào)