一些 ASP.NET + Oracle 11g 边学边写的随笔。本系列文章可帮助对 Oracle 不熟悉、甚至完全不会使用的 .NET 程序员,能在最短时间内上手并开发应用程序,避免像版工我一样花了大把时间翻书、查文件、撰码实作和测试、发问等回复。本帖内容包括:Oracle 的「空字符串 = NULL」特性、Oracle 的 Transaction Isolation Level、Oracle 的备份 & 还原。
(十) Oracle 小技巧
* 在 SQL Plus 中,用「desc」指令,可浏览数据表的 schema,如:desc table1;
* 不论是用 SQL Plus,还是 Oracle SQL Developer 工具,在执行完 INSERT、UPDATE、DELETE 语句后,都必须再执行一次「Commit;」指令,才会真正写入数据库,这点和他牌的数据库不同;但若是透过 ASP.NET 应用程序写入 Oracle,则不必加上此一动作。若某个人透过 SQL Plus 或 Oracle SQL Developer 写入 Oracle 之后,未再执行「Commit;」或「Rollback;」指令,会造成该笔记录被锁住,别人都无法「修改」该笔记录 (用户的应用程序或浏览器会一直处于等待状态),但仍可「读取」。
------------------------------------------------------------------------------------------
(十一) Oracle 里没有「空字符串」这种东西
Oracle 里根本没有「空字符串 ('')」这种东西,而会将空字符串视为 Null,但在 SQL Server、其它厂牌的数据库和 ANSI SQL 92 的定义却并非如此。
当您将 Oracle 里一个表的 varchar2 字段,更新成空字符串时,如下:
UPDATE table1 SET col1='' [WHERE id=80];
在 Oracle 中会等同于:
UPDATE table1 SET col1=NULL [WHERE id=80];
此时,当您用如下的 SQL 语句去 SELECT 时,会查不出任何数据 (但用其它厂牌的数据库则查得到):
SELECT * FROM table1 WHERE col1='';
若改成如下,用 IS NULL 去当查询条件,才查得到数据 (但用其它厂牌的数据库则查不到):
SELECT * FROM table1 WHERE col1 IS NULL;
即使您改用 ADO.NET 的 OracleDataReader.IsDBNull 方法,写 .NET 应用程序去判断,结果也是一样。
此时,当您用 LENGTH 函数去判断 col1 字段的长度,会返回 Null,而不是 0,如下:
SELECT LENGTH(col1) FROM table1 WHERE id=80;
这些 Oracle 的「空字符串」特性,和其它厂牌的数据库,以及 ANSI SQL 92 的定义都不同。
参考文件:
http://www.adp-gmbh.ch/ora/misc/null.html
http://blog.darkthread.net/blogs/darkthreadtw/archive/2008/04/01/empty-string-in-oracle.aspx
------------------------------------------------------------------------------------------
(十二) Oracle 的「Transaction (事务;交易)」隔离层级
SQL Server 的 Isolation Level (事务隔离层级) 有七种,并可和 ADO.NET 做搭配;而 Oracle 的 Isolation Level 只有两种,分别为:ReadCommitted、Serializable,但 Oracle 的 ReadCommitted,和 SQL Server 的 ReadCommitted,名称虽然相同,但在功能和定义上皆不同。Oracle 的 ReadCommitted,类似于 SQL Server 2005 的「快照隔离 (Snapshot)」。
ReadCommitted 是 SQL Server 默认的 Isolation Level,其特性为当某一笔记录在「 修改 (Edit;Update)」时,别人都无法「读取」;ReadCommitted 也是 Oracle 默认的 Isolation Level,但其特性和 SQL Server 的版本完全不同,Oracle 的版本为,当某一笔记录在「修改」时,别人仍可以「读取」,但不能「修改」(该笔记录会被锁定,直到正在修改的那个人 Commit 或 Rollback)。
Oracle 的 ReadCommitted,其特性为,当一笔记录在被「修改」时,若有别人刚好也来「读取」这一笔记录,Oracle 会将这笔记录上一次被 Commit 的版本先取出来给其它人读取,避免造成「共享锁定」,亦即避免让其它人的浏览器或应用程序一直处于等待中,而无法进行其它的工作。
ADO.NET / Visual Studio 中用来联机 Oracle 的 Data Provider,不论是 OleDb、OracleClient、ODP.NET 皆是如此,和 SqlClient 的 Isolation Level 完全不同。
参考文件:
http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?URL=N&board_pc2020=oracle&index=3&id=3985&mode=&type_pc2020=sametitleLevel-2
http://www.oracle.com.cn/viewthread.php?tid=54983&extra=page%3D109%26amp%3Bfilter%3D0%26amp%3Borderby%3Ddateline%26amp%3Bascdesc%3DDESC
http://cherishchen.javaeye.com/blog/183229
http://msdn.microsoft.com/zh-tw/library/system.data.oracleclient.oracletransaction.isolationlevel.aspx
http://www.oracle.com/technology/global/cn/pub/articles/price_dbtrans_dotnet.html
http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/6/22.aspx
------------------------------------------------------------------------------------------
(十三) Oracle 的备份、还原方式
Oracle 的「备份、还原」,有下列四种方式:
* Cool Backup (Offline Backup)
* Hot Backup (Online Backup)
* RMAN
* Exp / Imp
到了 Oracle 10g 时,引入了新的 Data Pump 工具,且性能比旧的 Exp / Imp 要好,因其不必经由 Session 进行,但使用上较麻烦,必须先建立备份用的相关目录。其指令如下:
expdp userid=system/密码 tables=xxx,yyy directory=abc_dir => abc_dir: 此为 Oracle 目录,要先建立
impdp userid=system/密码 dumpfile=EXPDAT.DMP directory=abc_dir
而另一种最古老的 Emp / Imp 只能算是一种转存储工具,执行速度慢,只适合小型数据库。版工我测试结果,用 Export 指令备份 Oracle 11g 的整个 orcl 数据库,里面只有十八个新建的数据表 (每个表只有十多笔,到最多一两百笔记录),就花了半个多小时;Import 还原指令更慢,跑了一个多小时还没跑完,跑到隔天到公司上班才跑完。其指令如下,但它不是在 SQL Plus 里下指令,以 Windows 而言,是要在「命令提示字符 (cmd.exe)」里去下指令 (指令最后面不用加「分号」):
exp system/密码@orcl file=c:\orcl_081207.dmp full=y
imp system/密码@orcl full=y ignore=y file=c:\orcl_081207.dmp
如此即会汇出整个 orcl 数据库,包括:Index、Sequence、…等等。其中的参数,ignore=y 表示当汇入时,要覆盖同名的数据表;若没加此参数,则同名的表就不会汇入。
若要让 Exp / Imp 跑快一点,就不要下 full=y 的参数,改用 user mode (指定 owner=xxxx ) 的备份会比较快。此外,亦可只汇出或汇入某一个或某几个数据表,如下 (最后面不用加「分号」):
exp system/密码@orcl file=c:\orcl_081207.dmp tables=(table1,table2)
imp system/密码@orcl file=c:\orcl_081207.dmp tables=(table1)
若想看到指令的 help,可用如下的 help 参数:
exp -help
imp -help
此外,要执行 Emp / Imp 前,TNS (tnsnames.ora) 必须先正确设定。以 Windows Server 2003、Oracle 11g 而言,该档案位于下列目录:
C:\app\Administrator\product\11.1.0\db_1\NETWORK\ADMIN
tnsnames.ora 我的设定如下:
ORCL_S =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.1)(PORT = 1521))
)
(CONNECT_DATA =
(SID = ORCL)
)
)
亦可透过 Oracle 内建的图形界面工具「Net Manager」做设定。注意上述的 SID 一定要设定 (设成数据库的名称),但 Oracle 刚安装好时,SID 不一定会自动加上。设定好后,可用「tnsping 服务名称」指令,来测试是否有设定正确,以上述的 tnsnames.ora 而言,可在 Windows 操作系统的「命令提示字符」里,像这样下 tnsping 指令 (最后面不用加「分号」):
tnsping ORCL_S
参考文件:
http://big5.webasp.net/article/21/20961_print.htm
http://www.blueshop.com.tw/board/show.asp?subcde=BRD20081202144229DSV&fumcde=FUM200410061527578K1&rplcnt=11
------------------------------------------------------------------------------------------
(十四) Oracle 的存储过程,与 ASP.NET 的「分页」应用程序撰写
过去版工在用 SQL Server 和 Sybase 数据库,搭配 ASP.NET 2.0 程序撰写时,为了应付 GridView 控件的「分页」功能处理,都会写一种用来处理「分页」的 Stored Procedure,可参考本站的几篇系列文章 - ASP.NET 的数据分页:
http://www.cnblogs.com/WizardWu/archive/2008/09/28/1301616.html
http://www.cnblogs.com/WizardWu/archive/2008/09/07/1286270.html
http://www.cnblogs.com/WizardWu/archive/2008/08/06/1261589.html
http://www.cnblogs.com/WizardWu/archive/2008/08/02/1258832.html
SQL Server 和 Sybase 数据库的存储过程,都可直接 return 一个 ResultSet 格式的数据 (亦即有多行多列的表格式数据,如同 ADO.NET 中的 DataTable),给呼叫 (调用) 它的应用程序,如下所示,但 Oracle 却不能直接这样 return:
CREATE PROCEDURE dbo.pager_SqlServer2005
AS
SET @sqlDataTable = 'SELECT * FROM table1'
exec (@sqlDataTable)
RETURN
Oracle 有它另一套做法,只能透过 REF CURSOR 类型,去处理和返回「结果集」,但听说这样的性能并不好。不知为何 Oracle 干麻要这样自己搞另一套 (包括先前提到的「空字符串」和 Null),非得和其它厂牌的数据库与众不同才高兴,也搞得所有程序员得另外学习一套标准。有关 Oracle 的 Stored Procedure 和 REF CURSOR,可参考下列文件:
http://msdn.microsoft.com/zh-cn/library/ms971506.aspx
http://msdn.microsoft.com/zh-cn/library/4s2zbbsz(VS.80).aspx
http://msdn.microsoft.com/zh-tw/library/4s2zbbsz(VS.80).aspx (同上,但此为繁体中文版本)
http://support.microsoft.com/kb/308072/zh-tw
http://big5.chinaz.com:88/www.chinaz.com/Program/.NET/0H512HH007.html
http://www.phpq.net/oracle/oracle-stored-procedure-tutorial.html
http://www.bccn.net/Article/sjk/oracle/200709/6126.html
http://blog.csdn.net/dacula/archive/2005/03/01/306566.aspx
至于 ASP.NET 的 GridView「分页」功能,版工我是放弃 Oracle 的存储过程,改自己写一个 .NET 的函数,去接收 ObjectDataSource 控件传来的参数 (要撷取第几笔到第几笔的记录),将原本可在存储过程中处理的工作,搬到 .NET App_Code 文件夹里的 DAL Layer 来处理。
------------------------------------------------------------------------------------------