OCI编程高级篇(三) 批量执行

前面我们讨论了数组插入数据的过程,实际上Oracle还是一条条的执行数组中的元素,只不过是一次性的提供了多条数据而已。当数据单条执行时,如果出现了错误,我们确切的知道是那一条出了错,修改数据后再执行就可以了。但是数组插入让我们遇到了问题,数组中的数据只要有一条出现了错误,整个数组插入操作就会报错,并且错误行后面的数据不会再执行,这时只能先回滚掉所有的操作,然后找出错误数据行,修正后,再重新执行数组操作。如果数组中有多条错误,那会严重影响效率,并且程序员处理这些错误也会非常头痛。

那么有没有什么好的方法来避免上面的麻烦呢?答案是肯定的,OCI提供了一种机制,叫做执行的批量错误模式,在这一模式下,OCI会执行数组中的每一条数据,然后收集错误行的信息,OCI程序可以返回每个错误行的出错信息,然后修正数据,只需要重新执行错误行数据就可以了。

批量错误模式的处理步骤如下。

1. 分配OCI语句。

2. 准备SQL语句文本。

3. 绑定数组数据。

4. 执行语句,使用OCI_BATCH_ERRORS模式。

5. 获取出错行的数量,使用OCI_ATTR_NUM_DML_ERRORS调用OCIAttrGet()函数。

6. 获取每个错误的错误码和错误信息文本

7. 处理错误。

在第六步中用到了一个函数OCIParamGet()用于取得每个错误的错误句柄。

sword OCIParamGet ( const void *hndlp,
    ub4          htype,
    OCIError *errhp,
    void         **parmdpp,
    ub4          pos );

hndlp是一个输入参数,是要获取参数的句柄,我们在这里使用错误句柄。

htype是一个输入参数,是句柄的类型,这里是OCI_HTYPE_ERROR。

errhp是一个输入/输出参数,是一个错误句柄,用于OCIErrorGet()取回错误信息。

parmdpp是一个输出参数,参数值,在这里我们要取回一个错误句柄。

pos是一个输入参数,这里是错误的位置号,从0开始。

还是用前面数组插入的例子,看看用OCI_BATCH_ERRORS模式怎样处理错误。

​​OCIEnv		*envhp = NULL;
OCIError	*errhp = NULL;
OCIServer	*svrhp = NULL;
OCISession	*usrhp = NULL;
OCISvcCtx	*svchp = NULL;
OCIStmt		*smthp = NULL;

OCIError    *errhp1 = NULL;
OCIError    *errhp2 = NULL;

/* 数组插入10条数据 */
int insert_array_batch(void)
{
    int     i;
    sword	rc;
    int		slen;
    ub4     num_errs;
    ub4     row_off;
    sb4     ecode;
    sb2		ind_id[10];
    sb2		ind_name[10];
    sb2		ind_addr[10];
    ub2		alen_id[10];
    ub2		alen_name[10];
    ub2		alen_addr[10];
    ub2		rcode_id[10];
    ub2		rcode_name[10];
    ub2		rcode_addr[10];
    int32_t	id[10];
    char	name[10][32];
    char	addr[10][256];
    OCIBind	*bndp;
    char	sqltxt[1024];
    char    errmsg1[16384];

    /* 分配OCI语句句柄 */
    rc = OCIHandleAlloc(
        (void *)envhp,
        (void **)&smthp,
        OCI_HTYPE_STMT,
        0,
        (void **)NULL
    );

    if (rc != OCI_SUCCESS) {
        fprintf(stderr, "OCIHandleAlloc() - allocate statement handle error !\n");
        return (-1);
    }

    /* 生成SQL语句文本 */
    strcpy(sqltxt, "INSERT INTO test_tab (ID, NAME, ADDR) VALUES (:1, :2, :3)");
    slen = strlen(sqltxt);

    /* 准备语句 */
    if (check_oci_error(errhp,
        OCIStmtPrepare(smthp, errhp, (const OraText *)sqltxt, slen,
            OCI_NTV_SYNTAX, OCI_DEFAULT)) < 0)
        return (-1);

    /* 绑定第一个占位符ID,绑定数组地址 */
    if (check_oci_error(errhp,
        OCIBindByPos((OCIStmt *)smthp,
            (OCIBind **)&bndp,
            errhp,
            (ub4)1,	            /* position */
            (void *)id,         /* valuep */
            (sb4)4,             /* value_sz */
            (ub2)SQLT_INT,      /* dty */
            (void *)ind_id,     /* indp */
            (ub2 *)alen_id,     /* alenp */
            (ub2 *)rcode_id,    /* column return code pointer */
            (ub4)0,             /* maxarr_len */
            (ub4 *)NULL,        /* curelep */
            (ub4)OCI_DEFAULT)   /* mode */
        ) < 0)
        return (-1);

    /* 指定绑定参数跳过的字节数 */
    if (check_oci_error(errhp,
        OCIBindArrayOfStruct(bndp,
            sizeof(int32_t),
            sizeof(sb2),
            sizeof(ub2),
            sizeof(ub2))
        ) < 0)
        return (-1);

    /* 绑定第二个占位符NAME */
    if (check_oci_error(errhp,
        OCIBindByPos((OCIStmt *)smthp,
            (OCIBind **)&bndp,
            errhp,
            (ub4)2,             /* position */
            (void *)name,       /* valuep */
            (sb4)30,            /* value_sz */
            (ub2)SQLT_STR,      /* dty */
            (void *)ind_name,   /* indp */
            (ub2 *)alen_name,   /* alenp */
            (ub2 *)rcode_name,  /* column return code pointer */
            (ub4)0,             /* maxarr_len */
            (ub4 *)NULL,        /* curelep */
            (ub4)OCI_DEFAULT)   /* mode */
        ) < 0)
        return (-1);

    /* 指定绑定参数跳过的字节数,name定义的长度为32 */
    if (check_oci_error(errhp,
        OCIBindArrayOfStruct(bndp,
            32,
            sizeof(sb2),
            sizeof(ub2),
            sizeof(ub2))
        ) < 0)
        return (-1);

    /* 绑定第三个占位符ADDR */
    if (check_oci_error(errhp,
        OCIBindByPos((OCIStmt *)smthp,
            (OCIBind **)&bndp,
            errhp,
            (ub4)3,             /* position */
            (void *)addr,       /* valuep */
            (sb4)200,           /* value_sz */
            (ub2)SQLT_STR,      /* dty */
            (void *)ind_addr,   /* indp */
            (ub2 *)alen_addr,   /* alenp */
            (ub2 *)rcode_addr,  /* column return code pointer */
            (ub4)0,             /* maxarr_len */
            (ub4 *)NULL,        /* curelep */
            (ub4)OCI_DEFAULT)   /* mode */
        ) < 0)
        return (-1);

    /* 指定绑定参数跳过的字节数,addr定义的长度为256 */
    if (check_oci_error(errhp,
        OCIBindArrayOfStruct(bndp,
            256,
            sizeof(sb2),
            sizeof(ub2),
            sizeof(ub2))
        ) < 0)
        return (-1);

    /* 赋值绑定的变量数据 */
    for (i=0; i<10; i++) {
        id[i] = i+10;
        memset(name[i], 'a', 10);
        memset(addr[i], 'b', 20);

        name[i][10] = '\0';
        addr[i][20] = '\0';

        /* 指示符赋值为0,插入非NULL数据 */
        ind_id[i]    = 0;
        ind_name[i]  = 0;
        ind_addr[i]  = 0;

        /* 赋值变量的真实数据长度 */
        alen_id[i]   = sizeof(int32_t);
        alen_name[i] = strlen(name[i]) + 1;
        alen_addr[i] = strlen(addr[i]) + 1;
    }

    /* 执行OCI语句,注意这里要用OCI_BATCH_ERRORS模式 */
    if (check_oci_error(errhp,
        OCIStmtExecute(svchp,
            smthp,              /* stmthp */
            errhp,              /* errhp */
            10,                 /* iters */
            0,                  /* rowoff */
            NULL,               /* snap_in */
            NULL,               /* snap_out */
            OCI_BATCH_ERRORS)   /* mode */
        ) < 0)
        return (-1);

    /* 分配错误句柄errhp1,用于取回错误条数 */
    if (check_oci_error(errhp,
        OCIHandleAlloc((const void *)envhp, (void **)&errhp1,
            OCI_HTYPE_ERROR, 0, (void **)NULL)) < 0)
        return (-1);

    if (check_oci_error(errhp,
        OCIAttrGet (smthp, OCI_HTYPE_STMT, &num_errs, 0,
            OCI_ATTR_NUM_DML_ERRORS, errhp1)) < 0)
        return (-1);

    if (num_errs) {
        /* 分配错误句柄errhp2 */
        if (check_oci_error(errhp,
            OCIHandleAlloc((const void *)envhp, (void **)&errhp2,
                OCI_HTYPE_ERROR, 0, (void **)NULL)) < 0)
            return (-1);

        for (i=0; i<num_errs; i++) {
            if (check_oci_error(errhp,
                OCIParamGet(errhp, OCI_HTYPE_ERROR, errhp1, &errhp2, i)) < 0)
                return (-1);

            if (check_oci_error(errhp,
                OCIAttrGet (errhp2, OCI_HTYPE_ERROR, &row_off, 0,
                    OCI_ATTR_DML_ROW_OFFSET, errhp1)) < 0)
                return (-1);

            OCIErrorGet(errhp2, 1, NULL, &ecode, (OraText *)errmsg1,
                16383, OCI_HTYPE_ERROR);

            fprintf(stderr, "row[%d] error: %d-%s\n", row_off, ecode, errmsg1);
            fprintf(stderr, "ID=%d, NAME=%s, ADDR=%s\n",
                id[row_off], name[row_off], addr[row_off]);
        }
    }

    /* 提交改变的数据 */
    if (check_oci_error(errhp,
        OCITransCommit(svchp, errhp, OCI_DEFAULT)) < 0)
        return (-1);

    return (0);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值