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

66 篇文章 4 订阅
23 篇文章 0 订阅

穷遍所有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,  sizeof(sb2),0, 0);    //  PS (这里要感谢大神https://blog.csdn.net/sl8023dxf 的研究  不仅要设置元素大小 maxLength还要设置 m_indicator ,不然默认值的写入存在覆盖或者无效问题)

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);
            }
        }

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OCIStmtPrepare2函数用于准备一个SQL语句,它的原型如下: ``` sword OCIStmtPrepare2(OCISvcCtx *svchp, OCIStmt **stmtp, OCIError *errhp, CONST OraText *stmt, ub4 stmt_len, CONST OraText *key, ub4 key_len, ub4 language, ub4 mode); ``` 其中,svchp是一个服务上下文句柄,stmtp是一个指向语句句柄指针的指针,errhp是一个错误句柄,stmt是一个指向SQL语句的指针,stmt_len是SQL语句的长度,key是一个指向游标的名称的指针,key_len是游标名称的长度,language是编译SQL语句时使用的语言,mode是编译模式。 如果SQL语句中有参数,可以通过绑定变量的方式传递参数,具体步骤如下: 1. 在SQL语句中使用":"加上变量名表示一个参数。 2. 通过OCIBindByName函数绑定变量名和变量地址。 3. 在OCIStmtExecute函数中传递绑定变量的数组。 下面是一个示例代码: ```c /* 准备SQL语句 */ const char *sql = "SELECT name FROM employees WHERE id = :id"; OCIStmtPrepare2(svchp, &stmtp, errhp, sql, strlen(sql), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT); /* 绑定变量 */ int id = 123; OCIBind *bindp; OCIStmtGetBindInfo(stmtp, errhp, 1, &bindp); OCIBindByName(bindp, &id, sizeof(id), OCI_BIND_INPUT, NULL, 0, SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT); /* 执行SQL语句 */ OCIStmtExecute(svchp, stmtp, errhp, 1, 0, NULL, NULL, OCI_DEFAULT); ``` 在上面的代码中,使用OCIBindByName函数绑定了一个名为":id"的变量和变量id的地址,然后在OCIStmtExecute函数中传递了绑定变量的数组。注意,这里的第一个参数是1,表示绑定变量的数组中只有一个变量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值