首页  编辑  

使用ODBC


使用ODBC

本部分提供Oracle和SQL Server使用ODBC的方法的信息和关于开发和移植使用ODBC的应用程序的信息。

推荐的转换策略

如果要把你的应用程序从Oracle转换到SQL Server上,推荐采用下面的过程:

1. 如果你的应用程序使用Oracle Pro*C或者Oracle调用接口(Oracle Call Interface,OCI)的话,考虑把它转换到ODBC。

2. 理解SQL Server缺省的结果集合和游标选项,然后选择针对你的应用程序的最有效的提取策略。(fetching strategy)。

3. 重新映射Oracle ODBC SQL数据类型到合适的SQL Server ODBC SQL数据类型。

4. 使用ODBC Extended SQL扩展来创建类属的SQL语句。

5. 决定SQL Server应用程序是否需要手工提交模式。

6. 测试你的应用程序的性能,并对程序做必要的修改。

ODBC结构

微软提供了ODBC SQL Server驱动程序的16位和32位两种版本。32位的ODBC SQL Server驱动程序是线程安全的。驱动程序通过多个线程连续共享对共享语句句柄(hstmt)、连接句柄((hdbc)和环境句柄(henv)的访问。但是,ODBC程序仍然以适当的顺序对语句和连接空间内的保存操作作出响应,即使程序使用多个线程。

由于Oracle的ODBC驱动程序可以由许多供应商提供,所以关于结构和操作有很多种可能的情况。你必须联系你的供应商,确保ODBC驱动程序符合你的应用程序的需要。

在大多数情况下,Oracle的ODBC驱动程序使用SQL*Net连接到Oracle RDBMS。但是,连接到Personal Oracle的时候,可以不用SQL*Net。

下图显示了32位环境中的应用程序/驱动程序结构。

 “形实替换”(thunking)这个术语的含义是截获一个函数调用,进行特定的处理以在16位和32位代码之间转换,然后再把控制传送到目标函数。注意ODBC Cursor Library是如何随意的在驱动程序管理器和它的驱动程序的之间驻留的。该库在驱动程序的基础上提供了可卷动的游标服务,而驱动程序本身只支持前向游标(forward-only cursors)

只能向前的游标(Forward-Only Cursors

Oracle和SQL Server处理结果集合和游标的方式是不一样的。理解这些不同是成功转换Oracle和SQL Server的客户程序和使转换后的程序性能优化的基础。

在Oracle中,当在客户应用程序中进行提取时,来自SELECT命令的任何结果集合都被当成一个只能向前的游标来处理。无论你是用ODBC、ODI、还是内含的SQL作为开发工具,都是这样的。

缺省情况下,客户程序使用的每一个Oracle FETCH命令(例如,ODBC中的SQLFetch)将导致通过网络到服务器的一个来回以返回一行。如果客户应用程序希望通过网络一次返回多于一行,必须在程序中设置一个数组并且执行数组提取(array fetch)。

由于Oracle的多版本并行模型,在提取期间,服务器并不为一个只读的游标保存锁定。当程序用FOR UPDATE子句指定一个可更新的游标时,则当语句打开的时候,SELECT语句需要的所有的行被锁定。这些行级别的锁定将一直维持到程序发出一个COMMIT或者ROLLBACK请求。

在SQL Server中,一个SELECT语句并不总是伴随一个服务器上的游标。缺省情况下,SQL Server简单的把来自一个SELECT语句的所有结果集合以流的形式返回到客户。这个流从SELECT语句执行就开始。结果集合流也可以被存储过程中的SELECT语句返回。另外,一个单独的存储过程或者批处理命令可以以流的形式返回多个结果集合来响应一个EXECUTE语句。

一旦得到结果集合,SQL Server客户负责从这些缺省的结果集合中提取。对于缺省的结果集合,在客户端的提取不会导致到服务器的来回。取而代之的是,通过本地网络缓存把从缺省的结果集合中提取的数据放入程序变量中。这个缺省的结果集合模型创建了一个有效的机制来通过一个网络来回返回多行数据到客户。最小化网络来回通常是客户/服务器应用程序性能的最重要的因素。

同Oracle的游标相比较,缺省结果集合对SQL Server客户应用程序提出了一些额外的职责。SQL Server客户应用程序必须立刻提取从EXECUTE语句返回的结果集合行。如果应用程序需要向程序的其它部分提供增件的行,它必须缓存这些行到一个内部数组。如果它不能提取所有的结果集合行,到SQL Server的连接仍然是繁忙的。

如果发生了这样的事情,在那个连接上将不能进行其它工作(例如UPDATE语句),直到整个结果集合行被提取或者客户取消这个请求。而且,服务器将继续维持表数据页的共享锁定,直到提取完成。这个事实强迫你必须尽可能快的提取所有的行。这个技术同Oracle应用程序常用的增量形式的提取形成鲜明的对比。

服务器游标

Microsoft SQL Server提供了服务器游标来满足通过网络进行结果结合增量提取的需要。服务器游标可以被应用程序通过简单的调用SQL SetStmtOption来设置SQL_CURSOR_TYPE选项来请求。

当一个SELECT语句作为服务器游标执行的时候,EXECUTE语句将只返回一个游标标识符。其后的提取请求传送这个游标标识符到服务器,同时还有一个指明一次返回的行的数目的参数。服务器再返回请求的数目的行。

在提取请求之间,连接是空闲的可以进行其它命令,包括别的游标OPEN或者FETCH请求。在ODBC术语中,这表示服务器游标允许SQL Server驱动程序支持一个连接上多个活动的语句。

而且,服务器游标并不总是在提取请求之间维持锁定,因此你可以自由的在提取之间暂停以进行用户输入,并不会影响其它用户。服务器游标可以通过乐观的冲突探测(optimistic conflict detection)或者悲观卷动锁定(pessimistic scroll locking)并行选项来更新。

虽然这些特性使使用服务器游标的程序对Oracle开发人员来说比使用缺省结果集合更熟悉一些,但是,它们并不自由。跟缺省结果集合相比:

  • 在服务器资源方面,服务器游标的开销更高昂,因为需要在服务器上使用临时存储空间来维持游标状态信息。
  • 要取得一个指定的结果集合数据,服务器游标的开销更大,因为EXECUTE语句和服务器游标的每一次提取请求需要一个独立的到服务器的来回。
  • 在支持的批处理和存储过程方面,服务器游标不够灵活。这是因为服务器游标一次只能执行一条SELECT语句,反之,缺省数据集合可以用于一次返回多个结果集合的批处理和存储过程,或者在SELECT以外的其它语句中使用。

由于这些原因,把服务器游标的使用限制在那些确实需要它们的特征的程序部分是聪明之举。可以在Ssdemo.cpp示例SQL Server ODBC程序文件中的LIST_STUDENTS函数里看到使用服务器游标的例子。

可卷动游标

Oracle RDBMS只支持前向卷动游标(forward-scrolling cursors)。每一行都以查询中指定的顺序提取到应用程序中。Oracle不接受向后移动到前面提取过的行的请求。向后移动的唯一的方法是关闭游标然后再打开它。不幸的是,你需要回到活动查询集合的第一行。

由于SQL Server支持可卷动的游标,你可以把SQL Server游标定位到任何行位置。你可以向前或者向后卷动。对于许多涉及到用户界面的应用程序,卷动性是一个有用的特性。利用可卷动游标,你的应用程序可以一次提取充满一屏的行,而只在用户需要的时候才提取更多的行。

尽管Oracle不直接支持可卷动游标,但是通过使用一些ODBC选项中的一个,这个限制可以被减到最小。例如,一些Oracle ODBC驱动程序,比如随同Microsoft Developer Studio可视化开发系统一同发行的驱动程序,在驱动程序本身提供了客户端的可卷动游标。

作为选择,ODBC Cursor Library支持对任何以一级一致级别编译的ODBC驱动程序的块可卷动游标。这些客户游标选项都可以通过使用RDBMS做只能向前的提取,然后在内存或者磁盘上缓存结果集合数据的方式来支持卷动。当数据被请求时,驱动程序根据需要从RDBMS或者本地缓存中取得该数据。

客户基游标也支持对SELECT语句生成的结果集合的定位UPDATE和DELETE语句。游标库构造一个带有WHERE子句的UPDATE或者DELETE语句,该子句为行中的每一列指定缓存值。

如果你需要可卷动的游标,并且想维持同样的源代码在Oracle和SQL Server中执行,ODBC Cursor Library是一个有用的选择。要了解关于ODBC Cursor Library的详细信息,请参看你的ODBC文档。

使用SQL Server Default Result SetsServer Cursors的策略

有这么多SQL Server提供的用于提取数据的选择,有时候很难决定该用什么,什么时候用,下面是一些有用的指导:

缺省结果集合通常是从SQL Server获取整个数据集合到客户的最快的方法。在你的应用程序中看看,在什么地方可以使用这个来提高性能。例如,批处理的报告生成,一般需要处理整个结果集合才能完成,不需要用户的互动,而且在处理过程的中间不会有更新操作。

如果你的程序需要更新游标,使用服务器游标。当使用定位UPDATE或者DELETE语句时,缺省结果集合是不能更新的。另外,服务器游标对于更新也比客户基游标要好一些,后者必须通过构造一个等价搜寻的UPDATE或者DELETE语句来模拟一个定位UPDATE或者DELETE。

如果你程序需要可卷动的、只读游标,ODBC Cursor Library和服务器游标都是好的选择。ODBC Cursor Library赋予你在SQL Server和Oracle中都兼容的行为,而服务器游标在一次通过网络提取许多数据的时候更灵活。

当你使用缺省结果集合或者构筑在缺省数据集合上的ODBC Cursor Library时,确保尽可能快的提取到结果集合的底部以避免在服务器上维持共享锁定。

当你使用服务器游标的时候,确保使用SQLExtendedFetch来提取行块,而不是一次提取一个单独的行。这同Oracle应用程序中数组类型的提取是一样的。对服务器游标的每一次提取都需要通过网络在应用程序和RDBMS之间的一个来回。

让我们考虑杂货店采购的情况。假设你在一个杂货店采购了十包杂货,把一个包放到你的车上,开车回家,放下这个包,然后返回杂货店拿下一个包。这是一种很少见的情景,但是如果你编制一个从服务器游标的单行提取的程序,这就是你在SQL Server上做的。

如果你的程序只需要前向的、只读的游标,但是要依靠在一个连接上的多个打开的游标,则在你知道你能够立刻把整个结果集合的数据提取到程序变量的情况下,使用缺省结果集合。如果你不能,那么使用服务器游标。

这个策略没有它听起来那么困难。许多程序员都知道用单次选择返回最大数量的行。对于单次选择,缺省结果集合比服务器游标更有效。

要获得这个技术的示例,请参看Ssdemo.cpp示例SQL Server ODBC程序文件的LIST_STUDENTS函数。注意只有在SELECT语句可能返回多于一行的时候如何请求服务器游标。随着执行的步骤,行集的尺寸被设定为一个合理的批处理尺寸。这允许同样的SQLExtendedFetch循环可以在缺省结果集合和服务器游标中有效的工作。

要了解关于游标实现的更多信息,请参看SQL Server联机手册。

一个连接上的多个活动语句

ODBC驱动程序使用语句句柄(hstmt)来跟踪程序中的每一个活动的SQL语句。语句句柄总是伴随着一个RDBMS连接句柄(hdbc)。ODBC驱动程序管理器使用连接句柄来发送请求的SQL语句到指定的RDBMS。绝大多数Oracle的ODBC驱动程序允许一个连接上的多个语句句柄。但是,在使用缺省结果集合时,SQL Server 只允许在一个连接上只能有一个活动的语句句柄。当用SQL_ACTIVE_STATEMENTS选项查询的时候,该SQL Server驱动程序的SQLGetinfo函数返回值1。当语句选项以使用服务器游标的方式设定时,一个连接句柄上的多个活动语句是被支持的。

如果要了解关于设置语句选项来请求服务器游标的更多信息,参看SQL Server联机手册。

数据类型映射

SQL Server ODBC驱动程序提供了比任何Oracle ODBC驱动程序都丰富的数据类型映射集。

SQL Server数据类型

ODBC SQL数据类型

binary

SQL_BINARY

bit

SQL_BIT

char, character

SQL_CHAR

datetime

SQL_TIMESTAMP

decimal, dec

SQL_DECIMAL

float, double precision, float(n)
for n = 8-15

SQL_FLOAT

image

SQL_LONGVARBINARY

int, integer

SQL_INTEGER

money

SQL_DECIMAL

nchar

SQL_WCHAR

ntext

SQL_WLONGVARCHAR

numeric

SQL_NUMERIC

nvarchar

SQL_WVARCHAR

real, float(n) for n = 1-7

SQL_REAL

smalldatetime

SQL_TIMESTAMP

smallint

SQL_SMALLINT

smallmoney

SQL_DECIMAL

sysname

SQL_VARCHAR

text

SQL_LONGVARCHAR

timestamp

SQL_BINARY

tinyint

SQL_TINYINT

uniqueidentifier

SQL_GUID

varbinary

SQL_VARBINARY

varchar

SQL_VARCHAR

timestamp数据类型被转换成SQL_BINARY数据类型。这是因为timestamp列中的数据不是datetime数据,而是binary(8)数据。它们用来指出一行中的SQL Server活动的次序。

下表中显示了为Oracle的 Microsoft ODBC驱动程序映射的Oracle数据类型。

Oracle数据类型

ODBC SQL数据类型

CHAR

SQL_CHAR

DATE

SQL_TIMESTAMP

LONG

SQL_LONGVARCHAR

LONG RAW

SQL_LONGVARBINARY

NUMBER

SQL_FLOAT

NUMBER(P)

SQL_DECIMAL

NUMBER(P,S)

SQL_DECIMAL

RAW

SQL_BINARY

VARCHAR2

SQL_VARCHAR

来自其他供应商的Oracle ODBC驱动程序可能有不同的数据类型映射。

ODBC扩展SQLODBC Extended SQL

ODBC扩展SQL标准提供了ODBC的SQL扩展,该扩展支持Oracle和SQL Server提供的非标准的SQL特征。这个标准允许ODBC驱动程序把类属的SQL语句转换为Oracle-或者SQL Server-方言的SQL语法。

该标准引入了外部连接(outer joins),例如谓词转义字符(predicate escape characters),标量函数(scalar functions),date/time/timestamp值,以及存储程序(stored programs)。下面的语法用来标识这些扩展:

--(*vendor(Microsoft), product(ODBC) extension *)--

OR

{extension}

转换是在运行时间发生的,不需要修改任何程序代码。在大多数应用程序开发情景下,最好的方法是编写一个程序,并且允许ODBC在程序运行时执行RDBMS转换处理。

外部连接(Outer Joins

Oracle和SQL Server没有兼容的外部连接语法。这可以用ODBC扩展SQL外部连接语法来解决。Microsoft SQL Server语法同ODBC扩展SQL/SQL-92语法是一样的。唯一不同之处是{oj }容器

ODBC Extended SQL and SQL-92 Oracle Microsoft SQL Server

SELECT STUDENT.SSN, FNAME, LNAME, CCODE, GRADE
FROM {oj STUDENT LEFT OUTER JOIN GRADE ON STUDENT.SSN = GRADE.SSN}

SELECT SUBSTR(LNAME,1,5)
FROM STUDENT

SELECT SUBSTRING(LNAME,1,5)
FROM STUDENT

DateTime、和Timestamp

ODBC为date、time、和timestamp值提供了三中转义子句。

类别

速记语法

格式

Date

{d 'value'}

"yyyy-mm-dd"

Time

{t 'value'}

"hh:mm:ss"

Timestamp

{Ts 'value'}

"yyyy-mm-dd hh:mm:ss[.f…]"

日期的格式对Oracle应用程序的影响比对SQL Server应用程序的影响更大。Oracle希望日期格式是“DD-MON-YY”。在任何其它情况下,需要用TO_CHAR或者TO_DATE函数加上一个日期格式模型来执行格式转换。

Microsoft SQL Serve自动转换大多数通常的日期格式,当不能进行自动转换时,提供了CONVERT函数。

如表中所示,ODBC扩展SQL能在两中数据库上工作。SQL Server不需要转换函数。但是,ODBC标准语法可以一般的应用于Oracle和SQL Server两种数据库。

ODBC Extended SQL

Oracle

Microsoft SQL Server

SELECT SSN, FNAME, LNAME, BIRTH_DATE
FROM STUDENT WHERE BIRTH_DATE < {D '1970-07-04'}

SELECT SSN, FNAME, LNAME,
BIRTH_DATE
FROM STUDENT
WHERE BIRTH_DATE <
TO_DATE('1970-07-04', 'YYYY-MM-DD')

SELECT SSN, FNAME, LNAME,
BIRTH_DATE
FROM STUDENT
WHERE BIRTH_DATE < '1970-07-04'

调用存储过程

ODBC调用存储过程的速记语法支持Microsoft SQL Server存储过程以及Oracle存储过程、函数、和包。可选的“?=”为Oracle函数或者SQL Server过程捕捉返回值。参数语法用于向被调用程序传递或者返回值。在大多数情况下,同样的语法可以一般的应用于Oracle和SQL Server应用程序。

在下面的例子中,SHOW_RELUCTANT_STUDENTS函数是Oracle包P1的一部分。因为该函数从一个PL/SQL游标返回多个行,所以它必须存在于一个包中。当你调用存在于一个包中的函数或者过程时,包名必须放在程序名的前面。

包P1中的SHOW_RELUCTANT_STUDENTS函数使用一个包游标来取得多行数据。每一行都必须通过对该函数的调用来请求。如果没有更多的行要获取,函数返回值0,表示这里没有更多的行可以获取了。这个示例Oracle包和它的函数的性能也许不是十分的令人满意。对于这类操作,SQL Server过程更有效一些。

Generic ODBC Extended SQL

Oracle

Microsoft SQL Server

{?=} call procedure_name[(parameter(s))]}

SQLExecDirect(hstmt1,(SQLCHAR *)"{? = call owner.procedure(?)}",
SQL_NTS);

SQLExecDirect(hstmt1, (SQLCHAR*)"{? = call
STUDENT_ADMIN.P1.
SHOW_RELUCTANT
_STUDENTS(?)}"
,
SQL_NTS);

SQLExecDirect(hstmt1, (SQLCHAR*)"{? = call
STUDENT_ADMIN.
SHOW_RELUCTANT
_STUDENTS}"
,
SQL_NTS);

SQL方言翻译

由于Oracle和SQL Server的ODBC驱动程序的多样性,你也许不能总是用扩展SQL 函数获得同样的转换字串。为了帮助应用程序调试,你也许会考虑使用SQLNativeSql函数。这个函数返回驱动程序翻译的SQL字串。

下面是包含标量函数CONVERT的SQL字串的可能的转化结果。列SSN被定义为CHAR(9)类型,并且被转换为一个数字值。

原语句

转换成的Oracle语句

转换成的SQL Server语句

SELECT (fn CONVERT
(SSN, SQL_INTEGER))
FROM STUDENT

SELECT TO_NUMBER(SSN)
FROM STUDENT

SELECT CONVERT(INT, SSN)
FROM STUDENT

Common.cpp示例程序

Common.cpp示例程序并没有利用ODBC扩展SQL语法。而是利用了一系列的视图和过程来隐藏那些对Oracle和SQL Server来说不是普遍的语句和函数。这个程序,虽然是为使用ODBC而写的,但更集中于说明一个应用程序开发人员在试图编写一个普遍的程序时如何轻松的克服任何明显的障碍。

这些技术和策略能很好的用于非ODBC的开发环境。如果你正在使用ODBC,考虑使用ODBC扩展SQL语法来克服Oracle和SQL Server之间的任何语法上的差异。

手工提交模式

无论何时当用户修改数据,Oracle自动进入事务模式。必须跟一个显明的COMMIT语句把改变写到数据库中。如果用户想撤消修改,可以发出一个ROLLBACK语句。

缺省情况下,SQL Server自动提交每一个改变。在ODBC中,这叫自动模式。如果你不想它这样做,你可以用BEGIN TRANSACTION语句标记一个包含事务的语句块的开始。在这些语句列举以后,用一个显明的COMMIT TRANSACTION or ROLLBACK TRANSACTION语句结束。

要确保同你的Oracle应用程序兼容,建议你用SQLConnectOption函数把你的SQL Server应用程序以隐含事务模式放置。SQL_AUTOCOMMIT选项必须被设置为SQL_AUTOCOMMIT_OFF以完成这个任务。下面的代码引自示例程序来说明这个概念:

SQLSetConnectOption(hdbc1, SQL_AUTOCOMMIT,-sql_AUTOCOMMIT_OFF);

SQL_AUTOCOMMIT_OFF选项指示驱动程序使用隐含事务。缺省选项SQL_AUTOCOMMIT_ON指示驱动程序使用自动提交模式,在这种模式,每一个语句在执行以后立即提交。从手工提交模式变化到自动提交模式将提交连接上的任何打开的事务。

如果设置了SQL_AUTOCOMMIT_OFF选项,则应用程序必须通过显明的SQLTransact函数来提交或者后滚(rool back)事务。该函数请求对在所有伴随连接句柄的语句句柄上的所有活动操作的一个提交或者后滚。还可以请求对伴随环境句柄的所有连接句柄执行提交或者后滚操作。

SQLTransact(henv1, hdbc1, SQL_ROLLBACK);

(SQLTransact(henv1, hdbc1, SQL_COMMIT);

当自动提交模式关闭的时候,驱动程序发出SET IMPLICIT_TRANSACTIONS ON语句到服务器。从SQL Server 6.5开始,这种模式支持DDL语句。

要以手工模式提交或者后滚事务,应用程序必须调用SQLTransact。SQL Server发出一个COMMIT TRANSACTION语句提交一个事务,发出一个ROLLBACK TRANSACTION语句后滚事务。

要小心,手工提交模式可能对你的SQL Server应用程序的性能有负面影响。每一次提交需要到服务器的一个来回来发出COMMIT TRANSACTION字串。

如果你有单原子事务(一个单独的INSERT、UPDATE、或者DELETE,在后面紧跟着一个COMMIT),使用自动提交模式。

在示例程序中,手工提交模式被打开,即使是对于原子事务,这样做是为了说明SQL Server应用程序要尽量模仿为Oracle设计的类似的程序是多么的容易。

image008.gif (3.8KB)
image009.gif (6.9KB)