2.5 关系范式
函数依赖
关系范式
分解关系数据库逻辑设计
– 针对具体问题,如何构造一个适合于它的数据模式
– 数据库逻辑设计的工具 ──关系数据库的规范化理论数据依赖 (函数依赖,多值依赖 )
是通过一个关系中属性间值的相等与否体现出来的数据间的相互关系
是现实世界属性间相互联系的抽象
是数据内在的性质
是语义的体现函数依赖
设 R 是一关系模式,且有属性集
R, R
函数依赖

在 R上成立 当且仅当对任意合法关系 r(R),若 r 的任意两条元组 t1 和 t2 在属性集?上的值相同,则他们在属性集?上的值也相同,即,
t1[?] = t2 [?]? t1[? ] = t2 [? ]
K 是关系模式 R 的超键当且仅当 K? R
K 是 R 的候选键当且仅当,
K? R,并且没有 K 使 R
函数依赖的使用:
我们用函数依赖来,
检查关系在给定函数依赖之下是否合法,
若关系 r 在函数依赖集 F 下是合法的,则称 r 满足
F.
对合法关系集合指定约束如果 R 上的所有合法关系都满足 F,则称 F 在 R上成立,
注意,关系模式的特定实例可能满足一函数依赖,但该函数依赖不是在所有合法实例上成立,
平凡依赖被所有关系实例都满足的函数依赖称为平凡的例如
customer-name,loan-number? customer-
name
customer-name? customer-name
一般地,若 则 是平凡的
CTO( CNO,TNAME,ADDRESS,OFFICE),
CNO?TNAME,TNAME? ADDRESS
transitive dependency:
CNO→ADDRESS
如下的函数依赖是传递的,
,( ),,,
称?传递依赖于?,
函数依赖:
if:
,,
部分依赖于?.
CTO( CNO,TNAME,ADDRESS,OFFICE),
CNO?TNAME,TNAME? ADDRESS
partial dependency:
(CNO,TNAME)→ADDRESS
部分依赖:
给定函数依赖集 F,存在其他函数依赖被 F 逻辑蕴含,
– 例如,如果 A? B 且 B? C,则可推出 A? C
被 F 逻辑蕴含的全体函数依赖的集合称为 F 的闭包,
– 用 F+ 表示 F 的闭包,
可利用 Armstrong公理找出 F+,
– 若,则 (自反 )
– 若,则 (增广 )
– 若 且,则 (传递 )
这些规则是
– 正确的 (只产生确实成立的函数依赖 )
– 完备的 (产生所有成立的函数依赖 ).
函数依赖集的闭包:
下列过程计算函数依赖集 F的闭包,
F+ = F
repeat
for each F+中的函数依赖 f
对 f 应用自反和增广规则将结果函数依赖加入 F+
for each F+中的一对函数依赖 f1 和 f2
if 若 f1 和 f2 可利用传递规则合并
then将结果函数依赖加入 F+
until F+ 不再变化注意,后面会介绍完成此任务的另一过程扩展:
可用下列规则进一步简化 F+ 的手工计算,
– 若 与 成立,则 成立 (合并 )
(, ;,; )
– 若 成立,则 与 成立 (分解 )
(,, )
– 若 与 成立,则 成立 (伪传递 )
(,;, )
属性集的闭包
给定属性集合?,定义? 在 F 下的闭包 (记做?+) 为被?
在 F 下函数决定的属性的集合,
is in F++
计算?+ 的算法
result,=?;
while (result 有变化 ) do
for each in F do
begin
if result then result,= result
end
R = (A,B,C,G,H,I),
F = {A? B,A? C,CG? H,CG? I,B? H}
求,(AG)+
1,result = AG
2,result = ABCG (A? C and A? B)
3,result = ABCGH (CG? H and CG? AGBC)
4,result = ABCGHI (CG? I and CG? AGBCH)
例:
属性闭包的用法:
属性闭包算法有多种用途,
测试超键,
– 为检测?是否超键,可计算?+ 并检查?+ 是否包含 R的所有属性
测试函数依赖
– 为检测函数依赖 是否成立 (即属于 F+),只需检查是否+,
– 即,可计算?+,并检查它是否包含?,
– 这个检查简单而高效,非常有用
计算 F的闭包
– 对每个 R,计算?+,再对每个 S+,输出函数依赖
S.
两个函数依赖集等价:
函数依赖集 F,G,若 F+= G+,则称 F与 G等价 。
F+ = G+?F?G+,G?F+
正则覆盖 /最小覆盖:
函数依赖集合可能有冗余依赖 (即他们能从其他函数依赖推出 )
– 例如,A? C 在 {A? B,B? C,A? C} 中是冗余的
– 函数依赖的某部分可能是冗余的
依赖右部,{A? B,B? C,A? CD} 可化简为
{A? B,B? C,A? D}
依赖左部,{A? B,B? C,AC? D} 可化简为
{A? B,B? C,A? D}
直观地说,F的正则覆盖是指与 F等价的,极小的,函数依赖集合,没有冗余依赖,依赖也没有冗余部分无关属性
考虑函数依赖集合 F及其中的函数依赖.
– 如果 A并且 F 逻辑蕴含 (F – {})? {(? – A)},
则称 属性 A 在?中是无关的,
Attribute A is extraneous in? if A
and F logically implies (F – {})? {(? – A)}.
IF F and (F – {})? {(? – A)} are equivalent,
F? ((F – {})? {(? – A)} ) +,
(? – A), (? – A),
(F – {})? {(? – A)}?F+
(? – A)F+
(? – A)+ (F) contains?
–如果 A并且 (F – {})? {(? – A)} 逻辑蕴含 F,则称属性 A 在?中是无关的,
Attribute A is extraneous in? if A
and the set of functional dependencies
(F – {})? {(?– A)} logically implies F.
IF F and (F – {})? {(?– A)} are equivalent,
(F – {})? {(?– A)}?F+
, (? – A),(?– A)
F? ((F – {})? {(?– A)} ) +,
A? ((F – {})? {(?– A)} ) +
+ ((F – {})? {(?– A)} ) contains A
例如,给定 F = {A? C,AB? C }
–B 在 AB? C 中是无关紧要的,因为 A? C 逻辑蕴含
AB? C.
例如,给定 F = {A? C,AB? CD}
–C 在 AB? CD 中是无关紧要的,因为即使删除 C 也能推出 A? C
检测属性是否无关
考虑函数依赖集合 F以及其中的函数依赖.
– 为检测属性 A在?中是否无关紧要
1,计算在 F 下的 (?– {A})+
2,检查 (?– {A})+是否包含?; 如果是,则 A 是无关紧要的
–为检测属性 A 在?中是否无关紧要
1.计算在 F’= (F – {})? {(?– A)} 下的?+
2.检查?+ 是否包含 A; 如果是,则 A 是无关紧要的正则覆盖
函数依赖集合 F 的一个正则覆盖是满足下列条件的函数依赖集合 Fc
– F 逻辑蕴含 Fc 中的所有函数依赖
– Fc 逻辑蕴含 F 中的所有函数依赖
– Fc 中的函数依赖不含无关紧要的属性
– Fc 中的函数依赖的左部都是唯一的
计算 F 的正则覆盖,
repeat
对 F 中的依赖利用合并规则
11 和?11 替换成?11?2
找出含有无关紧要属性的函数依赖 (在? 或?中 )
如果找到无关紧要的属性,从中删去
until F 不再变化
–注,删除某些无关紧要的属性之后,可能导致合并规则可应用,所以必须重新应用
R = (A,B,C)
F = {A? BC
B? C
A? B
AB? C}
合并 A? BC 及 A? B 成 A? BC
集合变成 {A? BC,B? C,AB? C}
A 在 AB? C 中是无关紧要的,因为 B? C 逻辑蕴含 AB? C.
集合变成 {A? BC,B? C}
C 在 A? BC 中是无关紧要的,因为 A? BC 可由 A? B 和 B? C逻辑推出,
正则覆盖是,
A? B
B? C
例:
First Normal Form 1NF
Second Normal Form 2NF
Third Normal Form 3NF
Boyce-Codd Normal Form BCNF
基于数据依赖和函数依赖,
关系范式:
第一范式
如果域中元素被认为是不可分的,则域称为是原子的
– 非原子域的例子,
名字集合,复合属性
象 CS101之类的标识号可以分成若干部分
如果关系模式 R的所有属性的域都是原子的,则 R称为属于第一范式
非原子值存储复杂并易导致数据冗余
– E.g,每个客户的账户集合,以及每个账户的拥有者集合
– 我们假定所有关系都属于第一范式
2NF,若 R是 1NF,且每个非键属性完全依赖于候选键,则称
R为 2NF(消除非键属性对候选键的部分依赖)。
例:关系模式 S(SNO,SN,SD,DEAN,CNO,G)
( SNO,CNO) 为候选键,SNO?SN,SNO?SD,存在部分依赖,
非 2NF,则会有以下问题:
插入异常:如果学生没有选课,关于他的个人信息及所在系的信息就无法插入 。
删除异常:如果删除学生的选课信息,则有关他的个人信息及所在的信息也随之删除了 。
更新异常:如果学生转系,若他选修了 k门课,则需要修改 k次 。
数据冗余:如果一个学生选修了 k门课,则有关他的所在系的信息重复 。
可将 S分解为,SC(SNO,CNO,G)
S_SD(SNO,SN,SD,DEAN)
第三范式
关系模式 R 属于第三范式 (3NF) 当且仅当对所有 F+中依赖,

下列条件中至少一个成立,
– 是平凡的 (i.e.,)
–?是 R 的超键
–? –? 中的每个属性 A 包含在 R 的某个候选键中,
(注,各属性可能包含在不同候选键中 )
或:
非主属性既不部分依赖也不传递依赖于 R的候选键,则称 R是第三范式。
S_SD(SNO,SN,SD,DEAN),
SNO?SD,SD?DEAN
STUDENT(SNO,SN,SD)
DEPT(SD,DEAN)
Boyce-Codd 范式:
是平凡的 (i.e.,)
是 R的超键具有函数依赖集合 F 的关系模式 R 属于 BCNF当且仅当对 F+
中所有函数依赖,下列两条件至少一个成立,
或:
如果关系模式 R是 1NF,且每个属性都不部分依赖于候选键也不传递依赖于候选键,那么称 R是 BC范式 。
SPC(SNO,PNO,CNO),
PNO?CNO
( SNO,CNO)?PNO
每位老师只教授一门课;某学生选定一门课,就对应一位老师
( SNO,PNO)?CNO
( SNO,CNO)?PNO,( SNO,PNO),( SNO,CNO) 为候选键 。 所有属性为键属性,为第三范式,但非 BCNF
SP( SNO,PNO),PC( PNO,CNO)
因 3NF的冗余引起的问题
– R = (J,K,L)
F = {JK? L,L? K}
J
j1
j2
j3
null
L
l1
l1
l1
l2
K
k1
k1
k1
k2
属于 3NF但不属于 BCNF的模式有下面的问题
信息重复 (e.g.,联系 l1,k1)
需要使用空值 (e.g.,表示联系 l2,k2,这里没有对应的 J 值 ).
多属性依赖集候选关键字求法:
输入:关系模式 R及其函数依赖集 F
输出,R的所有候选关键字。
方法:
( 1)将 R的所有属性分为四类:
L类:仅出现在 F的函数依赖左部的属性;
R类:仅出现在 F的函数依赖右部的属性;
N类:在 F的函数依赖左右两边均未出现的属性;
LR类:在 F的函数依赖左右两边均出现的属性;
并令 X代表 L,N类,Y代表 LR类;
( 2)求 X+,若包含了 R的所有属性,则 X即为 R的唯一候选关键字,转( 5),否则转( 3);
( 3)在 Y中取一属性 A,求( XA)+,若它包含了 R的所有属性,则转( 4),否则,调换一属性反复进行这一过程,直到试完所有 Y中的属性;
( 4)如果已找出所有的候选关键字,则转( 5),否则在 Y中依此取两个、三个,…,求他们的属性闭包,直到其闭包包含 R的所有的属性。
( 5)停止,输出结果。
( 1) R( U,F),U={A,B,C,D},F ={B?D,AB
C};
Candidate Key {A,B},1NF.
( 2) R( U,F),U={A,B,C,D,E},F ={AB?CE,
E?AB,C?D};
Candidate Key {A,B} and {E},2NF.
( 3) R( U,F),U={A,B,C,D},F ={B?D,D?B,
AB?C};
Candidate Key {A,B} and {A,D},3NF.
( 4) R( U,F),U={A,B,C},F ={A?B,B?A,A
C};
Candidate Key {A}和 {B},BCNF.
( 5) R( U,F),U={A,B,C},F ={A?B,B?A,C
A};
Candidate Key {C},2NF.
( 6) R( U,F),U={A,B,C,D},F ={A?C,D?B};
Candidate Key {A,D},1NF.
1NF
2NF
3NF
BCNF
消除非键属性对键的部分函数依赖消除非键属性对键的传递函数依赖消除键属性对键的部分和传递函数依赖消除决定因素非键的非平凡函数依赖分解:
确定一个关系是否属于,好的” 形式,
如果关系 R 不属于“好的”形式,则将它分解为关系集合 {R1,R2,...,Rn} 使得:
原模式 (R) 的所有属性都必须出现在分解结果
(R1,R2,..,Rn)中,
R = R1? R2,.,? Rn
关系模式 R<U,F>的一个分解:
ρ={ R1<U1,F1>,R2<U2,F2>,…,Rn<Un,Fn>}
U=U1∪ U2∪ … ∪ Un,且不存在 Ui? Uj,Fi 为 F在 Ui
上的投影
函数依赖集合 {X→ Y | X→ Y? F+∧ XY?Ui} 的一个 覆盖
Fi 叫作 F 在属性 Ui 上的投影无损连接分解:
将关系模式 R分解为 {R1,R2,...,Rn},对模式 R上的所有可能关系 r,
有:
r =?R1 (r)?R2 (r),.,?Rn (r)
有损连接的分解导致信息的丢失:
将 student(sno,dept,head)分解为
R1=(sno),R2=(dept),R3=(head)
R1=(sno,dept),R2=(sno,head)
R 分解成 R1 和 R2 是无损连接的,当且仅当下列依赖中的至少一个属于 F+:
R1? R2? R1
R1? R2? R2
例,R = (A,B,C)
F = {A? B,B? C)
R1 = (A,B),R2 = (B,C)
Lossless-join decomposition:
R1? R2 = {B} and B? BC
函数依赖保持若 F+ = (?Fi)+,则称 R< U,F >的分解?=
{R1<U1,F1>,…,Rn<Un,Fn>}保持函数依赖。
例,R = (A,B,C),F = {A? B,B? C)
R1 = (A,B),R2 = (B,C)
函数依赖保持
R1 = (A,B),R2 = (A,C)
非 函数依赖保持检查依赖保持
为检查依赖 在 R 到 R1,R2,…,Rn的分解中是否得到保持,可进行下面的简单测试 (设已计算了 F下的属性闭包 )
– result =?
while (result 有变化 ) do
for each 分解后的 Ri
t = (result? Ri)+? Ri
result = result? t
– 若 result 包含?中的所有属性,则 得到保持,
需要对 F中所有依赖进行依赖保持的测试
此算法需要多项式时间,而计算 F+ 和 (F1? F2? …? Fn)+
需要指数时间
3NF 分解算法令 Fc 是 F 的正则覆盖 ;
i,= 0;
for each Fc中的函数依赖 do
if 没有模式 Rj (1? j? i) 包含
then begin
i,= i + 1;
Ri,=
end
if 没有模式 Rj (1? j? i) 包含 R 的候选键
then begin
i,= i + 1;
Ri,= R 的任意候选键 ;
end
return (R1,R2,...,Ri)
上述算法确保,
每个关系模式 Ri 属于 3NF
分解是保持依赖的和无损连接的
U={SNO,SD,MN,CNO,G}
F={SNO?SD,SNO?MN,SD?MN,(SNO,CNO)?G}
⒈ G={SNO?SD,SD?MN,(SNO,CNO)?G}

U1 = {SNO,SD},F1={SNO?SD}
U2 = {SD,MN},F2={SD?MN}
U3 = {SNO,CNO,G},F3 = {(SNO,CNO)?G}
BCNF 分解算法
result,= {R};
done,= false;
compute F+;
while (not done) do
if (result 中存在模式 Ri 不属于 BCNF)
then begin
令 是 Ri 上的一个非平凡函数依赖使得 Ri 不属于 F+,且 =?;
result,= (result – Ri)? (Ri –?)? (?,? );
end
else done,= true;
注意,每个 Ri 都属于 BCNF,且分解是无损连接的,
U={SNO,SD,MN,CNO,G}
F={SNO?SD,SNO?MN,SD?MN,(SNO,CNO)?G}
( 1) U1={SNO,SD},F1={SNO?SD}
U2={SNO,MN,CNO,G},F2={SNO?MN,
(SNO,CNO)?G}
( 2) U1 = {SNO,SD},F1={SNO?SD}
U2 = {SNO,MN},F2={SNO?MN}
U3 = {SNO,CNO,G},F3 = {(SNO,CNO)?G}
2,6 对象-关系数据库
是将关系数据库和面向对象的数据库相结合的产品
特点
– 运行用户扩充基本数据类型
– 能够在 SQL中支持复杂对象
– 能够支持子类对超类的各种特性的继承
– 能够提供功能强大的通用规则系统
实现方法
– 从头开始对象-关系 DBMS
– 在现有的关系型 DBMS基础上进行扩展
– 将现有的关系型 DBMS与其他厂商的对象-关系型
DBMS连接在一起,使现有的关系型 DBMS直接而迅速具有对象-关系型 DBMS的特征
– 将现有的面向对象型 DBMS与其他厂商的对象-关系型 DBMS连接在一起,使现有的面向对象型的 DBMS直接而迅速地具有对象-关系型 DBMS的特征
– 扩充现有的面向对象型 DBMS,使之成为对象-关系型 DBMS
对象关系数据库系统除具有原来关系数据库的各种特点外,
还应该提供以下特点:
1)扩充数据类型:允许用户根据自己应用需求定义自己的数据类型、函数和操作符。例如可以定义数组、向量、矩阵、集合以及这些数据类型上的操作。
2)支持复杂对象:复杂对象指由多种基本类型或用户定义类型构成的对象。
3)支持继承:支持子类、超类的概念,支持属性数据和函数及过程的继承。
4)提供通用的规则系统:规则在 DBMS及其应用中十分重要,传统 DBMS中的触发器可以看作规则的一种形式。
对象关系数据模型
通过引入面向对象及处理复杂数据类型的构造来扩展关系数据模型,
允许元组属性具有复杂类型,包括非原子值 (如嵌套关系 ).
保持关系基础,尤其是对数据的描述性存取,同时扩展建模能力,
与现有关系语言向上兼容,
嵌套关系
动机,
– 允许非原子域 (原子? 不可分割 )
– 非原子域例,整数集合,元组集合
– 对含有复杂数据的应用可以更直观地建模
直观定义,
– 在原先只允许出现原子值 (标量 )的地方可以出现关系 — 关系内的关系
– 保持关系模型的数学基础
– 违反第一范式嵌套关系例
例如,图书馆信息系统
每本书具有
– 书名,
– 作者集合,
– 出版商,
– 关键词集合
非 1NF 关系 books
对 SQL的扩展支持复杂类型,包括,
– 集合与大对象类型
嵌套关系是集合类型的例子
– 结构类型
类似符合属性的嵌套记录结构
– 继承
– 面向对象
包含对象标识符和引用
SQL:1999标准
– 当前的数据库系统并没有完全实现
– 但某些特性在主要商业数据库系统中都存在
请参阅具体数据库系统的手册看看支持什么对象关系数据定义
对象关系模型中,属性可以是复合类型,
1)结构类型,不同类型元素的有序集合 ;
2)数组类型,同类元素的有序集合 ;
3)集合类型,同类元素的无序集合,
数据类型可以嵌套,
继承性表现在类型的继承和表的继承,子类型继承超类型的特征 ;子表继承父表的特征,
数据类型可以嵌套定义,但要实现递归,就要采用 ‘ 引用 ’
类型,
ORACLE对象 _关系支持:
对象类型;
集合类型:支持变长数组和嵌套表;
对象表:用于在提供对象属性的关系视图时存储对象;
表函数:生成行集合作为输出;
对象视图:可以将存储在常规关系表中的数据提供虚拟对象表视图;
方法:PL/SQL等来编写;
用户定义聚集函数;
XML数据类型。
创建对象的一般格式,
CREATE TYPE typename AS OBJECT
(属性名 类型 [,属性名 类型 …]);
若 ORACLE中表的行包含对象类型,则称为对象表 (informix中称类型表 ).采用如下形式定义表,
CREATE TABLE tablename OF typename
[([属性名 NOT NULL][,属性名 NOT NULL…]
[,PRIMARY KEY (属性名 [,属性名 …])])];
DROP TYPE typename [FORCE];
DROP TABLE tablename;
create type namesex_t as object
(name char(8),sex char(2));/
create table employees (eid char(4),
ename namesex_t,job char(6));
insert into employees value(‘0001’,
namesex_t(‘赵一 ’,’男 ’ ),’经理 ’ );
select e.eid,e.ename.name,e.ename.sex
from employees e
where e.job=‘经理 ’ ;
create type employee_t as object
(eid char(4),ename namesex_t,job char(6));
create table employees of employee_t
(primary key(eid));
对象表 (包含行对象 employee_t和列对象 ename)
顶层属性 eid ename Job
ename属性 ename,name ename.sex
行 1 0001 赵一 男 经理行 2 0002 王二 男 职员行 3 0003 李三 女 职员
对象构造器
每个对象类型有一个使新对象遵循对象类型声明的系统定义的构造器。构造器方法与对象类型具有相同的名称,其参数与对象类型属性的名称和类型相同。
insert into employees
values(‘0004’,namesex_t(‘胡四 ’,’男 ’ ),null);
update employees e set e=employee_t(‘0004’,
namesex_t(‘胡四 ’,’男 ’ ),‘职员 ’ ) where eid=‘0004’;
对象标识符(OID)
ORACLE为每个行对象提供的一个唯一标识,唯一标识对象表中的每一行对象。
隐式创建和维护对象表中的 OID列的一个索引。
REF数据类型用于封装对某一具体对象类型的行对象的引用。
一个表的列可被定义为 REF(引用 )的内部数据类型,允许它 ‘ 指向 ’ 一个对象表的行对象,
集类型:
数组类型和表类型
表类型和嵌套表创建,
CREATE TYPE typename AS TABLE OF obj_name;
CREATE TABLE tablename
( 属性名 类型 [NOT NULL][…] )
[NESTED TABLE 属性名 STORE AS tablename]
[,NESTED TABLE 属性名 STORE AS tablename…];
例,
create type depe_t as table of namesex_t;
create table employees
(eid char(4),ename namesex_t,dependents depe_t,
primary key (eid) )
nested table dependents store as d_tab;
嵌套表的行被存储在不能被用户直接访问的单独的存储表中。子表的数据通过父表来访问。
存储表中有一个隐藏的列,Nested_Table_Id,按照它们相应的父行来匹配每一行。给定的某行的嵌套表所有的元素具有同一个 Nested_Table_Id值,不同行的嵌套表具有不同的值。
数组类型:
数组是一个数据元素的有序集合,其数据元素的类型都相同。
数组的长度可以是固定的或变化的。
数组类型的创建并不分配空间,而是定义一个可以使用的数据类型。
create type phone_t as varray(4) of int;
create table employees
(eid char(4),ename namesex_t,
telephone phone_t,primary key (eid)) ;
对象方法对象关系数据库中利用对象类型定义表、利用用户定义函数定义对象的方法。
定义包含方法的对象
CREATE TYPE typename AS OBJECT
(attrname datatype [,attrname datatype…]
MEMBER FUNCTION methodname
[(param type [,param type…])]
RETURN datatype,
[,MEMBER FUNCTION methodname
[(param type [,param type…])]
RETURN datatype,…]);
在前面对象类型的基础上增加了 MEMBER FUNCTION语句,定义对象的成员函数,即方法。
对象成员函数的定义
CREATE [OR REPLACE] TYPE BODY typename
[AS|IS]
MEMBER FUNCTION methodname [(param type…)]
RETURN type IS
BEGIN --PL/SQL语句开始
statements
END; --PL/SQL语句结束
[MEMBER FUNCTION methodname [(param type…)]
RETURN type is
BEGIN……]
END;
思考题:
1已知关系模式 R<U,F>,U={A,B,C,D },F={ A?C,
C?A,B?AC,D?AC },完成以下各小题要求:
(1) 给出 R 的所有候选键;
(2) 判断 R 最高属于第几范式,为什么?
(3)求 F的最小函数依赖集 。
(4)将 R分解成满足要求 3NF 并具有无损联结性和函数依赖保持性。
(5)将 R分解成满足要求 BCNF 并具有无损联结性,
2 对象关系数据库特点?