C语言开发过程中,需要对数据库进行操作,使用 Oracle 数据库一般会采用Oracle Pro*C/C++ 预编译器。
本文章参考Oracle官方文档和自我实践,实验环境为 RHEL7 和 Oracle19c,详细介绍 Oracle Pro*C/C++ 预编译器,了解它在开发操作 Oracle 数据的应用程序中的作用,并了解它使您的应用程序能够做什么,具体如使用。
宿主变量语法
Oracle 使用宿主变量将数据和状态信息传递给您的程序;程序使用寄主变量将数据传递给 Oracle。
输出与输入寄主变量
根据它们的使用方式,宿主变量被称为输出宿主变量或输入宿主变量。
SELECT 或 FETCH 语句的 INTO 子句中的主变量称为输出寄主变量,因为它们保存 Oracle 输出的列值。Oracle 将列值分配给 INTO 子句中相应的输出寄主变量。
SQL 语句中的所有其他寄主变量都称为输入寄主变量,因为程序将它们的值输入到 Oracle。例如,在 INSERT 语句的 VALUES 子句和 UPDATE 语句的 SET 子句中使用输入宿主变量。它们也用于 WHERE、HAVING 和 FOR 子句中。输入宿主变量可以出现在 SQL 语句中允许使用值或表达式的任何地方。
EXEC SQL SELECT ename, empno INTO :name,:number FROM emp ORDER BY ename;
您不能使用输入宿主变量来提供 SQL 关键字或数据库对象的名称。因此,您不能在数据定义语句(例如 ALTER、CREATE 和 DROP)中使用输入主变量。在以下示例中,DROP TABLE 语句无效:
char table_name[30];
printf("Table name? ");
gets(table_name);
EXEC SQL DROP TABLE :table_name; -- 宿主变量不允许
如果需要在运行时更改数据库对象名称,请使用动态 SQL。 在 Oracle 执行包含输入主变量的 SQL 语句之前,程序必须为它们赋值。一个例子如下:
int emp_number;
char temp[20];
VARCHAR emp_name[20];
/* chong */
printf("Employee number? ");
gets(temp);
emp_number = atoi(temp);
printf("Employee name? ");
gets(emp_name.arr);
emp_name.len = strlen(emp_name.arr);
EXEC SQL INSERT INTO EMP (EMPNO, ENAME)
VALUES (:emp_number, :emp_name);
请注意,INSERT 语句的 VALUES 子句中的输入宿主变量以冒号为前缀。
指标变量语法
可以将任何宿主变量与可选的指示符变量相关联。每次在 SQL 语句中使用宿主变量时,结果代码都存储在其关联的指示符变量中。因此,指示器变量可以监视宿主变量。
您可以在 VALUES 或 SET 子句中使用指示符变量将 NULL 分配给输入宿主变量。在 INTO 子句中使用指示符变量来检测输出宿主变量中的 NULL 或截断值。
输入时
您的程序可以分配给指示变量的值具有以下含义:
多变的 | 描述 |
---|---|
-1 | Oracle 会为该列分配一个 NULL,忽略宿主变量的值。 |
>=0 | Oracle 会将宿主变量的值分配给该列。 |
在输出
Oracle 可以分配给指示变量的值具有以下含义:
多变的 | 描述 |
---|---|
-1 | 列值为 NULL,因此宿主变量的值是不确定的。 |
0 | Oracle 为宿主变量分配了一个完整的列值。 |
>0 | Oracle 为宿主变量分配了一个截断的列值。指示器变量返回的整数是列值的原始长度,SQLCA 中的 SQLCODE 设置为零。 |
-2 | Oracle 将截断的列变量分配给主变量,但无法确定原始列值(例如 LONG 列)。 |
请记住,指示符变量必须定义为 2 字节整数,并且在 SQL 语句中,必须以冒号为前缀,并且必须紧跟在其主变量之后。
插入 NULL
可以使用指标变量来插入 NULL。在 INSERT 之前,对于要为 NULL 的每一列,将适当的指示符变量设置为 -1,如下例所示:
set ind_comm = -1;
EXEC SQL INSERT INTO emp (empno, comm)
VALUES (:emp_number, :commission:ind_comm);
指示符变量ind_comm指定 NULL 将存储在 COMM 列中。
可以改为对 NULL 进行硬编码,如下所示:
EXEC SQL INSERT INTO emp (empno, comm)
VALUES (:emp_number, NULL);
虽然这不太灵活,但它可能更具可读性。通常,有条件地插入 NULL,如下例所示:
printf("Enter employee number or 0 if not available: ");
scanf("%d", &emp_number);
if (emp_number == 0)
ind_empnum = -1;
else
ind_empnum = 0;
EXEC SQL INSERT INTO emp (empno, sal)
VALUES (:emp_number:ind_empnum, :salary);
返回 NULL
还可以使用指示符变量来操作返回的 NULL,如以下示例所示:
EXEC SQL SELECT ename, sal, comm
INTO :emp_name, :salary, :commission:ind_comm
FROM emp
WHERE empno = :emp_number;
if (ind_comm == -1)
pay = salary; /* commission is NULL; ignore it */
else
pay = salary + commission;
获取 NULL
当 DBMS=V7 或 DBMS=V8 时,如果您 SELECT 或 FETCH NULL 到与指示符变量无关的主变量中,Oracle 会发出以下错误消息:
ORA-01405: fetched column value is NULL
测试 NULL
可以在 WHERE 子句中使用指示符变量来测试 NULL,如以下示例所示:
EXEC SQL SELECT ename, sal
INTO :emp_name, :salary
FROM emp
WHERE :commission INDICATOR :ind_comm IS NULL ...
但是,不能使用关系运算符将 NULL 相互比较或与其他值进行比较。例如,如果 COMM 列包含一个或多个 NULL,则以下 SELECT 语句将失败:
EXEC SQL SELECT ename, sal
INTO :emp_name, :salary
FROM emp
WHERE comm = :commission;
下一个示例显示了当其中一些值可能为 NULL 时如何比较值是否相等:
EXEC SQL SELECT ename, sal
INTO :emp_name, :salary
FROM emp
WHERE (comm = :commission) OR ((comm IS NULL) AND
(:commission INDICATOR :ind_comm IS NULL));