数据库系统原理教案

更新时间:2024-04-10 00:16:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

《数据库系统原理》教案

教学内容 教学周次 授课对象 第五章 数据库编程 教材章节 第五章(第5节) 第八章 教学课时 8 计算机科学与技术 信息管理与信息系统 教学环境 多媒体教室 教学目标 本章介绍使用编程方法对数据库进行操纵的编程技术。 教学重点 1、 嵌入式SQL中游标的概念和用法。 2、 存储过程的基本概念、结构和语法。 3、 存储过程的基本概念、类型以及各种触发器的创建。 4、 ODBC的工作流程、使用ODBC开发应用程序。 系统开发中存储过程和触发器的使用。 使用ODBC进行数据库应用程序的设计,实现异构数据库之间的访问。 本章分4次讲述,每次2学时,每次主要讲述以下内容介绍如下: 第一次:嵌入式SQL。 第二次:存储过程。 第三次:触发器。 第四次:ODBC编程。 教学难点 教学过程 作业与要求 P183第6、7、8、9、10、11、13、16、17。 备注

本提交文档内容与次序与实际讲课内容与次序有不一致的地方。 1

第五章 数据库编程

在数据库系统的应用开发中常常使用数据库编程方法对数据库进行操纵,主要包括:嵌入式SQL,SQL的过程化扩展PL/SQL和存储过程,触发器,使用ODBC设计与开发数据库应用系统的方法。

5.1嵌入式SQL

SQL语言有两种不同的使用方式:

*在终端交互方式下使用(作为独立语言在交互环境下使用的SQL语言)

*嵌入到某种高级语言中使用(因为对查询结果不能做进一步处理,利用高级语言的过程性结构来弥补SQL语言在实现复杂应用方面的不足) **主语言(宿主语言):嵌入SQL的高级语言

例:建立我班同学的档案信息

学生表student(sno,sname,sage,ssex,sdept)

EXEC SQL INCLUDE SQLCA; (1)定义SQL通信区 EXEC SQL BEGIN DECLARE SECTION; (2)说明主变量

CHAR sno(8); CHAR sname(20); CHAR ssex(1); CHAR sdept(10); INT sage;

EXEC SQL END DECLARE SECTION; Main() {

gets(sdept); while(1) {

结束控制;

从键盘读入一个学生的数据到主变量中; EXEC SQL INSERT

INTO student(sno,sname,sage,ssex,sdept) VALUES(:sno,:sname,:sage,:ssex,:sdept)

IF (sqlca.sqlcode<>SUCCESS) Break; } }

说明:

1、 区分SQL语言与主语言语句,

所有SQL语句前都必须加前缀EXEC SQL,结束标志随主语言的不同而不同,有些语言用;有写用END-EXEC

2、 嵌入式SQL分两类:说明性语句(主要有定义SQL通信区、说明主变量语句);可执行

语句(数据定义、数据控制、数据操纵)。

2

*数据定义、数据控制只要在SQL语句前加EXEC SQL,其他方面基本上同交互方式下使用的SQL。

*数据操纵,与交互式比因使用主变量有所略有不同。

3、 SQL通信区:是一个数据结构,其中主要包括描述系统当前的工作状态(每条SQL语

句运行成功或失败信息,使主语言能据此控制程序流程)和运行环境的各种数据。如PB的SQLCA称为事物对象,有16个属性,其中10个提供数据库管理系统(DBMS)所需的连接信息,6个用于返回每条SQL语句运行成功或失败信息。其中属性sqlca.sqlcode表示当前SQL操作成功或失败(0成功,100表示SELECT语句找不到符合条件的数据,-1表示SQL操作出错)

4、 主变量:在SQL语句中使用的主语言程序变量。

主变量的主要作用。

*可以指定向数据库中插入的数据。通过输入主变量(由应用程序对其赋值,SQL语句引用)。如上例。

*可以指定WHERE子句或HAVING子句中的条件。 *可以得到SQL语句的结果数据和状态。通过输出主变量(由SQL语句对其赋值,返回给应用程序)。 Givensno=2001;

EXEC SQL SELECT sno,sname,sage,ssex,sdept

INTO :sno,:sname,:sage,:ssex,:sdept FROM student

WHERE sno=:givensno;

查询结果为单记录的SELECT语句说明(P117): *明确结果为单记录(如结果为多记录出错) *增加INTO

*将数据库中的数据修改为指定的值。

EXEC SQL UPDATE Student SET Ssge=sage+:xx

5、 嵌入式SQL语句与主程序间的通信小节

1) 向主语言传递SQL语句的执行状态信息,使主语言能据此控制程序流程。通过SQL

通信区。

2) 主语言向SQL语句提供参数。通过主变量

3) 将SQL语句查询数据库的结果交主语言进一步处理。通过主变量。 6、 使用游标的SQL

因一组主变量一次只能存放一条记录,仅使用主变量不能完全满足SQL语句向应用程序输出数据的要求。

例:查询某个系的学生信息。要查询的系名有用户在程序运行过程中指定,放在主变量deptname.

EXEC SQL INCLUDE SQLCA; (1)定义SQL通信区 EXEC SQL BEGIN DECLARE SECTION; (2)说明主变量

CHAR sno(8); CHAR sname(20); CHAR ssex(1); CHAR sdept(10);

3

INT sage;

EXEC SQL END DECLARE SECTION; Main() {

gets(deptname); // 为主变量赋值

EXEC SQL DECLARE SX CURSOR FOR SELECT sno, sname, sage, ssex

FROM student

WHERE sdept=:deptname; //说明游标

EXEC SQL open SX ; //打开游标:执行SELECT 语句,把所有满足条件的记录取到缓冲区中,游标处于活动状态,指针指向查询结果集中的第一条记录。 WHILE (1) {

EXEC SQL FETCH SX INTO :sno , :sname , :sage , :ssex;

//推进游标,将缓冲区中的当前记录取出来送至主变量供主语言进一步处理。

IF (sqlca.sqlcode<>SUCCESS)

Break; //若所有查询结果处理完或出现SQL语句错误,则推出循环。 /*由主语言作进一步处理*/ }

EXEC SQL CLOSE SX ; //关闭游标:释放结果集占用的缓冲区及其它资源。 }

游标的作用:通过游标把对集合的操作转换为对单记录的处理。

5.2 PL/SQL

PL/SQL是对 SQL的扩展,使其增加了过程化语句功能。PL/SQL程序的基本结构是块,每个块完成一个逻辑操作 1、常量变量的定义 2、常用语句:

? 赋值语句 ? 条件控制语句 ? 循环控制语句

5.3 存储过程

1、存储过程的定义

SQL Server的存储过程类似于编程语言中的“过程”。在使用Transact-SQL语言编程的过程中,将某些需要多次调用的实现某个特定任务的代码段编写成一个过程,将其保存在数据库中,并由SQL Server服务器通过过程名来调用它们,这些过程就叫做存储过程。

存储过程在创建时就被编译和优化,调用一次以后,相关信息就保存在内存中,下次调用时可以直接执行。

存储过程是存储在服务器上的一组预编译的T-SQL 语句,是一种封装重复任务操作的方法,具有强大的编程功能。 2、存储过程的优点

? 实现了模块化编程。

4

? 存储过程具有对数据库立即访问的功能。 ? 使用存储过程可以加快程序的运行速度。

? 使用存储过程可以减少客户机和服务器之间的通信量。 ? 方便实施企业规则。 3、存储过程的创建

使用Transact-SQL语句创建存储过程的语法格式: CREATE PROCEDURE 过程名([参数列表]) AS

<过程体>

例如,CREATE PROCEDURE [dbo].[xselect] AS insert into student (sno,sname,ssex,sage,sdept) values ('11','zhanglu','男',23,'IS')

insert into student (sno,sname, ssex,sage,sdept) values ('12','yangjing','女',22,'CS')

select * from student where sname<>'' 4、存储过程的执行

存储过程创建成功后,保存在数据库中。在SQL Server中可用EXECUTE命令来直接执行存储过程:

execute 存储过程名称 例如:执行前面创建的存储过程xselect, execute xselect

例1,创建一个无参存储过程StuScoreInfo,查询以下信息:班级、学号、姓名、性别、课程名称、考试成绩。

Use StudentDB

If exists (select name from sysobjects where name='StuScoreInfo' and type ='P') Drop procedure StuScoreInfo --删除已存在的存储过程 Go

Create procedure StuScoreInfo as

Select 班级= substring (student.sno, 1, 6), student.sno as 学号, sname as 姓名,ssex as 性别, course.cname as 课程名称, sc.grade as 考试成绩 from student, course, sc where student.sno=sc.sno and course.cno=sc.cno go

执行存储过程:

exec StuScoreInfo;

例2,创建一个带参数的存储过程stu_info,该存储过程根据传入的学生编号在student表中查询此学生的信息。 Use StudentDB

If exists (select name from sysobjects where name='stu_info' and type ='P') Drop procedure stu_info --删除已存在的存储过程 Go

Create procedure stu_info @sno char(5) as

Select 班级= substring (student.sno, 1, 6), student.sno as 学号, sname as 姓名, ssex as 性别, sage as 年龄, sdept as 所在系 from student where sno=@sno

5

go

执行存储过程:

exec stu_info ?20060701?;

5、删除存储过程

删除存储过程的语句格式是: DROP PROCEDURE 过程名(); 例如,

DROP PROCEDURE stu_info (); 6、SQL Server中常用的系统存储过程

(1) sp_addlogin

创建新的 SQL Server 登录,该登录允许用户使用 SQL Server 身份验证连接到 SQL Server 实例。

sp_addlogin [ @loginame = ] 'login' [ , [ @passwd = ] 'password' ] [ , [ @defdb = ] 'database' ] [ , [ @deflanguage = ] 'language' ] [ , [ @sid = ] sid ] [ , [ @encryptopt= ] 'encryption_option' ]

例如,为用户 Victoria 创建 SQL Server 登录,密码为 B1r12-36,并且不指定默认数据库。

EXEC sp_addlogin 'Victoria', 'B1r12-36';

例如,为用户 Albert 创建 SQL Server 登录,密码为 B5432-3M6,默认数据库为StudentDB

EXEC sp_addlogin 'Albert', '''B5432-3M6', StudentDB'; (2) sp_droplogin

删除 Microsoft SQL Server 登录,禁止以该登录名访问 SQL Server 实例。

sp_droplogin [ @loginame= ] 'login' sp_droplogin 调用 DROP LOGIN。

例如,从 SQL Server 实例中删除 Victoria 登录。

sp_droplogin ?Victoria?; (3) sp_adduser

向当前数据库中添加新的用户:

sp_adduser [ @loginame = ] 'login'

[ , [ @name_in_db = ] 'user' ] [ , [ @grpname = ] 'role' ]

例如,使用现有的 SQL Server 登录名 Vidur,将数据库用户 Vidur 添加到当前数据库中的现有 Recruiting 角色。

EXEC sp_adduser ?Vidur?, ?Vidur?, ?Recruiting? ;

例如,将用户 Arvind 添加到 SQL Server 登录名 Arvind 的当前数据库。该用户属于默认的 public 角色。

EXEC sp_adduser ?Arvind? ; (4) sp_dropuser

从当前数据库中删除数据库用户。

sp_dropuser [ sp_dropuser ] 'user'

? sp_dropuser 执行 sp_revokedbaccess 以从当前数据库中删除用户。 ? 使用 sp_helpuser 将显示一个可从当前数据库中删除的用户名的列表。 例如,从当前数据库中删除用户 Albert。

EXEC sp_dropuser 'Albert';

6

(5) sp_addrole

在当前数据库中创建新的数据库角色。

sp_addrole [ @rolename = ] 'role?

[ , [ @ownername = ] 'owner' ]

例如,向当前数据库中添加名为 Managers 的新角色。 EXEC sp_addrole ?Managers? ; (6) sp_droprole

从当前数据库中删除数据库角色:

sp_droprole [ @rolename= ] 'role'

? 使用 sp_droprole 只能删除数据库角色。

? 不能删除带有现有成员的数据库角色。必须删除数据库角色的所有成员,然后才能

删除该数据库角色。若要从角色中删除用户,请使用 sp_droprolemember。 ? 不能删除固定角色及 public 角色。 例如,删除应用程序角色 Sales。

EXEC sp_droprole 'Sales'; (7) sp_grantdbaccess

将数据库用户添加到当前数据库的语句格式:

sp_grantdbaccess [ @loginame = ] 'login'

[ , [ @name_in_db = ] 'name_in_db' [ OUTPUT ] ]

例如,将 Windows 登录名 Edmonds\\LolanSo 的数据库用户添加到当前数据库。新用户名为 Lolan。

EXEC sp_grantdbaccess Lolan FOR LOGIN [Edmonds\\LolanSo]; (8) sp_revokedbaccess

从当前数据库中删除数据库用户的语句格式:

sp_revokedbaccess [ @name_in_db = ] 'name'

例如,从当前数据库中删除映射到 Edmonds\\LolanSo 的数据库用户。

EXEC sp_revokedbaccess 'Edmonds\\LolanSo'; (9) sp_rename

在当前数据库中更改用户创建对象的名称,此对象可以是表、索引、列、别名、数据类型。其语句格式:

sp_rename [ @objname = ] 'object_name' , [ @newname = ] 'new_name' [ , [ @objtype = ] 'object_type' ]

例如,将 SalesTerritory 表重命名为 SalesTerr。

EXEC sp_rename 'Sales.SalesTerritory', 'SalesTerr';

例如,将 SalesTerritory 表中的 TerritoryID 列重命名为 TerrID。

EXEC sp_rename 'Sales.SalesTerritory.TerritoryID', 'TerrID', 'COLUMN'; (10)sp_renamedb

更改数据库的名称的格式:

sp_renamedb [ @dbname = ] 'old_name' , [ @newname = ] 'new_name'

例如,创建 Accounting 数据库,然后将该数据库的名称更改为 Financial。然后,查询 sys.databases 目录视图以确认数据库的新名称。

CREATE DATABASE Accounting;

EXEC sp_renamedb 'Accounting', 'Financial';

SELECT name, database_id FROM sys.databases WHERE name = 'Financial';

7

5.4 触发器

数据库触发器是存放在数据库中的代码,由应用所产生的事件触发。触发器程序运行于数据库服务器上,由于不存在客户端与数据库服务器端的数据传输,因而有较好的执行性能。当用户对有数据库触发器的表执行某种操作时,就会触发对应的触发器工作,完成规定的任务。

触发器实际上就是具有特殊功能的能够自动执行的存储过程。SQL的触发器有3种类型:插入(INSERT)、更新(UPDATE)、删除(DELETE)。

触发器定义规定了触发器的特征和被调用时采取的行动。这些动作被规定在一个或多个SQL语句中(称作被触发SQL语句),可以包括如:更新表、删除数据、调用过程或执行在SQL语句中实现的更多任务。任何对这些语句的限制通常也就是SQL实现方式的限制。

就触发器的执行环境而言,是SQL的执行环境之一。这个执行环境创建在计算机内存中、在语句执行过程中保存语句进程的空间。

每当调用触发器时,就创建了触发器的执行环境。如果调用多个触发器,就会分别为每个触发器创建执行环境。但是,在任何时候,一个会话只有惟一的一个执行环境是活动的。

一个触发器执行环境包含了触发器正确执行所必需的信息,这些信息包括有关触发器本身的细节和触发器所定义的表,即目标表。此外,执行环境还包括一个或两个迁移表,迁移表是虚表,它保存对目标表插入、更新、删除的数据信息。如果更新数据,则创建两个迁移表,一个用于旧数据,一个用于新数据。如果插入数据,则创建一个迁移表来保存新数据。如果删除数据,则创建一个迁移表来保存旧数据。迁移表和触发器环境的信息是实现触发动作的SQL语句执行的依据。

触发器的功能主要表现在:

? 审核修改:可以检测和拒绝数据库中不允许的特定更新操作。

? 级联操作:可以检测通过对参照完整性的定义完成更新、删除操作中的级联处理。 ? 强制互联:可以强制执行比参照完整性更复杂的、通过定义的数据互联关系。 ? 日志管理:可以存储对数据库的增、删、改操作内容,建立数据库日志。 一、触发器的创建

创建触发器的一般语句格式是:

CREATE TRIGGER <触发器名> BEFORE | AFTER INSERT | DELETE | UPDATE [ OF <列名表> ]

ON <表名> [ REFERENCING {OLD [AS] old| NEW [AS] new}] [ FOR EACH ROW | FOR EACH STATEMENT ] [ WHEN(触发条件)] <触发动作体> 说明:

① BEFORE 与 AFTER 确定触发器是在目标表中数据修改语句前(选择BEFORE)调用还是修改语句后(选择AFTER)调用。

② 触发事件:插入(INSERT)、删除(DELETE)、更新(UPDATE),对于更新,还可以通过<列名表>选择触发器应用到哪些列。

③ ON <表名>是定义触发器的表(目标表)。触发事件会导致记录数据的改变,REFERENCING子句是对引用数据的来源与数据迁移的描述,一般用NEW代表新值状态对应的记录,OLD代表旧值状态对应的记录,其中AS可以省略。注意:对INSERT操作来说,不存在“旧”值对应记录;对DELETE操作来说,不存在“新”值对应记录。

④ 触发器按照触发动作的间隔尺寸可以分为行级触发器(FOR EACH ROW)和语句级触发器(FOR EACH STATEMENT),行级触发器表示每次插入、删除、更新一行就调用触

8

发器;而语句级触发器表示每个数据修改语句执行后调用一次,而不论影响到多少行。有的DBMS省缺此子句,表示定义的是语句级触发器。

例如,假设在2.1节SPJ数据库的零件表P上创建一个AFTER UPDATE触发器,若表中有1000条记录,执行如下SQL语句:

UPDATE P SET COLOR=?RED?; 如果该触发器为语句级触发器,那么执行完该语句后触发动作只发生一次,如果是行级触发器,则触发动作将执行1000次。

⑤ WHEN指明触发动作的条件,即触发事件发生后,应满足什么条件才执行该动作。 ⑥ 触发动作体确定触发器所应完成的对相关数据库表的操作,这些操作就是在定义触发器时要求实现的功能。触发动作体既可以是一个PL/SQL程序块,也可以是对已创建存储过程的调用。

MS SQL Server创建触发器的语句格式:

CREATE TRIGGER trigger_name ON { table | view } [ WITH ENCRYPTION ] {

{ { FOR | AFTER | INSTEAD OF }

{ [ INSERT ] [, DELETE ] [, UPDATE ] }

[ WITH APPEND ] [ NOT FOR REPLICATION ] AS

sql_statement [ ...n ] } }

SQL Server支持两种类型的触发器:

? AFTER触发器:指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行

后才激发,只可以建立在表上。

? INSTEAD OF触发器:代替了相应的触发事件而被执行,既可以建立在表上也可以

建立在视图上。当为表或视图定义了针对某一操作(INSERT、DELETE、UPDATE)的INSTEAD OF 类型触发器且执行了相应的操作时,尽管触发器被触发,但相应的操作并不被执行,而运行的仅是触发器SQL 语句本身。INSTEAD-OF触发器在数据库引擎中可以取代数据操作语句而执行。

例如,在SPJ数据库的SPJ表上创建一个AFTER触发器,完成的功能是:在SPJ表上创建一个插入、更新类型的触发器SPJCheck,当在SPJ表的QTY字段中插入或修改供应数量后,触发该触发器,检查供应数量是否在100-1000之间。

Use SPJ Go

IF EXISTS(SELECT name FROM sysobjects WHERE name='SPJCheck' and type ='TR')

DROP TRIGGER SPJCheck Go

CREATE TRIGGER SPJCheck ON SPJ FOR INSERT, UPDATE AS IF UPDATE (QTY) PRINT 'AFTER触发器开始执行……' BEGIN

Declare @QTYValue real

SELECT @QTYValue=( SELECT QTY FROM INSERTED)

9

If @ QTYValue >1000 or @ QTYValue <100 Print '输入供应数量错误!' END Go

用下面的语句测试创建的触发器: Use SPJ Go

Print '在SPJ中插入记录时触发器的执行结果:' Insert into SPJ values ('S4', 'P1',?J1?,50) Insert into SPJ values ('S5', 'P2',?J3?,1350) Go

Print '在SPJ中修改记录时触发器的执行结果:'

Update SPJ set QTY =1150 where SNO='S2' and PNO='P2' and JNO=?J1? Update SPJ set QTY =60 where SNO='S2' and PNO='P3' and JNO=?J2? Go

又如,INSTEAD OF触发器的实例:INSTEAD OF 触发器主要优点是使不可被修改的视图能够支持修改,在视图中添加INSTEAD-OF触发器后,则可创建更新视图。

--创建表

CREATE TABLE Products

( ProductID SMALLINT IDENTITY(1,1) PRIMARY KEY, Description VARCHAR(75), Price MONEY NOT NULL ) GO

CREATE TABLE Purchases

( PurchaseID SMALLINT IDENTITY(1,1) PRIMARY KEY, ProductID SMALLINT REFERENCES Products(ProductID), PurchasePrice MONEY NOT NULL,

PurchaseDate SMALLDATETIME DEFAULT(GETDATE()) ) GO

--插入记录

INSERT INTO Products(Description, Price) VALUES('Camera',325) INSERT INTO Products(Description, Price) VALUES('Projector',1500) INSERT INTO Products(Description, Price) VALUES('XBox',400) GO

INSERT INTO Purchases(ProductID, PurchasePrice) VALUES(1, 500) INSERT INTO Purchases(ProductID, PurchasePrice) VALUES(5, 325) INSERT INTO Purchases(ProductID, PurchasePrice) VALUES(1, 525) GO --创建视图

CREATE VIEW vw_ProductPurchases AS

10

SELECT pr.ProductID, pr.Description, pr.Price AS ProductPrice, pu.PurchasePrice, pu.PurchaseDate

FROM Products pr INNER JOIN Purchases pu ON pr.ProductID = pu.ProductID GO --创建触发器

CREATE TRIGGER tr_vwProductPurchases ON vw_ProductPurchases INSTEAD OF INSERT AS BEGIN

IF EXISTS

(SELECT TOP 1 * FROM INSERTED

WHERE ProductID IS NOT NULL AND

ISNULL(COALESCE(PurchasePrice, ProductPrice),0)>0 )

BEGIN

INSERT INTO Purchases (ProductID, PurchasePrice, PurchaseDate ) SELECT i.ProductID, COALESCE(PurchasePrice, ProductPrice), ISNULL(PurchaseDate, GETDATE()) FROM INSERTED i END ELSE BEGIN

PRINT 'Adequate data not provided.' END END

用下面的语句测试创建的触发器:

INSERT INTO vw_ProductPurchases(ProductID, PurchasePrice) VALUES(1, 700) 在视图上附上INSTEAD-OF触发器后,则允许修改表,但是不需要直接修改表中的数据,使用在vw_ProductPurchases视图上创建一个INSTEAD-OF触发器。INSTEAD OF并不表示在试图进行插入操作之前执行触发器。也不表示在进行插入操作之后执行触发器,表示忘掉插入操作,转而执行触发器中的代码。 二、删除触发器

在标准SQL中不提供修改触发器的语句,但支持对触发器的删除。语句格式是: DROP TRIGGER <触发器名> 例如,

DROP TRIGGER SPJCheck, tr_vwProductPurchases;

5.5 ODBC编程

ODBC(Open DataBase Connectivity)是用高级语言实现的应用程序编程接口,为应用程序提供了从数据库管理系统中访问数据的能力。ODBC的结构包括四个主要部分:应用程序编程接口、驱动器管理器、数据库驱动器和数据源。

ODBC (Open Database Connectivity,开放数据库互连)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。

一个基于ODBC的应用程序对数据库的操作不依赖任何DBMS,不直接与DBMS打交

11

道,所有的数据库操作由对应的DBMS的ODBC驱动程序完成。也就是说,不论是FoxPro、Access还是Oracle数据库,均可用ODBC API进行访问。由此可见,ODBC的最大优点是能以统一的方式处理所有的数据库。 (一)ODBC的组成

一个完整的ODBC由下列几个部件组成:(1)应用程序(Application);(2)ODBC管理器(Administrator),该程序位于Windows 95控制面板(Control Panel)的32位ODBC内,其主

数据源名 (DSN) ODBC 管理器 驱动程序 管理器 ODBC 驱动程序 应用 程序 应用层 ODBC API ODBC层 数据层 数据源 图6-1 ODBC部件关系图

要任务是管理安装的ODBC驱动程序和管理数据源;(3)驱动程序管理器(Driver Manager),驱动程序管理器包含在ODBC32.DLL中,对用户是透明的。其任务是管理ODBC驱动程序,是ODBC中最重要的部件(4)ODBC API;(5)ODBC 驱动程序,ODBC驱动程序是一些DLL,提供了ODBC和数据库之间的接口;(6)数据源。数据源包含了数据库位置和数据库类型等信息,实际上是一种数据连接的抽象。各部件之间的关系如图6-1所示。

应用程序要访问一个数据库,首先必须用ODBC管理器注册一个数据源,管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与具体数据库的联系。这样,只要应用程序将数据源名提供给ODBC,ODBC就能建立起与相应数据库的连接。

在ODBC中,ODBC API不能直接访问数据库,必须通过驱动程序管理器与数据库交换信息。驱动程序管理器负责将应用程序对ODBC API的调用传递给正确的驱动程序,而驱动程序在执行完相应的操作后,将结果通过驱动程序管理器返回给应用程序。 (二)在C++中的ODBC API数据库编程

(1) 动态加载数据源:

① 通过修改注册表加载数据源:

用户数据源:HKEY_CURRENT_USER\\SOFTWARE\\ODBC\\ODBC.INI 系统数据源:HKEY_LOCAL_MACHINE\\SOFTWARE\\ODBC\\ODBC.INI

对于不同类型的数据源,注册表的修改也不同,但基本上要修改两个地方,一个是在ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在\\ODBC.INI\\ODBC Data Sources子键下建立一个新项以便告诉驱动程序管理器ODBC数据源的类型。

② 通过ODBC API加载:Windows系统子目录下的动态链接库Odbcinst.dll提供了一个可以动态增加、修改和删除数据源的函数SQLConfigDataSource,由于VC的默认库文件中不包含此函数,因此使用前需将Odbcinst.h文件包含在工程的头文件中,在工程的setting属性框Link页的Object/library module编辑框中增加Odbc32.lib,同时保证系统目录system32下有文件Odbccp32.dll。

12

③ 文件数据源的连接:除了ODBC管理器,还可以通过SQLDriverConnect来添加文件数据源。

(2) ODBC API编程

如果一个ODBC API函数执行成功,则返回SQL_SUCCESS或SQL_SUCCESS_WITH_INFO,SQL_SUCCESS指示可通过诊断记录获取有关操作的详细信息,SQL_SUCCESS_WITH_INFO指示应用程序执行结果带有警告信息,可通过诊断记录获取详细信息。如果函数调用失败,返回码为SQL_ERROR。

一般,编写ODBC程序主要有以下几个步骤:

① 分配环境句柄:声明一个SQLHENV的变量,调用函数SQLAllocHandle。

设置环境属性:完成环境分配后,用函数SQLSetEnvAttr设置环境属性,注册ODBC版本号。

释放环境句柄:完成数据访问任务时,应调用SQLFreeHandle释放前面分配的环境。 ② 分配连接句柄:声明一个SQLHDBC类型的变量,调用SQLAllocHandle函数分配句柄。

设置连接属性:所有连接属性都可通过函数SQLSetConnectAttr设置,调用函数SQLGetConnectAttr可获取这些连接属性的当前设置值。

③ 连接数据源:对于不同的程序和用户接口,可以用不同的函数建立连接

SQLConnect:该函数只要提供数据源名称、用户ID和口令,就可以进行连接了。 SQLDriverConnect:该函数用一个连接字符串建立至数据源的连接,它可以让用户输入必要的连接信息,使用系统中还没定义的数据源。

SQLBrowseConnect:该函数支持以一种迭代的方式获取到数据源的连接,直到最后建立连接,它基于客户机/服务器体系结构,因此本地数据库不支持该函数。

④ 准备并执行SQL语句

A、分配语句句柄:语句句柄是通过调用SQLAllocHandle函数分配的。

函数SQLGetStmrrAttr和SQLSetStmrrAttr用来获取和设置一个语句句柄的选项,使用完,调用SQLFreeHandle释放该句柄。

B、执行SQL语句

SQLExecDirect:该函数直接执行SQL语句,对于只执行一次的SQL语句来说,该函数是执行最快的方法。

SQLPrepare和SQLExecute:对于需要多次执行的SQL语句来说,可先调用SQLPrepare准备SQL语句的执行,用SQLExecute执行准备好的语句。

C、使用参数:使用参数可以使一条SQL语句多次执行,得到不同的结果。 函数SQLBindParameter负责为参数定义变量,将一段SQL语句中的一个参数标识符(\?\捆绑在一起,实现参数值的传递。

⑤ 获取记录集

A、绑定列:首先必须分配与记录集中字段相对应的变量,然后通过函数SQLBindCol将记录字段同程序变量绑定在一起,对于长记录字段,可以通过调用函数SQLGetData直接取回数据。

绑定字段可以根据自己的需要全部绑定,也可以绑定其中的某几个字段。 通过调用函数SQLBindCol将变量地址值赋为NULL,可以结束对一个记录字段的绑定,通过调用函数SQLFreeStmt,将其中选项设为SQL_UNBIND,或者直接释放句柄,都会结束所有记录字段的绑定。

B、SQLFetch:该函数用于将记录集的下一行变成当前行,并把所有捆绑过的数据字段的数据拷贝到相应的缓冲区。

13

C、光标:应用程序获取数据是通过光标(Cursor)来实现的,在ODBC中,主要有3种类型的光标:单向光标、可滚动光标和块光标。

有些应用程序不支持可滚动光标和块光标,ODBC SDK提供了一个光标库(ODBCCR32.DLL),在应用程序中可通过设置连接属性(SQL_STTR_ODBC_CURSOR)激活光标库。

⑥ 记录的添加、删除和更新:数据源数据更新可通过3种方式:通过SQLExecDirect函数使用相应的SQL语句;调用SQLSetPos函数实现记录集定义更新;调用SQLBulkOperations函数实现数据更新。

第一种方式适用于任何ODBC数据源,后两种方式有的数据源不支持,可调用SQLGetInfo确定数据源。

SQLBulkOperations:该函数操作基于当前行集,调用前,须先调用SQLFetch或SQLFetchScroll获取。

函数调用后,块光标的位置变为未定义状况,因此,应该先调用函数SQLFetchScroll设定光标位置。

⑦ 错误处理:每个ODBC API函数都能产生一系列反映操作信息的诊断记录,可以用SQLGetDiagField函数获取诊断记录中特定的域,另外,可以使用SQLGetDiagRec获取诊断记录中一些常用的域。

⑧ 事务处理:事务提交有两种方式:自动提交模式和手动提交模式。应用程序可通过调用函数SQLSetConnectAttr设定连接属性SQL_ATTR_AUTOCOMMIT,自动提交模式是默认的连接属性设置,对于所有的ODBC驱动程序都能适应这种模式下,所有语句都是作为一个独立的事务进行处理的。

手动提交模式把一组SQL语句放入一个事务中,程序必须调用函数SQLEenTran明确地终止一个事务。若使用多个激活的事务,就必须建立多个连接,每一个连接包含一个事务。

⑨ 断开数据连接并释放环境句柄:完成数据库操作后,可调用SQLDisconnect函数关闭同数据库的连接。

例如,利用ODBC函数访问数据库 #include #include #include #include #include main() {

HENV henv; /*说明henv是一个环境型变量*/ HDBC hdbc; /*说明hdbc是一个连接型变量*/ HSTMT hstmt; /*说明hstmt是一个语句句柄变量*/ RETCODE retcode; /*说明retcode是一个返回变量*/ SQLAllocEnv(&henv); /*分配一个环境语句*/ SQLAllocConnect(henv,&hdbc); /*分配一个连接句柄*/ SQLConnect(hdbc,\学生\ /*连接数据库源*/ SQLAllocStmt(hdbc,&hstmt); /*分配一个语句句柄*/

retcode=SQLExecDirect(hstmt,\ /*直接执行SQL语句*/

14

?? /*结果集处理*/ SQLDisconnect(hdbc); /*断开数据源*/

SQLFreeStmt(hstmt,SQL_DROP); /*释放一个语句句柄*/ SQLFreeConnect(hdbc); /*释放一个连接句柄*/

SQLFreeEnv(henv); /*当应用完成后,释放环境句柄*/ }

15

本文来源:https://www.bwwdw.com/article/3a6r.html

Top