第8章SQL的系统特征主讲人:骆炎民Email:lym@hqu.edu.cn2本章讨论的问题几乎所有的DBMS都会提供一个SQL解释器来接受和执行SQL查询和命令。但实际上,多数SQL语句是一个更大程序段的一部分。本章主要讨论的就是如何把SQL应用到一个完整的编程环境中。3SQL的三种编程方式嵌入到宿主语言(常规程序设计语言,如C)中(8.1节)关键问题:SQL与环境变量/宿主语言之间如何交换数据存储过程(8.2-8.3节)将SQL和被称为持久性存储模块的一般通用程序结合起来,这些模块是以数据库模式形式存储的代码段,由用户以命令的形式执行调用级界面(8.4节)以常规语言编程,用函数库来访问数据库为了从C程序中调用,可以使用称为SQL/CLI的SQL标准库4主要内容8.1编程环境下的SQL8.2模式中的存储过程8.3SQL环境8.4使用调用层接口8.5Java数据库连接8.6SQL中的事务8.7SQL中的安全机制和用户认证5包含SQL语句的典型编程系统用宿主语言编程,且用到了一些并非宿主语言的嵌套SQL语句,将整个程序提交给预处理器。预处理器将嵌套SQL语句转化为调用函数,此函数把SQL语句当作字符串参数,并且执行这个SQL语句经过预处理的宿主语言程序随后以通常的方式编译6包含SQL语句的典型编程系统DBMS销售商提供了必要的函数定义库。这样实现SQL的函数被执行,并且整个程序像一个整体一样运作。还有另一种方式:程序员直接用宿主语言编写程序,只是在必要时使用这些函数调用。这就是SQL编程的第3种方式“调用级界面/CLI”7阻抗不匹配问题连接SQL语句和常规编程语言的基本问题就是阻抗不匹配:即SQL数据模式与编程语言模式差别非常大。SQL:关系数据模型,不使用指针、结构体、循环和转移常规编程语言:整型、实型、算术性、字符型、指针、记录、数组……无法直接表示集合……因此,在SQL和常规编程语言之间不能直接转移数据8SQL与常规编程语言二者缺一不可SQL与常规编程语言二者缺一不可SQL在很大程度上帮助程序员编写数据库操作而很多重要工作(如算法实现)又离不开常规编程语言必须设计一种机制(例如提供接口),允许程序的开发既可以使用SQL,也可以使用常规编程语言(宿主语言)9SQL/宿主语言接口数据库只能由SQL语句访问在数据库和宿主语言程序之间的信息交换是通过共享变量实现的。这种变量允许出现在宿主语言和SQL语句中。SQL中共享变量前面要加上冒号作为前缀在宿主语言中这些变量并不需要冒号10关键字EXECSQL在宿主语言中使用SQL语句时,通过SQL语句前面的关键字EXECSQL提示预处理器将有SQL代码进入。系统将预处理这些语句,用宿主语言中合适的函数调用来代替这些语句,并充分利用与SQL相关的函数库。11特殊变量SQLSTATE在SQL标准中,SQLSTATE这个特殊变量用于连接宿主语言程序与SQL执行系统。SQLSTATE是五个字符的数组类型。每次调用SQL的库函数,向SQLSTATE变量中存放一个代码,该代码表示调用过程中出现的问题。12SQLSTATE变量的值SQL标准同时指定了大量的五个字符的代码和它们的意义。例如:’00000’(五个零):没有产生任何错误‘02000’:没找到作为SQL查询结果组成部分的元组。这个代码非常重要:它允许在宿主语言程序中创建一个循环并且每执行一次循环检查一个元组,当关系中最后一个元组被检查后中止该循环。13DECLARE节EXECSQLBEGINDECLARESECTION;共享变量的声明EXECSQLENDDECLARESECTION;声明节中的变量声明形式可以是宿主语言要求的任何形式。为使声明的变量有意义,共享变量类型必须是宿主语言和SQL都可以处理的如整型、实型和字符型,或者数组类型声明节14示例8.1EXECSQLBEGINDECLARESECTION;charstudioName[50],studioAddr[256];charSQLSTATE[6];EXECSQLENDDECLARESECTION;15使用共享变量后面是嵌套的SQL语句共享变量在SQL中须加冒号作前缀16SQL语句的嵌入任何不返回结果的SQL语句(即非查询语句)都可以用EXECSQL为前缀直接嵌入到宿主语言中包括INSERT、DELETE和UPDATE语句以及那些创建、修改或者删除表和视图等模式元素的语句。然而由于“阻抗不匹配”,select-from-where查询不能直接嵌套到宿主语言。查询产生的结果是元组包,但是大多数宿主语言均不直接支持集合或包数据类型。17查询嵌入宿主语言的机制为了将查询结果与宿主语言程序相连接,嵌套SQL有两种机制可选择:(1)单元组选择语句:只有一个结果元组的查询可将该元组存储到共享变量中,一个变量对应元组的一个分量。(2)游标:如果为查询声明一个游标,那么产生多于一个元组的查询就可以执行了。游标范围覆盖了结果关系中的所有元组,每个元组依次被提取到共享变量中,并由宿主语言进行处理。18(1)单元组选择语句单元组选择的形式类似于普通的select-from-where语句,只是SELECT子句后紧跟着关键字INTO和一连串的共享变量(以冒号作为前缀)。如果查询结果是个单一元组,那么这个元组的分量将分配给这些变量并成为它们的值。如果结果没有元组或者多于一个元组,那么不会分配给这些共享变量,同时一个相应的错误码被写入到SQLSTATE变量中。19单元组选择示例20(2)游标①游标声明EXECSQLDECLARE游标名称CURSORFOR查询其中查询可以是通常的select-from-where查询或者关系名。游标范围覆盖该查询产生的关系元组。②打开游标(初始化)EXECSQLOPEN游标名称这个语句初始化游标的位置,使游标指向其覆盖的那个关系中的第一个元组,并从那里开始检索。21(2)游标(续1)③一次或者多次使用fetch子句EXECSQLFETCHFROM游标名称INTO变量列表目的是得到游标覆盖的那个关系中的下一个元组。每个关系元组的属性对应列表里的一个变量。假如有一个可获取的元组,那么该元组相应的分量将赋值给对应的变量。如果元组已经被遍历过了,那么不会返回任何元组,且SQLSTATE被赋值为‘02000’,表示“没有发现任何元组”。22(2)游标(续2)④关闭游标EXECSQLCLOSE游标名称这条语句关闭游标,游标将不再覆盖关系的元组。然而,游标可由另外一条OPEN语句重新初始化,它将重新覆盖这个关系的元组。23游标示例分别计算净资产位数从1-14的出品人个数24游标更新当游标遍历一个基本表的元组时,不仅可以读和处理每个元组的值,也可以修改或者删除当前元组。这里的UPDATE和DELETE语句,要求其WHERE子句只能是WHERECURRENTOF,其后跟着游标的名称。25游标更新示例26防止并发更新不希望游标读取的元组被并发的变化所影响,而强调统计是针对某个时刻已存在的关系。对游标所读取关系进行修改的语句,在游标读取元组前就已彻底完成或在游标读取元组后再运行为了保证这一点,对于并发变化可以将游标声明为对并发修改不敏感(insensitive)。例如:EXECSQLDECLAREexecCursorINSENSITIVECURSORFORSELECTnetWorthFROMMovieExec;保证在execCursor打开和关闭之间,对关系MovieExec所作的变化不会影响游标提取到的元组集合。27将游标声明为READONLY若确信关系R上的游标不会改变R,则该游标可与R的不敏感游标同时运行。将游标声明为FORREADONLY,那么数据库系统可以保证基本关系R不会因为读取游标而修改了关系R。EXECSQLDECLAREexecCursorCURSORFORSELECTnetWorthFROMMovieExecFORREADONLY;这样,任何试图通过游标execCursor所做出的关系修改都会产生错误。28卷型游标游标提供了怎样遍及一个关系的元组的选择缺省选择是从关系顶端开始,依次提取元组直至末尾(最常用)。不过还可以按别的顺序提取元组。为了可以按其他顺序提取元组,有2个步骤:①声明游标时,将关键字SCROLL置于保留字CURSOR之前。告诉SQL系统,游标的使用方式不只是按照元组顺序向前移动。②FETCH语句中,关键字FETCH后面的选项决定所期望的元组的位置。29FETCH选项NEXT|PRIOR:按顺序提取相对于游标当前位置的下一个|上一个元组。NEXT为默认值。FIRST|LAST:提取第一个|最后一个元组RELATIVEn:n为正|负整数。相对游标当前位置向前(n为正整数)|向后(n为负整数)移动n个元组。RELATIVE1即NEXT,RELATIVE-1即PRIORABSOLUTEn:n为正|负整数。从头部(n为正整数)|尾部(n为负整数)移动n个元组。ABSOLUTE1即FIRST,ABSOLUTE-1即LAST30卷型游标示例EXECSQLDECLAREexecCursorSCROLLCURSORFORMovieExec;EXECSQLOPENexecCursor;EXECSQLFETCHLASTFROMexecCursorINTO:execName,:execAddr,:certNo,:worth;While(1){EXECSQLFETCHPRIORFROMexecCursorINTO:execName,:execAddr,:certNo,:worth;31动态SQL目前为止,嵌套在宿主语言中的SQL模型都是在宿主语言程序中特定的SQL查询和命令。嵌套SQL的另一种形式是自身可以被宿主语言处理的语句。这种语句编译时不可知,因此,不能被SQL预处理器和宿主语言编译器处理。例如:程序提示用户输入SQL查询,然后读这个查询并执行。查询被读到后,将立即进行语法分析,并且由SQL系统寻找适合执行该查询的方式。32动态SQL的执行1.EXECSQLPREPAREVFROM表达式V是SQL变量,表达式是其值为字符串的任意一条宿主语言表达式,该字符串被当作SQL语句。宿主语言程序指导SQL系统接受刚读到的字符串,经语法分析后将其转化为可执行SQL语句,用V表达2.EXECSQLEXECUTEV执行V所代表的SQL语句。33动态SQL的执行(2)上述两个步骤也可以合二为一EXECSQLEXECUTEIMMEDIATE+字符串表达式如果一条语句被编译一次,然后执行很多次时,就会看到合并这两步是不利的。使用EXECUTEIMMEDIATE,每次语句执行时都要付出准备该语句的代价,而不是只付出一次。34动态SQL示例既然查询只执行一次,那么6)7)可替代为:EXECSQLEXECUTEIMMEDIATE:query35主要内容8.1编程环境下的SQL8.2模式中的存储过程8.3SQL环境8.4使用调用层接口8.5Java数据库连接8.6SQL中的事务8.7SQL中的安全机制和用户认证36持久性存储模块PersistentStoredModules:SQL/PSM(简写为PSM)允许用简单通用的语言编写过程并且将它们存储在数据库中,作为模式的一部分。这些过程可以在数据库中完成不能用SQL单独完成的处理。每个商用性DBMS均向用户提供了自身的PSM扩展。本书描述的是SQL/PSM标准,表明这些功能的主要思想,帮助大家理解任何与特定系统相关的该类语言。37PSM模块PSM中定义了模块(modules),该模块是如下内容的集合:函数和过程定义临时关系声明其他可选声明381.创建PSM函数和过程过程声明PSM过程