分类: Oracle
2008-04-09 20:46:25
来源:Oracle 作者:Catherine Devlin |
PL/SQL 比较适合于 Oracle 数据库中的编程任务。而大多数 Oracle 专业人员并不会局限于完全在数据库中进行工作。您可能曾经遇到过许多不便于使用甚至无法使用 PL/SQL 的任务,如操作磁盘上的文件、调用外部程序或 shell 脚本、执行复杂的文本分析以及频繁执行面向对象的工作。如果 PL/SQL 是您唯一的编程语言,那么可能会为您的 Oracle 工作带来很大的不便。
既然如此,应另外选择哪种语言呢?您的时间可能并不多,因此它应该是一种简单的语言,即易于学习和使用。某些动态语言(尤其是 Perl、Python、PHP 和 Ruby)正是因为其易用性而获得了越来越多的关注。动态语言的许多用户发现,他们编写有效代码的速度要比使用更传统的语言(如 C++ 或 Java)快很多倍。
尤其值得一提的是 Python,该语言不但易于使用,而且还更高效,它生成的代码不但具有可读性而且结构良好。因此,当您再看几个月前写的代码时,仍可以理解、修改并重用它。由于具备近乎自描述的外观,因此 Python 清晰、简洁的语法有时被称作“可执行的伪代码”。Python 是一种高度面向对象的语言,它易于学习并遵循良好的编程风格,即使那些没有经过正式软件工程培训的用户也可以使用它。由于易于学习,因此它无论是对于新手还是对于专家都具有很大的吸引力。
与 Perl 一样,Python 的功能可以满足各种软件需要;该语言的简单并不代表它肤浅和应用面窄。一旦您使用了 Python,您就不会再想使用第三种语言。
最后,Python 是免费的跨平台开放源代码软件。Python 实在值得一试!
本文并不尝试对 Python 进行全面的介绍或细致的讲解,而是创建一个有用的示例应用程序,用以演示对 Oracle 用户最重要的 Python 功能。如果您确信 Python 值得进一步研究,那么本文提供的链接可以为您提供更多信息。
快速入门
如果您正在运行 Linux,则可能已经拥有了 Python,它包含在常见的发行版本中。否则,请从 下载它。除了源代码以外,还提供了便于使用的 RPM 和 Windows 可执行安装程序。安装后,请确保 PATH 中包含 Python 可执行文件,然后在命令提示符下键入 python 启动交互式解释器。您将看到类似如下所示的信息:
c:\>python
Python 2.4 (#60, Nov 30 2004, 11:49:19) [MSC v.1310 32 bigt (Intel)] on win32
Type "help", "copyright", "credits", or "license" for more information.
>>>
>>> 提示符等待接受您发出的 Python 命令。与 SQL*Plus 一样,Python 允许您即席执行命令或在编写成熟的脚本之前试验命令。
按照惯例,您接下来将执行以下命令:
>>> print 'Hello, World'
解释器做出以下响应:
Hello, World
注意不要键入 Print 或 PRINT。在 Python 中,所有东西(命令、变量名等)都是区分大小写的。
使用文本文件
假设您用一个标准的 init.ora 文件来数据库中的所有默认参数。您需要将其内容与特定数据库实例的 init.ora 文件内容进行比较。
清单 1:init_default.ora
DB_BLOCK_SIZE=4096
COMPATIBLE=9.2.0.0.0
SGA_MAX_SIZE=104857600
SHARED_POOL_SIZE=50331648
清单 2:init_orcl.ora
FAST_START_MTTR_TARGET=300
SHARED_POOL_SIZE=50331648
DB_BLOCK_SIZE=8192
COMPATIBLE=9.2.0.0.0
UNDO_MANAGEMENT=AUTO
SGA_MAX_SIZE=135339844 |
首先打开要读取的 init_orcl.ora。
>>> initFile = open('init_orcl.ora')
您现在已经打开了该文件并指定了一个变量 initFile 来引用它。(Python 使用一个 = 进行赋值,并使用 == 进行比较,而 PL/SQL 分别使用 := 和 =。)注意,您不必声明 initFile 或指定它的数据类型;作为一种“动态类型”语言,Python 可以自动识别。
我们来看看结果如何。
>>> firstLine = initFile.readline()
>>> firstLine
'FAST_START_MTTR_TARGET=300\n'
其中的 readline 是对象 initFile 中定义的方法。如果您不熟悉面向对象的编程,则对您而言这将是一个全新的语言,而 Python 语言为您了解此概念提供了一个出色的环境。
从 PL/SQL 的使用经历中可以知道,readline 之后不需要圆括号,这是因为您并未向它传递任何参数。但在 Python 中,省略圆括号将导致意外的结果。
>>> firstLine = initFile.readline
>>> firstLine
您实际上并未调用 readline 方法,而是只将一个指针指向它并将其插入到 firstLine 中。这在某些高级编程技术中可能非常有用;例如,您实际上可以从分配给该函数的位置调用该函数。
>>> firstLine()
'SHARED_POOL_SIZE=50331648\n'
但对现在而言,只需记住在调用 Python 函数时必须使用 ()。下面我们向 firstLine 中插入一个全新的字符串以继续操作。
>>> firstLine = initFile.readline()
>>> firstLine
'DB_BLOCK_SIZE=8192\n'
自省
firstLine 结尾的 \n 是一个您并不需要的换行符。如何删除它?尽管 Python 提供了出色的文档,但您也不必参考它 - Python 的自省功能可以帮助您确定如何从提示符下执行正确操作。
在 Python 中,类似 firstLine 这样的字符串是一个对象。作为对象,它包含方法,即为操作它而定义的函数。下面我们来看看可以对 firstLine 执行哪些操作。
>>> type(firstLine)
>>> dir(firstLine)
['__add__', '__class__', '__contains__', '__delattr__',
'__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__',
'__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__',
'__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__rmod__', '__rmul__', '__setattr__',
'__str__', 'capitalize', 'center', 'count', 'decode',
'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum',
'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper',
'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex',
'rjust', 'rstrip', 'split', 'splitlines', 'startswith', 'strip',
'swapcase', 'title', 'translate', 'upper', 'zfill'] 能否执行 strip?根据上面显示的内容,应该可以执行。按照约定,一个简单的文档字符串存储在每个对象的 __doc__ 方法中。(类似 __doc__ 这样的以两个下划线开头和结尾的名称保留给系统定义的特殊方法和属性使用。)下面我们将打印 strip.__doc__,看看它是否是您所需的。
>>> print firstLine.strip.__doc__
S.strip([chars]) -> string or unicode
返回字符串 S 的一个副本,并删除了前导空格和后缀空格。
如果给出了 chars 且非 None,则删除 chars 中字符。
如果 chars 为 unicode,则在剥离前将把 S 转换为 unicode
>>> firstLine = firstLine.strip()
>>> firstLine
'DB_BLOCK_SIZE=8192'
接下来,您需要对 firstLine 中的参数与其值进行区分。尽管很想展示一下 Python 强大的正则表达式处理能力,但我们还是采用了一个更简单的方式:字符串方法 split()。
>>> firstLine.split('=')
['DB_BLOCK_SIZE', '8192']
变量和赋值
调用 split() 生成了一个由位于分隔符 = 之前和之后的字符串组成的列表。遗憾的是,由于您未将该结果指定给任何变量,因此结果只显示到屏幕,随后就不见了。这次,您将使用多个赋值来同时捕获两个结果。您可能想使用向上箭头键来省去一些键入操作。
>>> param, val = firstLine.split('=')
>>> param
'DB_BLOCK_SIZE'
>>> val
'8192'
实际上,您将需要分别存储多个参数的值。这种场合最适于使用一个称作字典的强大 Python 变量类型。字典是一个无序的键集,即值对;实际上,它与 Oracle 数据库中的普通(堆)表非常相似。键和值可以是数字、字符串或其他对象。您将创建一个空字典,并使用您到目前为止提取的信息填充它。
>>> initParams = {}
>>> initParams[param] = val
>>> initParams
{'DB_BLOCK_SIZE': '8192'}
现在,从打开的文件中提取另一行,并将该行也存储到字典中。这次,您将把 strip() 直接连接到 readline() 的结尾,就好象您在使用一个 UNIX 管道。
>>> nextLine = initFile.readline().strip()
>>> param, val = nextLine.split('=')
>>> initParams[param] = val
>>> initParams
{'DB_BLOCK_SIZE':'8192', 'COMPATIBLE': '9.2.0.0.0'} 编写脚本
现在,您已经以交互方式试用了解释器,下面您将准备编写一个 Python 脚本来处理整个文件。使用 Ctrl-D(在 Unix/Linux 中)或 Ctrl-Z(在 Windows 中)退出 Python 解释器,然后创建一个文本文件 - 称之为 readInitOra.py。
initFile = open('init_orcl.ora', 'r')
initParams = {}
rawTextLine = initFile.readline()
while rawTextLine:
param, val = rawTextLine.strip().split('=')
initParams[param] = val
rawTextLine = initFile.readline()
print initParams
在阅读此代码时,您很可能想到“命令结尾的分号哪去了?BEGIN 和 END 语句以及用于标记块的大括号哪去了?”实际上,Python 并不使用这些符号。按照惯例,几乎所有编程人员都使用换行符分隔命令,并使用缩进指示代码块,以便用户可以阅读代码。同时,Python 以外的大多数语言都要求他们使用一套不同的符号与编译器或解释器进行通信。使用两套符号将使代码杂乱不堪,并将产生一个很大的风险 - 显示给人眼的符号可能与显示给计算机的符号不同步,从而产生另人难以理解的错误。而 Python 可以像用户那样读取代码,从而消除了混乱并避免出现风险。
下面我们来看看代码操作。在操作系统提示符(而非 Python 解释器提示符)下,键入
c:\> python readInitOra.py
{'UNDO_MANAGEMENT':'AUTO', 'COMPATIBLE': '9.2.0.0.0',
'DB_BLOCK_SIZE':'8192', 'FAST_START_MTTR_TARGET' : '300',
'SGA_MAX_SIZE':157286400, 'SHARED_POOL_SIZE': '50331648'}
如果您习惯将 readInitOra.py 中的所有行缩进几个空格,则将使 Python 混淆并产生一个语法错误。缩进指示代码块,因此在不指示代码块的情况下请不要进行缩进。返回并确保不在块中的每一行都从第 1 列开始。
您实际上需要在多个位置使用此代码,因此我们将其从简单的脚本改进为接受参数的函数定义。
def read(fileName):
initFile = open(fileName, 'r')
initParams = {}
rawTextLine = initFile.readline()
while rawTextLine:
param, val = rawTextLine.strip().split('=')
initParams[param] = val
rawTextLine = initFile.readline()
return initParams 嵌套
下面,您需要创建一个包含 init_default.ora 中的默认参数的相似字典。您完全可以将其读取到全新的变量,但这里我们将介绍对象如何可靠地嵌套在 Python 中。您将创建一个简单的父目录 initParams,并针对每个 init.ora 文件在其中嵌套一个目录。您还将导入刚刚编写的文件,以便可以调用它的 read() 函数。创建一个名为 compareInitOra.py 的新文本文件。
import readInitOra
initParams = {}
# brackets denote a list that we can loop through
for fileName in ['init_orcl.ora', 'init_default.ora']:
initParams[fileName] = readInitOra.read(fileName)
print initParams
c:\> python compareInitOra.py
{'init_orcl.ora':
{'UNDO_MANAGEMENT':'AUTO', 'COMPATIBLE': '9.2.0.0.0',
'DB_BLOCK_SIZE':'8192', 'FAST_START_MTTR_TARGET': '300',
'SGA_MAX_SIZE':'157286400, 'SHARED_POOL_SIZE': '50331648'}
'init_default.ora':
{'COMPATIBLE':'9.2.0.0.0', 'DB_BLOCK_SIZE': '4096',
'FAST_START_MTTR_TARGET':'300', 'SGA_MAX_SIZE': '100663296',
'SHARED_POOL_SIZE': '50331648'}}
这次的输出包含一些空格,以帮助您查看嵌套结构。您可以轻松地编写 Python 代码以可靠地打印它,也可以使用 Python 的 pprint 模块,但由于我们是数据库用户,因此将该数据置入 Oracle 数据库中。
在 Python 中使用 SQL
要访问数据库,Python 解释器需要安装数据库模块。您有许多选择,这些选择均遵守标准化的 API 规范,并且曾经以编程方式使用过 ODBC 或 JDBC 的任何人均非常熟悉它们。您将使用 cx_Oracle,因为它比较易于安装。只需下载一个与您的 Python 和 Oracle 数据库版本匹配的 Windows 安装程序。
安装 cx_Oracle 后,返回 Python 命令行解释器进行试用。由于 cx_Oracle 是一个独立于核心 Python 语言的模块,因此在将其用于任何会话或脚本之前必须导入它。
>>> import cx_Oracle
请记住要使用大写字母!下面,我们将创建一个用于存储结果的表。
>>> orcl = cx_Oracle.connect('scott/tiger@orcl')
>>> curs = orcl.cursor()
>>> sql = """CREATE TABLE INIT_PARAMS
... ( fileName VARCHAR2(30),
... param VARCHAR2(64),
... value VARCHAR2(512) )"""
三个引号 (""") 是一个用于输入包含换行符的方便语法。Python 解释器将其提示符从 >>> 更改为 ... - 提醒您将继续从上一行开始的输入。
>>> curs.execute(sql)
>>> curs.close()
现在您的表已经准备就绪,下面我们将编写 recordInitOra.py 来填充它。
import readInitOra, cx_Oracle
initParams = {}
for fileName in ['init_orcl.ora', 'init_default.ora']:
initParams[fileName] = readInitOra.read(fileName)
orcl = cx_Oracle.connect('scott/tiger@orcl')
curs = orcl.cursor()
for fileName in initParams.keys():
for param in initParams[fileName].keys():
value = initParams[fileName][param]
sql = """INSERT INTO INIT_PARAMS VALUES
(:fileName, :param, :value)"""
bindVars = {'fileName':fileName,
'param':param, 'value':value}
curs.execute(sql, bindVars)
curs.close()
orcl.commit() 以上就是全部代码了。注意,您这次在 SQL 字符串中使用了绑定变量,并在一个单独字典中为它们提供了值。使用绑定变量可以帮助您摆脱 SPCSP(防止错误使用共享池协会)的麻烦。
从查询中获取结果略微复杂一些。对游标对象调用 execute() 后,可以使用 fetchone() 一次获取一行,也可以使用 fetchall() 获取所有行的列表。无论在哪种情况下,每一行均采用字节组(即可以由数值索引访问的有序值序列)的形式。例如,我们将编写 compareInitOra.py 来打印与 V$PARAMETER 中的当前值冲突的 init_orcl.ora 参数:
import readInitOra, cx_Oracle
def readLiveParams():
liveParams = {}
orcl = cx_Oracle.connect('scott/tiger@orcl')
curs = orcl.cursor()
curs.execute('SELECT name, value FROM V$PARAMETER')
row = curs.fetchone()
while row:
(param, val) = (row[0], row[1])
liveParams[param.upper()] = val
row = curs.fetchone()
return liveParams
liveParams = readLiveParams()
fileName = 'init_orcl.ora'
fileParams = readInitOra.read(fileName)
for (param, val) in fileParams.items():
liveVal = liveParams.get(param)
if liveVal != val:
print """For %s, V$PARAMETER shows %s,
but the file %s shows %s""" % \
(param, liveVal, fileName, val)
Python 的对象关系映射工具
您可能对对象关系映射 (ORM) 工具很感兴趣,这些工具可以取代 SQL 的编写并为编程人员提供一个面向对象的界面。Oracle TopLink 便是一个适用于 Java 的对象关系映射器的例子。用于 Python 的与 Oracle 兼容的 ORM 工具有:
Modeling Object-Relational Bridge for Python
forgetSQL
ll.orasql
SQLObject(马上或很快便将支持 Oracle)
sqlWrap.p
该脚本引入了一些您还没见过的技巧:
对字典 fileParams 调用 items() 将返回一个(键, 值)对列表。可以通过在 for 语句中指定两个循环变量来遍历这些键值。
调用 liveParams.get(param) 的工作方式类似于 liveParams[param],区别在于如果在 liveParams 中未找到参数,则将返回一个错误 - 非常类似于“ORA-01403:no data found in PL/SQL”消息。而 liveParams.get(param) 将在 liveParams 中不存在参数的情况下返回 None。
Python 可以使用 % 运算符执行字符串替换。与 C 的 printf 相似,%s 表示将在该点插入一个字符串形式的值。这些值按顺序从 % 之后的字节组中提取。
最后一行代码比您在没有换行符的情况下进行键入运行时间更长,因此您使用了一个反斜线,从而打破了 Python 将换行符解释为命令结尾这一通常的规则。 Web 发布
最后,我们将通过 Python Web 应用程序公开此功能。首先,您需要选择要使用的 Python Web 应用程序平台,这可能要比实际编写应用程序困难!由于它们非常易于用 Python 编写,因此有大量平台可供使用。最流行的平台是 Zope;Zope 应用程序可以完全从易于用户使用的网页中创建和管理。或者,如果您对 Ruby on Rails 很感兴趣,则可能要考虑 TurboGears 或 Django - Python 自身的从上自下的应用程序构建器。TurboGears 也支持在应用程序中方便地包含 AJAX。现在,我们将选择一个非常简单的框架 CherryPy。请在此处下载它。
首先,对 compareInitOra.py 稍加修改(如下所示),创建一个 html() 函数,从而返回包含所需文本和标记的字符串。然后,只需要编写七行多代码即可将该函数公开为 Web 应用程序。
清单 3:
运行该脚本时,它开始以 Web 服务器的形式(默认情况下)在端口 8080 上工作。将浏览器指向 将提供您所需的 HTML 页面。 结论
衷心希望 Python 的易用性、简洁行和可读行能够引起您的兴趣。您对 Python 的强大功能还不了解。它的功能包括简洁的异常处理、单元测试、面向对象、实用编程、GUI 工具包、Web 框架、XML、Web 服务 - 编程人员几乎可以执行任何操作。随着工作变得更高级,您不必“过渡”到其他语言。
\n" % (val)
else
result += "%s (file) %s (live)\n" % (val, liveVal)
return result
import cherrypy
class WebApp:
def index(self, initOraFile='init.ora', instance='orcl'):
return html(initOraFile, instance)
index.exposed = True
cherrypy.root = WebApp()
cherrypy.server.start()