首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > SQL Server >

PL\SQL用户指南与参照10.2 转载

2012-07-15 
PL\SQL用户指南与参考10.2转载5、对象类型实例:实数有理数能够表现成两个整数相除的形式,一个分子和一个分

PL\SQL用户指南与参考10.2 转载

5、对象类型实例:实数

有理数能够表现成两个整数相除的形式,一个分子和一个分母。同大多数语言一样,PL/SQL并没有实数类型或是用于实数操作的预定义操作符。现在让我们就用对象类型来弥补这个缺失。首先,编写下面的对象说明:

CREATE?TYPE?rational?AS?OBJECT(
??num???INTEGER,
??den???INTEGER,
??MAP?MEMBER?FUNCTION?CONVERT
????RETURN?REAL,
??MEMBER?PROCEDURE?normalize,
??MEMBER?FUNCTION?reciprocal
????RETURN?rational,
??MEMBER?FUNCTION?plus(x?rational)
????RETURN?rational,
??MEMBER?FUNCTION?LESS(x?rational)
????RETURN?rational,
??MEMBER?FUNCTION?times(x?rational)
????RETURN?rational,
??MEMBER?FUNCTION?divby(x?rational)
????RETURN?rational,
??PRAGMA?RESTRICT_REFERENCES(DEFAULT,?RNDS,?WNDS,?RNPS,?WNPS)
);

PL/SQL不允许操作符重载。所以我们必须定义方法plus(),less()(minus是保留关键字),times()和divby()来替代操作符+、-、*和/。

下一步,创建下面的独立存储函数,它们会被方法normalize()调用:

CREATE?FUNCTION?gcd(x?INTEGER,?y?INTEGER)
??RETURN?INTEGER?AS
??--?find?greatest?common?divisor?of?x?and?y
??ans???INTEGER;
BEGIN
??IF?(y?<=?x)?AND(x?MOD?y?=?0)?THEN
????ans????:=?y;
??ELSIF?x?<?y?THEN
????ans????:=?gcd(y,?x);???--?recursive?call
??ELSE
????ans????:=?gcd(y,?x?MOD?y);???--?recursive?call
??END?IF;

??RETURN?ans;
END;

下面是对象类型体的内容:

CREATE?TYPE?BODY?rational?AS
??MAP?MEMBER?FUNCTION?CONVERT
????RETURN?REAL?IS
??--?convert?rational?number?to?real?number
??BEGIN
????RETURN?num?/?den;
??END?CONVERT;
??MEMBER?PROCEDURE?normalize?IS
????--?reduce?fraction?num?/?den?to?lowest?terms
????g???INTEGER;
??BEGIN
????g??????:=?gcd(num,?den);
????num????:=?num?/?g;
????den????:=?den?/?g;
??END?normalize;
??MEMBER?FUNCTION?reciprocal
????RETURN?rational?IS
????--?return?reciprocal?of?num?/?den
??BEGIN
????RETURN?rational(den,?num);???--?call?constructor
??END?reciprocal;
??MEMBER?FUNCTION?plus(x?rational)
????RETURN?rational?IS
????--?return?sum?of?SELF?+?x
????r???rational;
??BEGIN
????r????:=?rational(num?*?x.den?+?x.num?*?den,?den?*?x.den);
????r.normalize;
????RETURN?r;
??END?plus;
??MEMBER?FUNCTION?LESS(x?rational)
????RETURN?rational?IS
????--?return?difference?of?SELF?-?x
????r???rational;
??BEGIN
????r????:=?rational(num?*?x.den?-?x.num?*?den,?den?*?x.den);
????r.normalize;
????RETURN?r;
??END?LESS;
??MEMBER?FUNCTION?times(x?rational)
????RETURN?rational?IS
????--?return?product?of?SELF?*?x
????r???rational;
??BEGIN
????r????:=?rational(num?*?x.num,?den?*?x.den);
????r.normalize;
????RETURN?r;
??END?times;
??MEMBER?FUNCTION?divby(x?rational)
????RETURN?rational?IS
????--?return?quotient?of?SELF?/?x
????r???rational;
??BEGIN
????r????:=?rational(num?*?x.den,?den?*?x.num);
????r.normalize;
????RETURN?r;
??END?divby;
END;

七、声明并初始化对象

只要对象类型在模式中定义了,我们就可以在任何PL/SQL块、子程序或包中引用它来声明对象。例如,我们可以使用对象类型作为属性、字段、变量、绑定变量、记录的域、表元的素、形式参数或函数返回值的数据类型。在运行时,对象类型的实例会被创建,也就是对象实例被初始化。

1、定义对象

我们可以在使用内置类型(如CHAR或NUMBER)的地方使用对象类型。在下面的块中,我们声明了Rational类型的对象r。然后调用构造函数初始化对象,把值6和8分别赋给属性num和den。

DECLARE
??r???rational;
BEGIN
??r????:=?rational(6,?8);
??DBMS_OUTPUT.put_line(r.num);???--?prints?6
END;

我们还可以把对象作为函数和过程的形式参数。那样就能把对象从一个子程序传递到另一个子程序了。在下面的例子中,我们使用对象类型Account作为形式参数:

DECLARE
??...
??PROCEDURE?open_acct?(new_acct?IN?OUT?Account)?IS?...

下面,我们把Accout作为函数的返回类型来使用:

DECLARE
??...
??FUNCTION?get_acct?(acct_id?IN?INTEGER)?RETURN?Account?IS?...

2、初始化对象

如果不调用构造函数初始化对象,那它会被自动赋上空值。也就是说对象本身为空,不单单指它的属性。如下例:

DECLARE
??r???rational;???--?r?becomes?atomically?null
BEGIN
??r????:=?rational(2,?3);???--?r?becomes?2/3
END;

一个空对象总不能等于另一个对象。实际上,拿一个空对象与另一个对象比较结果总是NULL.同样,如果把一个空对象或NULL赋给另一个对象,那么被赋值的对象也为空,示例如下:

DECLARE
??r???rational;
BEGIN
??r????:=?rational(1,?2);???--?r?becomes?1/2
??r????:=?NULL;???--?r?becomes?atomically?null
??IF?r?IS?NULL?THEN?...???--?condition?yields?TRUE

一个好的编程习惯就是在声明的时候就为对象初始化,例如:

DECLARE
??r?Rational?:=?Rational(2,3);???--?r?becomes?2/3

3、PL/SQL如何对待未初始化对象

在表达式中,未初始化的对象属性值为NULL。如果为一个未初始化的对象属性赋值,就会引起预定义异常ACCESS_INTO_NULL。当对未初始化的对象或是它的属性使用IS NULL做比较时,结果总为TRUE。

下面的例子就演示了空对象和含有空属性的对象之间的差异:

DECLARE
??r???rational;???--?r?is?atomically?null
BEGIN
??IF?r?IS?NULL?THEN?...???--?yields?TRUE
??IF?r.num?IS?NULL?THEN?...???--?yields?TRUE
??r????????:=?rational(NULL,?NULL);???--?initializes?r
??r.num????:=?4;???--?succeeds?because?r?is?no?longer?atomically?null
??--?even?though?all?its?attributes?are?null
??r????????:=?NULL;???--?r?becomes?atomically?null?again
??r.num????:=?4;???--?raises?ACCESS_INTO_NULL
EXCEPTION
??WHEN?ACCESS_INTO_NULL?THEN
????...;
END;

调用一个未初始化对象的方法会引起预定义异常NULL_SELF_DISPATCH。如果把未初始化对象的属性作为IN模式参数进行传递时,就跟传递一个NULL参数一样;如果把它作为OUT或IN OUT模式参数传递,并且尝试为其赋值,就会引起异常。

八、访问属性

我们只能按名称来引用属性,不可以使用它的位置来进行引用。在下面的例子中,我们先把属性den赋给变量denominator,然后把变量numerator的值赋给属性num。

DECLARE
??r?????????????rational?:=?rational(NULL,?NULL);
??numerator?????INTEGER;
??denominator???INTEGER;
BEGIN
??...
??denominator????:=?r.den;
??r.num??????????:=?numerator;
END;

下面再看一个稍微复杂一点的对象嵌套例子:

CREATE?TYPE?address?AS?OBJECT(
??street?????VARCHAR2(30),
??city???????VARCHAR2(20),
??state??????CHAR(2),
??zip_code???VARCHAR2(5)
);

CREATE?TYPE?student?AS?OBJECT(
??NAME???????????VARCHAR2(20),
??home_address???address,
??phone_number???VARCHAR2(10),
??status?????????varcahr2(10),
??advisor_name???VARCHAR2(20),
??...
);

这里要注意的是,zip_code是对象类型Address的一个属性,而Address又是对象Student的属性home_address的数据类型。如果s是Student的对象的话,我们就可以像下面这样访问它的zip_code属性:

s.home_address.zip_code

九、定义构造函数

默认情况下,我们不需要为对象类型定义构造函数,因为系统会提供一个接受与每个属性相对应的参数的构造函数。

我们也许想定义自己的构造函数:

    为某些属性提供默认值,这样就能确保属性值的正确性而不必依赖于调用者所提供的每一个属性值。 避免许多特殊用途的过程只初始化对象的不同部分。 当新的属性加到对象类型中时,避免更改调用构造函数的应用程序中的代码。构造函数也许需要一些新的代码,例如把属性设置为空,但我们还需要保持方法签名不变,这样可以使已存在的构造函数调用继续工作。
CREATE?OR?REPLACE?TYPE?rectangle?AS?OBJECT(
??--?The?type?has?3?attributes.
??LENGTH???NUMBER,
??width????NUMBER,
??area?????NUMBER,
??--?Define?a?constructor?that?has?only?2?parameters.
??CONSTRUCTOR?FUNCTION?rectangle(LENGTH?NUMBER,?width?NUMBER)
????RETURN?SELF?AS?RESULT
);
/

CREATE?OR?REPLACE?TYPE?BODY?rectangle?AS
??CONSTRUCTOR?FUNCTION?rectangle(LENGTH?NUMBER,?width?NUMBER)
????RETURN?SELF?AS?RESULT?AS
??BEGIN
????SELF.LENGTH????:=?LENGTH;
????SELF.width?????:=?width;
????--?We?compute?the?area?rather?than?accepting?it?as?a?parameter.
????SELF.area??????:=?LENGTH?*?width;
????RETURN;
??END;
END;
/

DECLARE
??r1???rectangle;
??r2???rectangle;
BEGIN
??--?We?can?still?call?the?default?constructor,?with?all?3?parameters.
??r1????:=?NEW?rectangle(10,?20,?200);
??--?But?it?is?more?robust?to?call?our?constructor,?which?computes
??--?the?AREA?attribute.?This?guarantees?that?the?initial?value?is?OK.
??r2????:=?NEW?rectangle(10,?20);
END;
/

十、调用构造函数

只要是能够调用普通函数的地方,我们就可以调用构造函数。跟所有的函数一样,构造函数也可以作为表达式的一部分而被调用,如下例所示:

DECLARE
??r1???rational?:=?rational(2,?3);

??FUNCTION?average(x?rational,?y?rational)
????RETURN?rational?IS
??BEGIN
????...
??END;
BEGIN
??r1????:=?average(rational(3,?4),?rational(7,?11));

??IF?(rational(5,?8)?>?r1)?THEN
????...
??END?IF;
END;

当我们为构造函数传递参数的时候,调用会把对象中需要初始化的属性赋上初始值。如果是调用默认的构造函数,我们就需要为每个属性指定一个初始值;跟常量和变量不同,属性是不可以有默认值的。下例中,第n个参数为第n个属性赋值:

DECLARE
??r???rational;
BEGIN
??r????:=?rational(5,?6);???--?assign?5?to?num,?6?to?den
??--?now?r?is?5/6

十一、调用方法

跟打包子程序一样,方法也是使用点标志来调用的。示例如下:

DECLARE
??r???rational;
BEGIN
??r????:=?rational(6,?8);
??r.normalize;
??DBMS_OUTPUT.put_line(r.num);???--?prints?3
END;

如下例所示,我们可以连续调用方法。执行顺序从左到右。首先,成员函数reciprocal()会被调用,然后成员过程normalize()被调用。

DECLARE
??r???rational?:=?rational(6,?8);
BEGIN
??r.reciprocal().normalize;
??DBMS_OUTPUT.put_line(r.num);???--?prints?4
END;

在SQL语句中,调用无参数的方法需要使用一个空的参数列表。在过程化语句中,空的参数列表是可选的,除非我们使用链式调用,这时除了最后一个调用之外其他的都需要空的参数列表。

我们不能把过程作为连续调用的一部分,因为过程是作为语句使用而不是表达式。所以,像下面这样的语句是不允许的:

r.normalize().reciprocal;???--?not?allowed

同样,如果连续调用两个函数,第一个函数必须返回一个能传入第二个函数的对象。对于静态方法,我们使用下面的语法:

type_name.method_name

这种调用是不需要对象实例的。从子类实例中调用方法时,实际执行的方法是由类的继承关系决定的。如果子类覆盖了基类的方法,子类的方法就会被调用;否则的话,基类的方法会被调用。这种情况称为动态方法分派。

十二、通过REF修饰符共享对象

在真实世界中的大多数对象都要比有理数类型庞大而且复杂。如果对象比较大的话,把对象副本从一个子程序传递到另一个子程序时效率就可能会很低。这时如果使用对象共享就很有意义了,我们可以使用一个指向对象的引用来引用所需要的对象。

共享有两个重要的好处。首先,避免了不必要的数据重复。其次,在共享的对象内容更新时,任何引用所指向的内容也会被立即更新。如下面的例子:

CREATE?TYPE?home?AS?OBJECT(
??address??????VARCHAR2(35),
??owner????????VARCHAR2(25),
??age??????????INTEGER,
??style????????VARCHAR(15),
??floor_plan???BLOB,
??price????????REAL(9,?2),
??...
);
/

CREATE???TABLE?homes?OF?home;

修改一下Person,我们就能建立家庭的模型,几个人共享一个家。我们可以使用修饰符REF来声明引用:

CREATE?TYPE?person?AS?OBJECT(
??first_name?????VARCHAR2(10),
??last_name??????VARCHAR2(15),
??birthday???????DATE,
??home_address???REF?home,???--?can?be?shared?by?family
??phone_number???VARCHAR2(15),
??ss_number??????INTEGER,
??mother?????????REF?person,???--?family?members?refer?to?each?other
??father?????????REF?person,
??...
);

我们可以把变量、参数、字段或属性声明为引用。而且,我们还可以在SQL数据操作语句中把引用当作输入或输出变量使用。但是,我们不可以通过引用调用对象的内容。比如表达式x.attribute,其中x是一个引用,PL/SQL无法找到存放对象的数据表。例如,下面的赋值语句就是不允许的:

DECLARE
??p_ref??????REF?person;
??phone_no???VARCHAR2(15);
BEGIN
??phone_no????:=?p_ref.phone_number;???--?not?allowed
END;

解决办法就是用函数DEREF或调用包UTL_REF来访问对象。

1、向前类型定义

我们只能引用已经存在的模式对象。下例中,第一个CREATE TYPE语句是不允许的,因为它引用的Department并不存在:

CREATE?TYPE?employee?AS?OBJECT(
??NAME???VARCHAR2(20),
??dept???REF?department,???--?not?allowed
??...
);

CREATE?TYPE?department?AS?OBJECT(
??"number"???INTEGER,
??manager????employee,
??...
);

把上面两个CREATE TYPE语句调换位置也不会起作用,因为这两个对象类型是互相依赖的。对象类型Employee有着一个Department类型的属性,而 Department又同样有着一个Employee类型的属性。为了解决这个问题,我们应该使用特殊的CREATE TYPE语句,我称它为向前类型定义,这样我们就可以互相引用两个独立的对象类型。现在使用下面的语句:

要调试上面的例子,我们只要把下面的语句提前即可:

CREATE?TYPE?Department;???--?forward?type?definition
--?at?this?point,?Department?is?an?incomplete?object?type

向前类型定义创建的对象类型被称为不完全对象类型(incomplete object type),因为它没有属性和方法。一个不纯的不完全对象类型有属性,但编译时会发生错误,因为它引用了一个未确定的类型。例如,下面的CREATE TYPE语句就会因对象类型Address未确定而出错:

CREATE?TYPE?Customer?AS?OBJECT?(
??id?????NUMBER,
??name???VARCHAR2(20),
??addr???Address,???--?not?yet?defined
??phone??VARCHAR2(15)
);

如果使用了向前声明,我们就可以推迟对象类型Address的定义,并且,不完全类型Customer也可以被其他应用程序的开发者引用。

十三、操作对象

我们可以在CREATE TABLE语句中把某个字段指定为对象类型。一旦表被建立,我们就可以用SQL语句把对象插入表中,选取它的属性,调用它的方法更新它的状态。

注意:访问远程的或分布式对象都是不允许的。

在下面SQL*Plus脚本中,INSERT语句调用对象类型Rational的构造函数,然后插入resulting对象。SELECT语句检索属性num的值。UPDATE语句调用成员方法reciprocal(),在交换属性num和den之后,返回Retional的值。要注意的是,在引用属性或方法时,表别名是必须的。

CREATE?TABLE?numbers?(rn?Rational,?...)
/
INSERT?INTO?numbers?(rn)?VALUES?(Rational(3,?62))???--?inserts?3/62
/
SELECT?n.rn.num?INTO?my_num?FROM?numbers?n?...???--?returns?3
/
UPDATE?numbers?n?SET?n.rn?=?n.rn.reciprocal()?...?--???yields?62/3

用这种方法初始化对象时,对象在数据库表之外是没有标识的。但是,对象类型是独立于表而存在的,可以用其他方式创建对象。

下例中,我们创建一个存放对象类型Retional的表。这样包含对象类型的表称为对象表。一行中的每一列都与对象的属性对应。行与行间的列值是不同的。

CREATE?TABLE?rational_nums?OF?Rational;

对象表中每行都有一个对象标识,能够唯一辨识一个存放在数据库中的对象。

1、查询对象

假定我们要在SQL*Plus中运行下面的脚本,创建一个对象类型Person和一个对象表persons,并且我们为该表填充一些数据:

CREATE?TYPE?person?AS?OBJECT(
??first_name?????VARCHAR2(15),
??last_name??????VARCHAR2(15),
??birthday???????DATE,
??home_address???address,
??phone_number???VARCHAR2(15)
)
/

CREATE?TABLE?persons?OF?person
/

下面子查询能产生一个只包含Person对象属性的结果集:

BEGIN
??INSERT?INTO?employees???--?another?object?table?of?type?Person
????SELECT?*
??????FROM?persons?p
?????WHERE?p.last_name?LIKE?'%Smith';

要返回对象结果集,我们就必须使用VALUE函数。

使用VALUE函数

跟我们所期望的一样,函数VALUE能返回对象值。VALUE会把一个相关的变量作为它的参数。(在这里,相关变量就是行变量或与对象表中的一行相关联的表别名)。例如,要返回Person对象的结果集,要向下面这样使用VALUE:

BEGIN
??INSERT?INTO?employees
????SELECT?VALUE(p)
??????FROM?persons?p
?????WHERE?p.last_name?LIKE?'%Smith';

在下面的例子中,我们可以使用VALUE来返回一个特定的Person对象:

DECLARE
??p1???person;
??p2???person;
??...
BEGIN
??SELECT?VALUE(p)
????INTO?p1
????FROM?persons?p
???WHERE?p.last_name?=?'Kroll';

??p2????:=?p1;
??...
END;

p1是一个本地的Person对象,它是名为"Kroll"的存储对象的一个副本,而p2是另一个本地Person对象,它是p1的副本。如下例所示,我们可以使用这些变量来访问和更新它们所引用的对象:

BEGIN
??p1.last_name????:=?p1.last_name?||?'?Jr';
END;

现在,本地的Person对象p1的名字为"Kroll Jr"。

使用REF函数

我们可以用函数REF来检索引用,同VALUE一样,它也把一个相关的变量作为它的参数。下例中,我们检索一个或多个指向Person对象的引用,然后把引用插入表person_refs中:

BEGIN
??INSERT?INTO?person_refs
????SELECT?REF(p)
??????FROM?persons?p
?????WHERE?p.last_name?LIKE?'%Smith';
END;

下面的例子我们同时检索一个引用和一个属性:

DECLARE
??p_ref?????????REF?person;
??taxpayer_id???VARCHAR2(9);
BEGIN
??SELECT?REF(p),?p.ss_number
????INTO?p_ref,?taxpayer_id
????FROM?persons?p
???WHERE?p.last_name?=?'Parker';???--?must?return?one?row
??...
END;

在最后一个例子中,我们更新一个Person对象的属性:

DECLARE
??p_ref??????????REF?person;
??my_last_name???VARCHAR2(15);
BEGIN
??SELECT?REF(p)
????INTO?p_ref
????FROM?persons?p
???WHERE?p.last_name?=?my_last_name;

??UPDATE?persons?p
?????SET?p?=?person('Jill',?'Anders',?'11-NOV-67',?...)
???WHERE?REF(p)?=?p_ref;
END;
测试dangling引用

如果一个引用所指向的对象被删除了,那么它就会指向一个不存在的对象,我们称这样的引用为dangling引用。要测试这种情况,我们应该使用 SQL的IS DANGLING语句。假设关系表department的manager字段引用了对象表中的Employee对象。我们就可以使用下面的UPDATE语句来把"dangling"引用转为空值:

UPDATE?department
???SET?manager?=?NULL
?WHERE?manager?IS?DANGLING;
使用DEREF函数

我们不可以在PL/SQL过程语句中。(DEREF是dereference的缩写。当我们反引用一个指针时,我们就能获取它所指向的值。) DEREF把对象的引用作为它的参数值,然后返回这个引用所指向的对象。如果引用是"dangling"的,DEREF就会返回一个空对象。

在下面的例子中,我们可以反引用一个指向Person对象的引用。要注意的是,我们是从dummy表dual中取得引用值的。我们不需要指定对象表和检索条件,因为每个存放于对象表的对象都有唯一的互斥的对象标识,它是每一个指向对象的引用的一部分。

DECLARE
??p1??????person;
??p_ref???REF?person;
??NAME????VARCHAR2(15);
BEGIN
??...
??/*?Assume?that?p_ref?holds?a?valid?reference
??to?an?object?stored?in?an?object?table.?*/

??SELECT?DEREF(p_ref)
????INTO?p1
????FROM?DUAL;

??NAME????:=?p1.last_name;
END;

我们可以在后续的SQL语句中用DEREF进行反引用操作,如下例所示:

CREATE?TYPE?personref?AS?OBJECT(
??p_ref???REF?person
)
/

DECLARE
??NAME?????VARCHAR2(15);
??pr_ref???REF?personref;
??pr???????personref;
??p????????person;
BEGIN
??...
??/*?Assume?pr_ref?holds?a?valid?reference.?*/
??SELECT?DEREF(pr_ref)
????INTO?pr
????FROM?DUAL;

??SELECT?DEREF(pr.p_ref)
????INTO?p
????FROM?DUAL;
??...
END;
/

下面是一个我们不能在过程语句中使用DEREF函数的例子:

BEGIN
??...
??p1?:=?DEREF(p_ref);???--?not?allowed

在SQL语句中,我们可以使用点标志通过一个对象字段来引用它的属性,也可以通过一个对象字段的属性引用另一个属性。如果使用了表别名,那么就还能通过引用字段来访问属性。例如,下面的语法就是有效的:

table_alias.object_column.ref_attribute
table_alias.object_column.ref_attribute.attribute
table_alias.ref_column.attribute

假设我们在SQL*Plus中运行下面的脚本来创建对象类型Address和Person,对象表persons:

CREATE?TYPE?address?AS?OBJECT(
??street?????VARCHAR2(35),
??city???????VARCHAR2(15),
??state??????CHAR(2),
??zip_code???INTEGER
)
/

CREATE?TYPE?person?AS?OBJECT(
??first_name?????VARCHAR2(15),
??last_name??????VARCHAR2(15),
??birthday???????DATE,
??home_address???REF?address,???--?shared?with?other?Person?objects
??phone_number???VARCHAR2(15)
)
/

CREATE???TABLE?persons?OF?person
/

引用属性home_address对应对象表persons中的一个引用,该引用指向存放在其他表中的Address对象的字段。填充数据表之后,我们就可以像下面这样反引用它的引用来得到特定的address:

DECLARE
??addr1?Address;
??addr2?Address;
??...
BEGIN
??SELECT?DEREF(home_address)
????INTO?addr1
????FROM?persons?p
???WHERE?p.last_name?=?'Derringer';
END;

在下面的例子中,我们可以通过引用字段home_address来找到属性street。这种情况下,我们必须要使用表别名。

DECLARE
??my_street???VARCHAR2(25);
??...
BEGIN
??SELECT?p.home_address.street
????INTO?my_street
????FROM?persons?p
???WHERE?p.last_name?=?'Lucas';
END;

2、插入对象

我们可以使用insert语句为对象表添加一条记录。下例中,我们向对象表persons插入一个Person对象:

BEGIN
??INSERT?INTO?persons
???????VALUES?('Jenifer',?'Lapidus',?...);
END;

另外,我们还可以使用对象类型Person的构造函数来插入记录:

BEGIN
??INSERT?INTO?persons
???????VALUES?(person('Albert',?'Brooker',?...));
END;

下例中,我们可以使用RETURNING子句把对象Person的引用保存在本地变量中。注意一下这个子句是如何模拟SELECT语句的。我们也可以在UPDATE和DELETE语句中使用RETURNING子句。

DECLARE
??p1_ref???REF?person;
??p2_ref???REF?person;
BEGIN
??INSERT?INTO?persons?p
???????VALUES?(Person('Paul',?'Chang',?...))
????RETURNING?REF(p)
?????????INTO?p1_ref;

??INSERT?INTO?persons?p
???????VALUES?(Person('Ana',?'Thorne',?...))
????RETURNING?REF(p)
?????????INTO?p2_ref;
END;

要往对象表中插入对象,我们还可以利用返回同样对象类型的子查询来进行操作。示例如下:

BEGIN
??INSERT?INTO?persons2
????SELECT?VALUE(p)
??????FROM?persons?p
?????WHERE?p.last_name?LIKE?'%Jones';
END;

拷贝到对象表person2的行被赋予新的对象标识。对象标识是不会从对象表persons中拷贝出来的。

下面的脚本创建一个名为department的关系表,其中有一个类型为Person的字段,然后向表中插入一条记录。注意如何使用构造函数Person()为字段manager提供值。

CREATE?TABLE?department?(
??dept_name?VARCHAR2(20),
??manager?person,
??LOCATION?VARCHAR2(20))
/
INSERT?INTO?department
?????VALUES?('Payroll',?Person('Alan',?'Tsai',?...),?'Los?Angeles')
/

存放到字段manager中的新Person对象是不能被引用的,因为它是被存放在字段中(不是行中)的,也就没有对象标识。

3、更新对象

如果要修改对象表中的对象属性,我们可以像下面这样使用UPDATE语句:

BEGIN
??UPDATE?persons?p
?????SET?p.home_address?=?'341?Oakdene?Ave'
???WHERE?p.last_name?=?'Brody';

??UPDATE?persons?p
?????SET?p?=?person('Beth',?'Steinberg',?...)
???WHERE?p.last_name?=?'steinway';
END;

4、删除对象

我们可以用DELETE语句从对象表中删除对象。要有选择的删除对象,我们就得在WHERE子句中进行指定:

BEGIN
??DELETE?FROM?persons?p
????????WHERE?p.home_address?=?'108?Palm?Dr';
END;

热点排行