Chapter 5 Advanced SQL¶
Accessing SQL from a Programming Language¶
- 并非所有查询都可以用SQL来表示,因为
- 人机交互时使用图形界面、发送语音图像等,无法只通过SQL来实现
从通用编程语言中调用SQL主要有两种方式: 1. API(类似于库函数) 2. Embedded SQL + 将SQL语句嵌入到编程语言中,编译时会转换为函数调用 + 在运行时,这些函数调用会通过API来执行SQL语句
JDBC & ODBC¶
二者都是程序与数据库之间的API
- ODBC(Open Database Connectivity): with C, C++, C#
- JDBC(Java Database Connectivity): with Java
应用程序通过调用: + 和 Database server 连接 + 将SQL指令发送给database server + 将结果元组one by one返回给应用程序
JDBC¶
- JDBC支持元数据检索,可以获取数据库的schema相关信息
- Model for communicating with databases:
- Open a connection to the database
- Create a statement object
- Execute SQL queries using the statement object to send queries and fetch results
- Exception mechanism to handle errors
update
execute and fetch
Details
Prepared Statements¶
SQL语句中某些参数可以在运行过程中再确定,这时候可以使用prepared statement
Eg
PreparedStatement pstmt = conn.prepareStatement("insert into instructor values (?, ?, ?, ?)");
pstmt.setString(1, "10101"); // set first parameter to 10101
pstmt.setString(2, "Srinivasan");
pstmt.setString(3, "Comp. Sci.");
pstmt.setInt(4, 65000);
pstmt.executeUpdate();
?
是占位符,可以在运行时再确定
- Never use string concatenation to build SQL queries
SQL Injection
String name = "Srinivasan";
stmt.executeUpdate("insert into instructor values ('10101', '" + name + "', 'Comp. Sci.', 65000)");
name
是用户输入的,那么用户可以通过输入' or 1=1 --
来绕过验证,造成SQL注入
Metadata Features¶
将ResultSetMetaData
对象放在ResultSet
对象中,可以获取元数据信息
Eg
Transaction Control in JDBC¶
- 默认情况下,每个SQL语句都是一个transaction
conn.setAutoCommit(false)
可以关闭自动提交,需要手动提交conn.commit()
提交conn.rollback()
回滚
SQLJ¶
SQLJ是Java的一个扩展,可以将SQL语句嵌入到Java程序中
#sql iterator InstructorIterator {
String ID;
String name;
String dept_name;
int salary;
};
while (instructorIterator.next()) {
String deptName = iterator.dept_name();
int salary = iterator.salary();
System.out.println(deptName + " " + salary);
}
iterator.close();
ODBC¶
每个支持ODBC的数据库都有一个"driver" library,与客户端程序连接

Eg
int ODBCexample(){
RETCODE rc;
HENV henv; // environment handle
HDBC hdbc; // connection handle
HSTMT hstmt; // statement handle
SQLAllocEnv(&henv); // allocate environment handle
SQLAllocConnect(henv, &hdbc); // allocate connection handle
SQLConnect(hdbc, "univdb", "user", SQL_NTS, // "password"); // connect to database
{//do actual work}
SQLDisconnect(hdbc); // disconnect from database
SQLFreeConnect(hdbc); // free connection handle
SQLFreeEnv(henv); // free environment handle
}
其中`SQL_NTS`表示字符串以null结尾
SQLExecDirect
- 程序将SQL语句发送给数据库SQLFetch
- 从数据库中获取结果SQLBindCol
- 将结果绑定到变量上
main body of program
char deptName[80];
float salary;
int lenOut1, lenOut2;
HSTMT hstmt;
char *sqlquery = "select dept_name, sum(salary) from instructor group by dept_name";
SQLAllocStmt(hdbc, &hstmt);
err = SQLExecDirect(hstmt, sqlquery, SQL_NTS);
if (err == SQL_SUCCESS){
SQLBindCol(hstmt, 1, SQL_C_CHAR, deptName, 80, &lenOut1);
SQLBindCol(hstmt, 2, SQL_C_FLOAT, &salary, 0, &lenOut2);
while (SQLFetch(hstmt) != SQL_NO_DATA){
printf("%s %f\n", deptName, salary);
}
}
SQLFreeStmt(hstmt, SQL_DROP);
Prepared Statements¶
SQLPrepare
- 准备SQL语句SQLBindParameter
- 绑定参数SQLExecute
- 执行
Eg
char *sqlquery = "insert into instructor values (?, ?, ?, ?)";
SQLPrepare(hstmt, sqlquery, SQL_NTS);
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, "10101", 0, NULL);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, "Srinivasan", 0, NULL);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, "Comp. Sci.", 0, NULL);
SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, 65000, 0, NULL);
SQLExecute(hstmt);
More on ODBC¶
- 也支持元数据检索
- Transaction control
- 默认情况下,每个SQL语句都是一个transaction
- SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, 0)关闭自动提交
- SQLTransact(hdbc, SQL_COMMIT)提交
- SQLTransact(hdbc, SQL_ROLLBACK)回滚
Embedded SQL¶
- SQL嵌入的语言称为host language
-
在C中,可以使用
EXEC SQL
来嵌入SQL语句 -
嵌入时,主要有三个问题要考虑:
- 标记SQL语句的开始和结束:
EXEC SQL<SQL statement>END-EXEC
- database 和 programming language 之间的通信:
SQLCA
和SQLDA
结构 - 数据类型的转换:例如 SQL中
Date
转换为C中的char(12)
- 标记SQL语句的开始和结束:
Eg
main(){
EXEC SQL INCLUDE SQLCA; //声明段开始
EXEC SQL BEGIN DECLARE SECTION;
char account_no [11]; //host variables(宿主变量)声明
char branch_name [16];
int balance;
EXEC SQL END DECLARE SECTION;//声明段结束
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
scanf (“%s %s %d”, account_no, branch_name, balance);
EXEC SQL insert into account
values (:account_no, :branch_name, :balance);
If (SQLCA.sqlcode ! = 0) printf ( “Error!\n”);
else printf (“Success!\n”);
}
main()
{ EXEC SQL INCLUDE SQLCA; //声明段开始
EXEC SQL BEGIN DECLARE SECTION;
char account_no [11]; //host variables(宿主变量)声明
EXEC SQL END DECLARE SECTION;//声明段结束
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
scanf (“%s”, account_no);
EXEC SQL delete from account where
account_number=:account_no;
If (SQLCA.sqlcode ! = 0) printf ( “Error!\n”);
else printf (“Success!\n”);
}
main( )
{ EXEC SQL INCLUDE SQLCA; //声明段开始
EXEC SQL BEGIN DECLARE SECTION;
char account_no [11]; //host variables(宿主变量)声明
int balance;
EXEC SQL END DECLARE SECTION;//声明段结束
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
scanf (“%s %d”, account_no, balance);
EXEC SQL update account
set balance= balance+:balance
where account_number = :account_no;
If (SQLCA sqlcode ! = 0) printf ( “Error!\n”);
else printf (“Success!\n”);
}
main( )
{ EXEC SQL INCLUDE SQLCA; //声明段开始
EXEC SQL BEGIN DECLARE SECTION;
char account_no [11]; //host variables(宿主变量)声明
int balance;
EXEC SQL END DECLARE SECTION;//声明段结束
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
scanf (“%s ”, account_no);
EXEC SQL select balance into :balance
from account
where account_number = :account_no;
If (SQLCA sqlcode ! = 0) printf ( “Error!\n”);
else printf (“balance= %d \n”, balance);
}
main( )
{ EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
char customer_name[21];
char account_no [11];
int balance;
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
EXEC SQL DECLARE account_cursor CURSOR for
select account_number, balance
from depositor natural join account
where depositor.customer_name = : customer_name;
scanf (“%s”, customer_name);
EXEC SQL open account_cursor;
for (; ;)
{ EXEC SQL fetch account_cursor into :account_no, :balance;
if (SQLCA.sqlcode!=0)
break;
printf( “%s %d \ n”, account_no, balance);
}
EXEC SQL close account_cursor;
}
main()
{ EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
char account_no [11];
int balance;
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO bank_db USER Adam Using Eve;
scanf (“%s”, account_no);
EXEC SQL select balance into :balance
from account
where account_number = :account_no;
If (SQLCA sqlcode ! = 0) printf ( “Error!\n”);
else printf (“balance= %d \n”, balance);
EXEC SQL update account
set balance= balance+100
where current of account_cursor;
}
Procedural Extension and Stored Procedures¶
- SQL支持模块化的语言特性:
- 允许使用if-then-else、while、loop等控制结构来定义procedure
- Stored Procedure
- 在数据库中存储procedures
- 使用call语句调用从而执行
- 允许外部应用操作数据库,而不需要知道内部细节
Functions and Procedures¶
- Function
- 有返回值,返回值可以是一个标量值或者一个表
- 可以用在SQL语句中
function
returns
- 返回值类型declare
- 声明变量select ... into
- 将查询结果赋值给变量 首先进行语法检查和查询优化,然后转换为关系代数表达式,再生成目标代码,储存在数据库中
具体的调用例子如:
- Procedure
- 通常执行操作而不是返回结果
- 有输入参数和输出参数(in, out)
- 需要使用
call
语句来调用
procedure
function中的例子可以改为procedure
调用:procedure constructs
External Language Functions/Procedures¶
允许在SQL中调用外部函数,例如C、Java等,例如:
create procedure dept_count_proc(in dept_name varchar(20), out count integer)
language C
external name '/usr/avi/bin/dept_count_proc';
return integer
language C
external name '/usr/avi/bin/dept_count';
Triggers¶
- 触发器是一种在修改数据时自动执行的机制
- ECA rule:
- Event: insert, update, delete, etc.
- Condition: when, where, etc.
- Action: sql statement
trigger
```sql
record the transaction in account_log if the balance changes by more than 1000¶
create trigger account_trigger after update of account on balance referencing new row as nrow referencing old row as orow for each row when nrow.balance - orow.balance > 1000 or orow.balance - nrow.balance > 1000 begin insert into account_log values (nrow.account_no, nrow.balance - orow.balance, current_time()); end;
trigger 也可以用来检验数据的完整性,例如:
create trigger timeslot_check after insert on section
referencing new row as nrow
for each row
when(
nrom.time_slot_id not in(
select time_slot_id
from time_slot
)
)
begin
rollback;
end;
referencing
- 引用新旧行referencing new row as nrow
: for delete, updatereferencing old row as orow
: for update, delete
for each row
- 每次修改一行
Statement-Level Triggers¶
for each row
- 针对每一行受影响的数据触发for each statement
- 针对每一条SQL语句触发,即只触发一次referencing new table as ntab
- 引用新表
创建日期: 2025年3月20日 12:30:55