访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容。

上一节我们介绍了LOB定位符的绑定和定义操作,这里重点强调一下定义操作,这个行为一般用于LOB SELECT操作,LOB SELECT操作是所有LOB操作的基础,所有的关于LOB操作的OCI函数都是针对LOB定位符的,LOB SELECT的目的就是为了得到一个LOB定位符,一个LOB表的每行数据的每个LOB字段都对应一个LOB定位符,所以处理一行数据的多个LOB字段也要得到多个LOB定位符。下面看看LOB写操作的步骤。

1. 准备LOB SELECT语句,一般SQL语句文本为SELECT lob_column FROM lob_table WHERE XXXX=XXXX FOR UPDATE。

2. 定义输出的LOB定位符。这个定位符在之前要分配好,不用设置为空LOB。

3. 执行LOB SELECT语句。

4. 执行OCIStmtFetch()操作,把LOB定位符和LOB字段关联起来。

5. 打开LOB定位符,使用OCILobOpen()函数。

6. 写入LOB数据,可以循环写入多次LOB数据,使用OCILobWrite2()函数。

7. 关闭LOB定位符,使用OCILobClose()函数。

8. 提交数据库改变。

先看看用到的OCI函数原型和参数。

打开LOB函数。

sword OCILobOpen ( OCISvcCtx *svchp,
    OCIError           *errhp,
    OCILobLocator *locp,
    ub1                    mode );

svchp是一个输入参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要打开的LOB定位符。

mode是一个输入参数,打开的方式,取值OCI_LOB_READONLY或OCI_LOB_READWRITE。

关闭LOB函数。

sword OCILobClose ( OCISvcCtx *svchp,
    OCIError           *errhp,
    OCILobLocator *locp );

svchp是一个输入参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要关闭的LOB定位符。

写LOB函数。

sword OCILobWrite2 ( OCISvcCtx *svchp,
    OCIError           *errhp,
    OCILobLocator *locp,
    oraub8              *byte_amtp,
    oraub8              *char_amtp,
    oraub8              offset,
    void                   *bufp,
    oraub8               buflen,
    ub1                    piece,
    void                   *ctxp,
    OCICallbackLobWrite2 (cbfp)
    (
        void     *ctxp,
        void     *bufp,
        oraub8 *lenp,
        ub1      *piecep
        void      **changed_bufpp,
        oraub8 *changed_lenp
    )
    ub2                    csid,
    ub1                    csfrm );

svchp是一个输入/输出参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要操作的LOB定位符,唯一引用一个LOB。

byte_amtp是一个输入/输出参数,写入LOB的字节数,在BLOB写入时使用。

char_amtp是一个输入/输出参数,写入LOB的字符个数,在CLOB写入时使用,BLOB写入时忽略这个参数。

offset是一个输入参数,写入LOB的绝对偏移量,从LOB头开始计算。对CLOB计算单位是字符,对BLOB计算单位是字节。offset的位置从1开始计算。如果使用流写入方式,只需要在第一次调用写函数时设置offset值,后续的写函数可以忽略offset,函数会自动判断。

bufp是一个输入参数,是写入的LOB数据的缓冲区指针。

buflen是一个输入参数,表示LOB数据缓冲区中数据的大小,以字节计算。

piece是一个输入参数,表示写入的是哪个数据片。取值为OCI_ONE_PIECE,流模式中为OCI_FIRST_PIECE,OCI_NEXT_PIECE和OCI_LAST_PIECE。

ctxp是一个输入参数,是回调函数的上下文指针,可以设置为NULL。

cbfp是一个输入参数,是回调函数的函数指针,不使用回调函数设置为NULL。

csid是一个输入参数,是缓冲区中数据的字符集ID,只对CLOB起作用。

csfrm是一个输入参数,是缓冲区数据的字符集形式,取值SQLCS_IMPLICIT表示与数据库字符集一致,取值SQLCS_NCHAR表示使用国际字符集。

我们使用上一节中创建的表test_clob_tab,里面已经插入一条空LOB数据,现在看看怎样写入LOB数据,我们向LOB中写入20次,每次写入4000字符,使用流模式写入。代码如下。

OCIEnv        *envhp = NULL;
OCIError *errhp = NULL;
OCIServer *svrhp = NULL;
OCISession *usrhp = NULL;
OCISvcCtx *svchp = NULL;
OCIStmt *smthp = NULL; ​​int write_to_lob(void){
int i;
sword rc;
sb4 ec;
ub1 piece;
int slen;
oraub8 amt;
OCIDefine *defp;
OCILobLocator *locp;
char sqltxt[1024];
text errbuf[512];
char buf[4096]; /* 分配LOB定位符 */
rc = OCIDescriptorAlloc((const void *)envhp, (void **)&locp,
OCI_DTYPE_LOB, 0, (void **)NULL);
if (rc != OCI_SUCCESS) {
OCIErrorGet(envhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIDescriptorAlloc() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 下面执行LOB SELECT操作 */
strcpy(sqltxt, "SELECT MESSAGE FROM test_clob_tab WHERE ID=1 FOR UPDATE");
slen = strlen(sqltxt); rc = OCIStmtPrepare(smthp, errhp, (const OraText *)sqltxt, slen,
OCI_NTV_SYNTAX, OCI_DEFAULT); if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIStmtPrepare() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 定义LOB定位符输出 */
rc = OCIDefineByPos((OCIStmt *)smthp,
(OCIDefine **)&defp,
(OCIError *)errhp,
(ub4)1,
(void *)&locp,
(sb4)sizeof(OCILobLocator *),
(ub2)SQLT_CLOB,
(void *)NULL,
(ub2 *)NULL,
(ub2 *)NULL,
(ub4)OCI_DEFAULT); if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIDefineByPos() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 执行语句 */
rc = OCIStmtExecute(svchp,
smthp, /* stmthp */
errhp, /* errhp */
0, /* iters */
0, /* rowoff */
NULL, /* snap_in */
NULL, /* snap_out */
OCI_DEFAULT); /* mode */ if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIExecute() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 执行fetch操作 */
rc = OCIStmtFetch(smthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT); if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIStmtFetch() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 打开LOB */
rc = OCILobOpen(svchp, errhp, locp, OCI_LOB_READWRITE);
if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCILobOpen() - [%d] %s\n", ec, errbuf);
return (-1);
} for (i=0; i<20; i++) {
if (i == 0)
piece = OCI_FIRST_PIECE;
else if (i == 19)
piece = OCI_LAST_PIECE;
else
piece = OCI_NEXT_PIECE; sprintf(buf, "%02d", i);
memset(&buf[2], 3998, 'A'); amt = 4000; rc = OCILobWrite2(svchp, errhp, locp, NULL, &amt, 1,
buf, 4000, piece, 0, SQLCS_IMPLICIT);
if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCILobWrite2() - [%d] %s\n", ec, errbuf);
return (-1);
}
} /* 关闭LOB */
rc = OCILobClose(svchp, errhp, locp);
if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCILobClose() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 提交改变 */
rc = OCITransCommit(svchp, errhp, OCI_DEFAULT); if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCITransCommit() - [%d] %s\n", ec, errbuf);
return (-1);
} /* 释放LOB定位符 */
rc = OCIDescriptorFree((void *)locp, OCI_DTYPE_LOB);
if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIDescriptorFree() - [%d] %s\n", ec, errbuf);
return (-1);
} return (0);
}

在上面的写入中使用了流模式,在第一次写入时使用OCI_FIRST_PIECE,最后一次写入使用OCI_LAST_PIECE,其他的写入使用OCI_NEXT_PIECE,只需要在第一次写入时指定写入偏移量1,就可以从头写入数据,后续写操作的偏移量都被忽略了。写入函数的csid设置为0,数据使用环境变量NLS_LANG定义的字符集。

如果不使用流模式写入,那么写入操作使用OCI_ONE_PIECE,每次写入一片数据,每次要自己计算偏移量的位置,好处是可以随机向任意位置写入数据。

在写数据前要打开LOB,写完后要关闭LOB,跟写一个文件相似。如果不打开LOB也可以直接调用写函数,不过每次写操作Oracle还是会隐式的打开LOB,写完一次后隐式关闭LOB,这样在大量多次写入时会影响效率,所以在写之前打开LOB是一个好习惯。

OCI编程高级篇(八) LOB写操作的更多相关文章

  1. PC游戏编程(入门篇)(前言写的很不错)

    PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...

  2. ch341a编程器写操作超时失败

    当点击自动编写‘提示写操作超时失败’要怎么样才能解决,下面我给大家分享一下!   方法/步骤     首先我们点击操作   选择操作选项   看看箭头所指的几个地方是不是都没打上勾   我们把这几个地 ...

  3. 如何编写高质量的 JS 函数(3) --函数式编程[理论篇]

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ作者:杨昆 [编写高质量函数系列]中, <如何 ...

  4. C#高级知识点&(ABP框架理论学习高级篇)——白金版

    前言摘要 很早以前就有要写ABP高级系列教程的计划了,但是迟迟到现在这个高级理论系列才和大家见面.其实这篇博客很早就着手写了,只是楼主一直写写停停.看看下图,就知道这篇博客的生产日期了,谁知它的出厂日 ...

  5. 【转载】Spark性能优化指南——高级篇

    前言 数据倾斜调优 调优概述 数据倾斜发生时的现象 数据倾斜发生的原理 如何定位导致数据倾斜的代码 查看导致数据倾斜的key的数据分布情况 数据倾斜的解决方案 解决方案一:使用Hive ETL预处理数 ...

  6. 【读书笔记】.Net并行编程高级教程(二)-- 任务并行

    前面一篇提到例子都是数据并行,但这并不是并行化的唯一形式,在.Net4之前,必须要创建多个线程或者线程池来利用多核技术.现在只需要使用新的Task实例就可以通过更简单的代码解决命令式任务并行问题. 1 ...

  7. 【读书笔记】.Net并行编程高级教程--Parallel

    一直觉得自己对并发了解不够深入,特别是看了<代码整洁之道>觉得自己有必要好好学学并发编程,因为性能也是衡量代码整洁的一大标准.而且在<失控>这本书中也多次提到并发,不管是计算机 ...

  8. 【转】【技术博客】Spark性能优化指南——高级篇

    http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...

  9. Shell编程进阶篇(完结)

    1.1 for循环语句 在计算机科学中,for循环(英语:for loop)是一种编程语言的迭代陈述,能够让程式码反复的执行. 它跟其他的循环,如while循环,最大的不同,是它拥有一个循环计数器,或 ...

  10. Kotlin——从无到有系列之高级篇(一):Lambda表达式

    如果您对Kotlin很有兴趣,或者很想学好这门语言,可以关注我的掘金,或者进入我的QQ群大家一起学习.进步. 欢迎各位大佬进群共同研究.探索 QQ群号:497071402 进入正题 经过前面一系列对K ...

随机推荐

  1. TPS和QPS的概念

    TPS    TPS:Transactions Per Second(每秒传输的事务处理个数),即服务器每秒处理完成的事务数.TPS包括一条消息入和一条消息出,加上一次用户数据库访问.    TPS是 ...

  2. MySQL中自增长序列(@i:=@i+1)的用处及用法

    问题分析    Oracle中的伪列 ROWNUM 是一组递增的序列,在查询数据时生成,为结果集中每一行标识一个行号, 每条记录会因为输出的顺序不同而获得不同的逻辑编号:此自增长序列可以视作起始值为 ...

  3. Go与C/C++ 互相调用

    A. Go调用C 1.Go调用C:在go文件里调C(以下代码中除了开头的注释之外,其他注释不可删除) /* * go 和 C 互调用程序 */ package main /* int Add( int ...

  4. fastjson jsonobject对象转为网址传参pathvalue形式并按首字母排序

    效果 代码 @Test public void test() { JSONObject jsonObject = new JSONObject(true); jsonObject.put(" ...

  5. 【pr】眨眼特效

    来源 这个后半段 步骤 新建一段黑场视频 效果->网格化->边角的两个数值调整很大(4000,4000),现在黑场只剩下一个白色十字架. 效果控件->网格->锚点->第一 ...

  6. Permutation Counting

    \(n\) 的范围很小,考虑动态规划. \(f_{i,j}\) 在前 \(i\) 个数有 \(j\) 个 \(<\) 的个数. 若 \(\texttt {a<b<c<d}\), ...

  7. LLM 输出配置 (LLM output configuration)

    1.概述 大型语言模型(LLM)的输出行为可以通过多种配置参数进行精细控制.这些参数共同决定了模型生成文本的质量.风格和多样性.理解这些配置选项及其相互作用对于有效使用LLM至关重要. 2.输出长度 ...

  8. NAT的两种模式SNAT和DNAT介绍

    一.简单介绍 NAT(Network Address Translation):网络地址转换,是将IP数据包头中的IP地址转换为另一个IP地址的过程.在实际的应用中,NAT主要用于实现私有网络访问公共 ...

  9. shell 使用awk 分析nginx日志取出400 的请求写入文件,然后php读取文件处理数据

    使用awk分析昨日 的nginx日志,将服务端未处理成功的400 请求,重新请求,将数据补进去 下面是代码,如果有类似问题的话,可以参考一下 #!/bin/bash ## 1. shell 获取日期获 ...

  10. DotTrace系列:3. 时间度量之墙钟时间和线程时间

    一:背景 1. 讲故事 在用 dotTrace 对程序进行性能评测的时候,有一个非常重要的概念需要使用者明白,那就是 时间度量 (Time measurement),主要分为两种. 墙钟时间 线程时间 ...