Oracle Pro*C/C++ (七):嵌入式SQL语法

375 阅读4分钟

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 或截断值。

输入时

您的程序可以分配给指示变量的值具有以下含义:

多变的描述
-1Oracle 会为该列分配一个 NULL,忽略宿主变量的值。
>=0Oracle 会将宿主变量的值分配给该列。

在输出

Oracle 可以分配给指示变量的值具有以下含义:

多变的描述
-1列值为 NULL,因此宿主变量的值是不确定的。
0Oracle 为宿主变量分配了一个完整的列值。
>0Oracle 为宿主变量分配了一个截断的列值。指示器变量返回的整数是列值的原始长度,SQLCA 中的 SQLCODE 设置为零。
-2Oracle 将截断的列变量分配给主变量,但无法确定原始列值(例如 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));