文章目录
第五章:高级SQL
5.1 使用程序设计语言访问数据库
可以使用以下两种方法通过编程语言访问SQL
- 动态SQL:动态SQL允许运行时以字符串形式构建SQL,提交查询和更新
- 嵌入式SQL:嵌入式SQL必须在编译时全部确定,并交给预处理器
动态SQL中分为JDBC和ODBC
JDBC:Java语言开发的应用接口
ODBC:C语言,C++,C#等
5.1.1 JDBC
JDBC连接MySQL数据库的例子
public static boolean loginSuccess(String name,String password)
{
//连接数据库
Connection coon = null;
Statement stmt = null;
ResultSet rs = null;
try
{
//注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//获取连接
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/database_name?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true","root","123");
//获取数据库操作对象
stmt = coon.createStatement();
//执行sql语句,从login表中查找数据
String selectSql = "select * from login";
rs = stmt.executeQuery(selectSql);
//处理查询结果集
while(rs.next())
{
//如果账号密码相同并且没有登录时,就返回真
if(name.equals(rs.getString("name")) && password.equals(rs.getString("password")) && rs.getInt("isonline") == 0)
{
return true;
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
//释放资源
if(rs != null)
{
try
{
rs.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
if(stmt != null)
{
try
{
stmt.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
if(coon != null)
{
try
{
coon.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
return false;
}
预备语句
可以用"?"代表以后给出的实际值
可以使用PreparedStatement,提高应用程序的安全性,防止SQL注入
SQL注入
假设下面的查询
"select * from instructor where name = '" + name + "'"
如果用户不是输入名字,而是输入:'X' or 'Y' = Y'
那么where子句始终为真,则会返回整张instructor表,这会导致信息泄露
使用预备语句可以解决这类问题,因为输入的字符串将被插入转义字符
变为:
"select * from instructor where name = 'X\ ' or \ 'Y\ ' = \ 'Y'"
返回的结果为空集
元数据
1.ResultSetMetaData对象
当执行完一个executeQuery方法后,查询的结果被封装在ResultSet数据集中
ResultSet接口有一个getMataData()方法,返回包含结果集元数据的ResultSetMetaData对象
ResultSetMetaData rsmd = rs.getMetaData();
for(int i = 1; i <= rsmd.getColumnCount(); i++)
{
System.out.println(rsmd.getColumnName(i));
System.out.println(rsmd.getColumnTypeName(i));
}
getColumnCount()方法返回结果关系的属性个数
对于每一个属性,可以使用getColumnName()和getColumnTypeName()方法分别得到名称和数据类型
2.DatabaseMetadata接口
Connection接口通过getMetadata()方法用于返回一个DatabaseMetadata对象
DatabaseMetadata接口有大量的方法可以用于获取程序所连接的数据库和数据库系统的元数据
DatabaseMetadata dbmd = coon.getMetadata();
//getColumns参数:类别,模式名,表名,列名
//返回值:每列包含一行COLUMN_NAME,TYPE_NAME
ResultSet rs = dbmd.getColumns(null,"univdb","department","%");
while(rs.next())
{
System.out.println(rs.getString("COLUMN_NAME"),rs.getString("TYPE_NAME"));
}
JDBC中的事务控制
在默认情况下,每个SQL语句都被作为一个被自动提交的独立的事务
对于有多个更新的事务,这种做法并不好,因此建议将自动提交关闭
conn.setAutoCommit(false);
事务必须被显式的提交或回滚
conn.commit();
conn.rollback();
可以使用conn.setAutoCommit(true)
打开自动提交
5.1.3 嵌入式SQL
SQL标准定义了嵌入SQL到许多不同的语言中,例如C,C++,Java等。SQL查询所嵌入的语言被称为宿主语言,宿主语言中使用的SQL结构被称为嵌入式SQL
使用宿主语言写出的程序可以通过嵌入式SQL的语法访问和修改数据库中的数据
在JDBC中,SQL语句是在运行时被解释的,但是在嵌入式SQL中,一些SQL相关的错误可以在编译过程中被发现
使用EXEC SQL语句,让预处理器识别嵌入式SQL请求
EXEC SQL <嵌入式SQL语句>
连接数据库
EXEC SQL connect to server user user-name using password
server:将要连接的服务器
嵌入式SQL语句中可以使用宿主语言的变量
前面要加上“:”以区别于SQL变量
EXEC SQL BEGIN DECLARE SECTION:
int a;
EXEC SQL END DECLARE SECTION:
为了表示关系查询,使用声明游标语句
宿主变量为a
EXEC SQL
declare c cursor for
select id,name
from student
where tot_cred > :a;
上述表达式中的变量c称为该查询的游标,可以用c来标识该查询,然后用open语句执行该查询
EXEC SQL open c;
利用游标更新数据库关系
EXEC SQL
declare c cursor for
select *
from instructor
where dept_name = 'Music'
for update;
EXEC SQL
update instructor
set salary = salary + 100
where current of c;
5.2 函数和过程
SQL允许定义函数,过程和方法
这里介绍的是SQL标准所定义的语法
5.2.1 声明和调用SQL函数和过程
定义一个函数,函数的功能是:给定一个系的名字,返回该系的教师数目
create function dept_count(dept_name varchar(20))
returns integer
begin
declare d_count integer;
select count(*) into d_count
from instructor
where instructor.dept_name = dept_name
return d_count;
end
使用该函数进行查询
select dept_name,budget
from department
where dept_count(dept_name) > 12;
SQL标准支持返回关系作为结果的函数,这种函数称为表函数
create function teacher_of (dno char(20)
returns table (tno varchar(5),
name varchar(20),
salary numeric(8,2))
return table
(select tno, tname, salary
from t
where t.dno = teacher_of.dno)
使用
select *
from table (teacher_of (‘d01’))
5.2.2 支持过程和函数的语言构造
SQL1999支持for循环,允许对查询的所有结果重复执行
declare n integer default 0:
for r as
select budget from department
where dept_name = 'Music'
do
set n = n - r.budget
end for
其它部分略去
5.3 触发器
触发器是一条语句,当对数据库作修改时,它被系统自动执行
设置触发器机制,需要满足两个要求
- 指明什么条件下执行触发器
- 指明触发器执行时的动作
设置职工工资涨幅不能超过10%
create trigger RAISE_LIMIT
after update of sal on EMP
referencing new row as nrow old row as orow
for each row
when (nrow.sal > 1.1 * orow.sal)
begin
signal SQLSTATE '7500' (Salary increase 10%)
end
5.4 递归查询
语法格式
select * from …. where [结果过滤条件语句]
start with [and起始条件过滤语句]
connect by prior [and中间记录过滤条件语句]
例
select *
from p
start with parent='a' connect by prior child=parent;
结果集