静态SQL和动态SQL
引⾔
SQL 语⾔作为标准的查询语⾔,⼏乎被所有的数据库管理系统 (DBMS) 所⽀持,并成为国际标准。标准的 SQL 语⾔⼀般包括三类,即 DDL (Data Definition Language, 数据描述语⾔ ) 、DML (Data Manipulation Language, 数据操纵语⾔ ) 和 DCL(Data Control Language,数据控制语⾔ )。通过这些标准的 SQL 语句,使得各种数据库能以⼀种较为统⼀的⽅式被访问。
DB2(本⽂以下专指 DB2 UDB for Linux, Unix 和 Windows 版本)允许⽤户通过多种编程接⼝发送 SQL 语句到数据库引擎,然后由引擎统⼀编译并且运⾏。SQL 语句从编译和运⾏的⾓度可以分为两种,静态 SQL和动态 SQL,这两种 SQL 在使⽤⽅式、运⾏机制和性能表现等⽅⾯各有特点 :
静态 SQL:静态 SQL 语句⼀般⽤于嵌⼊式 SQL 应⽤中,在程序运⾏前,SQL 语句必须是确定的,例如 SQL 语句中涉及的列名和表名必须是存在的。静态 SQL 语句的编译是在应⽤程序运⾏前进⾏的,编译的结果会存储在数据库内部。⽽后程序运⾏时,数据库将直接执⾏编译好的 SQL 语句,降低运⾏时的开销。静态SQL在编译时已经确定了引⽤的表和列。宿主变量不改变表和列信息。可以使⽤主变量改变查询参数值,但是不能⽤主变量代替表名或列名。
动态 SQL:动态 SQL 语句是在应⽤程序运⾏时被编译和执⾏的,不在编译时确定 SQL 的表和列,⽽是让程序在运⾏时提供,并将
SQL 语句⽂本传给 DBMS 执⾏。静态 SQL 语句在编译时已经⽣成执⾏计划。⽽动态 SQL 语句,只有在执⾏时才产⽣执⾏计划。动态 SQL 语句⾸先执⾏ PREPARE 语句要求 DBMS 分析、确认和优化语句,并为其⽣成执⾏计划。例如,使⽤ DB2 的交互式⼯具 CLP 访问数据库时,⽤户输⼊的 SQL 语句是不确定的,因此 SQL 语句只能被动态地编译。动态 SQL 的应⽤较多,常见的 CLI 和 JDBC 应⽤程序都使⽤动态 SQL。
下表列举了静态 SQL 和动态 SQL 的⽐较结果。
静态 SQL动态 SQL
SQL 语句直接嵌⼊到宿主编程语⾔,程序需要预编译处理这些嵌⼊的 SQL 语句SQL 语句⼀般作为宿主语⾔的变量出现。嵌⼊式动态 SQL 应⽤需要预编译,⾮嵌⼊式 SQL 应⽤则⽆需预编译
SQL 语句在程序被编译时已知,涉及的数据库对象已存在SQL 语句在程序被编译时未知,涉及的数据库对象可以是运⾏时才创建的
SQL 语句在程序运⾏前被编译SQL 语句在程序运⾏时被编译
SQL 语句的编译结果在 DB2 的⽬录 (catalog) 中持久化保存SQL 语句的编译结果缓存在数据库的内存⾥
运⾏时仅读取⽬录 (catalog)运⾏时编译 SQL 语句需对⽬录 (catalog) 加锁
SQL 语句的优化是根据编译时的数据库统计信息进⾏的,不能完
全反映运⾏时的情况
SQL 语句的优化是根运⾏时的数据库统计信息进⾏的
对 SQL 语句所访问的数据对象的权限检查是在绑定时进⾏的对 SQL 语句所访问的数据对象的权限检查是在运⾏时进⾏的
权限控制的粒度是包(package,⼀组 SQL 语句的编译结果),⽤户
仅需要访问包的权限
权限控制的粒度是 SQL 语句,⽤户需要具有访问 SQL 语句中每个数据对象的权限
如果 SQL 语句中的对象被修改,如 DDL 执⾏,整个包都需要重
新绑定
当 SQL 语句中的对象被修改时,仅执⾏过的语句在下次运⾏时需要重新编译
根据编程⽅法的不同,DB2 的应⽤程序开还可以分为嵌⼊式 SQL 编程和⾮嵌⼊式编程 :
嵌⼊式 SQL 编程将 SQL 语句嵌⼊到宿主语⾔ (host) 的程序中,例如 C/C++ 程序。因为宿主语⾔不识别 SQL 语句,先要对程序进⾏预编译,把SQL 语句转变为对 DB2 服务的调⽤,并重写源代码,最后再使⽤宿主语⾔的编译器对应⽤程序进⾏编译。嵌⼊式 SQL 都需要被绑定到特定的数据库中,可分为嵌⼊式静态 SQL 和嵌⼊式动态 SQL。
⾮嵌⼊式应⽤程序不需要预编译,且⽅法较多,如 CLI、JDBC、ODBC、ADO.NET 等等,这些⽅法中都使⽤动态 SQL。列举了常见的 DB2 编程接⼝。
编程接⼝静态 / 动态是否为嵌⼊式
嵌⼊式 SQL静态和动态是
DB2 CLI动态否
SQLJ静态是
JDBC动态否数据库优化sql语句
ADO.NET,OLE DB动态否
Perl DBI动态否
PDO(PHP 数据对象 )动态否
在下⾯的⼏个章节中,我们将陆续从使⽤⾓度上描述静态和动态 SQL 在各种编程接⼝中的应⽤,并运⽤⼀些实例来介绍在具体的场景中如何选择。
静态SQL应⽤
嵌⼊式静态SQL
⽆论是嵌⼊式静态 SQL 还是嵌⼊式动态 SQL,都需要先进⾏预编译,并绑定到特定的数据库。DB2 的嵌⼊式 SQL 应⽤程序⽀持以下⼏种语⾔:C,C++,COBOL,FORTRAN 和 REXX?。
对嵌⼊式静态 SQL ⽽⾔,只能使⽤编译时确定的 SQL 语句和访问编译时已经存在的数据库对象。是⼀个查询表的例⼦,使⽤ C 语⾔ :
/
/test.sqc
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
sqlint32 t_seq = 0;
char t_name[64]={0};
EXEC SQL END DECLARE SECTION;
...
t_seq = 5;
EXEC SQL SELECT c_name INTO :t_name FROM test_tbl WHERE seq=:t_seq;
printf("c_name = %s \n", t_name);
使⽤下⾯的命令,对上述代码进⾏预编译和编译:
# 需要先连接数据库
db2 connect to TESTDB
# 使⽤ PREP 命令对 sqc 源⽂件进⾏预编译,这将⽣成 test.c 源⽂件
db2 PREP test.sqc
# 使⽤ C 编译器对 test.c 进⾏编译
xlC -q64 -I$DB2PATH/include -g -L$DB2PATH/lib -ldb2 -o test test.c
SQLJ
SQLJ 是应⽤于静态 SQL 的 Java 编程接⼝。使⽤ SQLJ 编写应⽤程序和使⽤其他的语⾔接⼝相似,⼀个典型的 SQLJ 应⽤程序主要包括以下⼏个⽅⾯:
载⼊包含 SQLJ 和 JDBC 的 Java 包。
定义收发数据的载体变量。
连接⾄数据库,运⾏相应的 SQL 语句并且正确处理错误情况,最后从数据库断开。
是 SQLJ 中执⾏ SELECT 语句的代码⽚断。
// 载⼊相关包
import sqlj.runtime.*;
import java.sql.*;
// 连接⾄数据库
Connection con0 = Connection(url);
// 执⾏相关的 SQL 语句
#sql [ctx] iter = {SELECT NAME FROM EMP};
// 得到结果
while (()) { System.out.println(iter.LASTNAME()); }
在 SQLJ 应⽤程序中,可以使⽤ ExecutionContext 类去控制和监控 SQL 语句的执⾏,如所⽰。
/
/ 分配存储执⾏上下⽂的变量
ExecutionContext exeCtx=new ExecutionContext();
// 关联变量和要执⾏的语句
#sql [connCtx, exeCtx] {DELETE FROM EMP WHERE SALARY > 10000};
// 获取结果
System.out.println("Deleted " + UpdateCount() + " rows");
动态SQL应⽤
嵌⼊式动态SQL
与嵌⼊式静态 SQL 相同,嵌⼊式动态 SQL 也需要预编译。不同的是,嵌⼊式动态 SQL 将 SQL 语句存放在宿主语⾔的字符型变量中,这样的 SQL 语句在预编译时是不被处理的,⽽是被当作主机变量对待,直到程序运⾏时才被编译执⾏。
得益于动态 SQL 的优点,嵌⼊式动态 SQL 可以处理运⾏时才确定的 SQL 语句,例如由程序运⾏时拼
接的 SQL 语句。为了处理返回结果未知的 SELECT 语句,嵌⼊式动态 SQL 使⽤ SQLDA(SQL descriptor area) 结构和 DESCRIBE 语句获取结果集的结构和属性。HEADER 描述整个结果集的信息,⽽每个 SQLVAR 结构描述结果集中⼀个字段的信息。
展⽰了如何使⽤ SQLDA 结构和 DESCRIBE 语句处理 SELECT 语句。
//test1.sqc
// 声明两个 SQLDA 指针,minsqlda 将是⼀个最⼩的 SQLDA 结构,⽤于 PREPARE 语句,
// 此时结果集的字段数量未知,所以只需⼀个最⼩的 SQLDA,即包含 HEADER 和⼀个 SQLVAR
struct sqlda * minsqlda = new sqlda;
struct sqlda * fulsqlda = NULL;
strcpy(hostVarStmt, "SELECT name FROM TEST_TBL");
// PREPARE 将填写 minsqlda 的 header,sqldabc 为 SQLDA 总长度,sqln 为 SQLVAR 数量,即字段数量
EXEC SQL PREPARE STMT INTO :*minsqlda FROM :hostVarStmt;
// 根据从 minsqlda 中获取的长度,分配完整的 SQLDA 结构 fulsqlda,其中将包括合适数量的 SQLVAR 结构
fulsqlda = (struct sqlda *)malloc(SQLDASIZE(minsqlda->sqln));
// 使⽤ DESCRIBE 语句,获取结果集中每个字段的描述信息,包括各字段的类型 (sqltype) 和长度 (sqllen)
EXEC SQL DESCRIBE STMT INTO :fulsqlda;
Loop {
// 根据每个字段的长度,分配内存,将地址存储在对应 SQLVAR 的 sqldata 中
}
// 声明游标
EXEC SQL DECLARE c1 CURSOR FOR STMT;
EXEC SQL OPEN c1;
// 读取记录,记录中每个字段的内容将写⼊ fulsqlda 中对应 SQLVAR 结构的 sqldata 指向的内存
EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
// 循环读取所有记录
while (sqlca.sqlcode != 100) {
EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
}
EXEC SQL CLOSE c1;
DB2 CLI
DB2 CLI(Call Level Interface)基于微软的 ODBC(Open Database Connectivity)标准,同时也增加了 DB2 特有的功能。它允许开发⼈员使⽤ C/C++ 语⾔访问 DB2 并通过函数调⽤将动态 SQL 语句传递给 DB2。DB2 CLI ⼀⽅⾯在 ODBC 的环境中作为 ODBC 驱动被 ODBC 管理器加载,另⼀⽅⾯,
应⽤程序也可以直接使⽤ DB2 CLI API,此时具有更好的性能。展⽰了 CLI 如何执⾏⼀个 DELETE 语句。
/* SQL statements to be executed */
SQLCHAR * stmt1 = (SQLCHAR *)"delete from test1 where col1 = 5";
/* directly execute statement 1 */
cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
对⼀个返回结果未知的 SELECT 查询语句,需要使⽤相关的 CLI API 函数动态地获取对结果集的描述,并取回数据。
与嵌⼊式动态 SQL 应⽤相⽐,CLI 程序的灵活性更强。列举了 CLI 应⽤程序和嵌⼊式动态 SQL 应⽤程序的⽐较。
CLI嵌⼊式动态 SQL
DB2 CLI 应⽤程序使⽤ API 函数发送 SQL 语句,应⽤程序的编译、连接和运⾏独⽴于特定数据库,
即⽆需预编译,也不需要绑定到某个数据库实例。嵌⼊式动态 SQL 应⽤程序需要预编译,并且需要绑定到特定的数据库实例。
CLI 提供的滚动游标(scroll cursor)⽀持前、后向移动⼀⾏或者多⾏记录,⽽移动的
起点可以是第⼀⾏、最后⼀⾏或者之前存储的位置。
只⽀持顺序前向读取游标,并使⽤ FETCH 语句取得记录。
可以获取存储过程调⽤返回的结果集。并⽀持多种 DB2 服务器。可以获取存储过程的输出型或输⼊ - 输出型参数的值,但不能获取存储过程返回的结果集。
只⽀持 C/C++ 语⾔。⽀持 C/C++, FORTRAN、COBOL 和 Java(SQLJ)。
使⽤ CLI API 函数 SQLDescribeCol()、SQLAttribute() 描述未知 SQL 语句结果集的信息,包括结果集字段长度、类型、精度等信息。使⽤ SQLDA 结构和 DESCRIBE 语句获取未知 SQL 语句结果集的字段列表,包括类型、长度等信息。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论