$ f* @8 f; f, x& `( P 看起来这应该是显而易见的事,但需求就是来自客户(这里要从* |% \3 e/ s, F( g/ N
内部和外部客户的角度考虑)。不要依赖用户写下来的需求,真正的. J. Y9 ~3 D; E2 m S2 ~
需求在客户的脑袋里。你要让客户解释其需求,而且随着开发的继续, & T" Q( ^. G( _, y% Y- x7 Y 还要经常询问客户保证其需求仍然在开发的目的之中。一个不变的真( V5 E( y% |9 ^
理是:“只有我看见了我才知道我想要的是什么”必然会导致大量的 f) X. @, y- ]/ y 返工,因为数据库没有达到客户从来没有写下来的需求标准。而更糟 1 ^/ T7 j6 |. G: t2 n 的是你对他们需求的解释只属于你自己,而且可能是完全错误的。) j& b$ D4 P# a! d+ H
; c" A; E8 b! y9 T' C 0 i* y) y4 A% T8 s- ]5 G$ b § 第 2 部分 - 设计表和字段- n8 p7 R+ }; W: O. c. X
────────────── " B/ s; v+ E, M. Y0 B4 K; E: A- j9 ]
■ 检查各种变化 ( e, x0 c$ m1 a" }) @+ a2 I" l7 g
我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变 + j/ C4 m# N3 M! ]3 x# V7 ^! z K 更。比方说,姓氏就是如此(注意是西方人的姓氏,比如女性结婚后" {3 N/ m5 \2 c% i) a
从夫姓等)。所以,在建立系统存储客户信息时,我倾向于在单独的 : K! D/ E, N, B- ?6 d 一个数据表里存储姓氏字段,而且还附加起始日和终止日等字段,这 _, V) `2 b& t. a- K
样就可以跟踪这一数据条目的变化。2 O5 [! T) F4 e$ u: E% ?
8 N. d! z* K0 F3 o! @
■ 采用有意义的字段名3 ~7 p! \( {2 z4 a! k7 B
3 ]3 K0 X4 x1 F( z# \0 k- Z0 I 有一回我参加开发过一个项目,其中有从其他程序员那里继承的( i1 L8 W! |; M6 @) R# b
程序,那个程序员喜欢用屏幕上显示数据指示用语命名字段,这也不7 O9 I Q& [+ m# F! }1 n
赖,但不幸的是,她还喜欢用一些奇怪的命名法,其命名采用了匈牙$ @4 ?& A* [4 W- C
利命名和控制序号的组合形式,比如 cbo1、txt2、txt2_b 等等。 , [* }: Z2 w6 ]( G ' }, L7 z. F. I( X1 Z5 G' v7 L9 N 除非你在使用只面向你的缩写字段名的系统,否则请尽可能地把 : l8 f+ {) ^6 N 字段描述的清楚些。当然,也别做过头了,比如 . O7 ^ s2 P- P" K7 v Customer_Shipping_Address_Street_Line_1,虽然很富有说明性, ' q: x' c7 ~% v6 K3 t& g! f0 P 但没人愿意键入这么长的名字,具体尺度就在你的把握中。 / A$ [* b" S9 R! ^3 s" T$ y9 ?; ~& d9 V2 x% U" i- R/ {* L
■ 采用前缀命名- i7 B U E& c/ a2 `
. G/ j% {8 L# K4 G0 R 如果多个表里有好多同一类型的字段(比如 FirstName),你不 / b5 Q; P6 G/ Y" v) a9 ? 妨用特定表的前缀(比如 CusLastName)来帮助你标识字段。 ! z! r+ h$ J2 u/ q, G - _0 E3 i. k& M# @2 c) d 时效性数据应包括“最近更新日期/时间”字段。时间标记对查 ! P) c( I" x- _9 V. S8 E; Y 找数据问题的原因、按日期重新处理/重载数据和清除旧数据特别有7 X3 T% b1 C* h/ f1 Z) P) i
用。, M$ P, i5 B1 y7 S! |4 R
7 E* {. f. o6 j$ { ■ 标准化和数据驱动 - t9 A) L/ B8 S( B 5 h" G! b$ }1 e2 ~( B 数据的标准化不仅方便了自己而且也方便了其他人。比方说,假 # C3 G2 P) X: Q6 D 如你的用户界面要访问外部数据源(文件、XML 文档、其他数据库等), 4 Z6 J8 v9 x9 C# v* @* r 你不妨把相应的连接和路径信息存储在用户界面支持表里。还有,如2 e% s7 |; R# R9 ~8 D D N
果用户界面执行工作流之类的任务(发送邮件、打印信笺、修改记录( i' |1 K# E/ ^7 {6 ?1 T
状态等),那么产生工作流的数据也可以存放在数据库里。预先安排- D0 d: |" Z% b& \# a+ U
总需要付出努力,但如果这些过程采用数据驱动而非硬编码的方式,/ z2 K$ C" g1 _" l4 K
那么策略变更和维护都会方便得多。事实上,如果过程是数据驱动的, ( v2 u ]) n$ M: { 你就可以把相当大的责任推给用户,由用户来维护自己的工作流过程。3 a* d* |0 h: C) j
! a) }( ^: T2 r+ W ■ 标准化不能过头 * r) ~% i5 O% T1 X, @9 P: Q# W 5 }# H1 Y% t! e$ O+ g; M7 V0 L 对那些不熟悉标准化一词(normalization)的人而言,标准化; ^6 t7 y1 ]: C# |* H/ p, Q* D' ^
可以保证表内的字段都是最基础的要素,而这一措施有助于消除数据3 A. a$ z/ D0 g7 h' z
库中的数据冗余。标准化有好几种形式,但 Thi rd Normal Form/ E P: j2 ^7 F- \( b* X$ a' Z
(3NF)通常被认为在性能、扩展性和数据完整性方面达到了最好平 " i$ T% y' L& ?1 @" M @7 E 衡。简单来说,3NF 规定: # p2 X+ ?% w% L9 \4 M9 D3 q' J0 Y4 D4 T: ~
· 表内的每一个值都只能被表达一次。 8 g5 a% Y7 @0 x+ ?5 `' ` · 表内的每一行都应该被唯一的标识(有唯一键)。! H* v% a8 c0 T# g6 y
· 表内不应该存储依赖于其他键的非键信息。 0 O6 E; R1 Y( ?/ b% W3 E+ h, {. P , x2 j0 X4 f! M& C) ^
遵守 3NF 标准的数据库具有以下特点:有一组表专门存放通过 2 n$ L; k8 [+ l% @( } 键连接起来的关联数据。比方说,某个存放客户及其有关定单的 / k% P Y3 j" Q 3NF 数据库就可能有两个表:Customer 和 Order。 0 E c; E1 h4 O5 T; Q0 `0 ?3 y! Y6 G2 W- S4 E$ E# {
Order 表不包含定单关联客户的任何信息,但表内会存放一个键9 @2 g m$ t2 N# ~/ ]$ S% ]
值,该键指向 Customer 表里包含该客户信息的那一行。 " b' v0 k6 G- f5 {+ D* X# o- [6 k2 U* s% b: K2 U# n& X
更高层次的标准化也有,但更标准是否就一定更好呢?答案是不 ~( B1 R! p3 w8 m' r 一定。事实上,对某些项目来说,甚至就连 3NF 都可能给数据库引& z6 `: D) o; Z2 X
入太高的复杂性。' X% ~* f" Z0 O0 Z
" w3 W2 f _$ T7 W 为了效率的缘故,对表不进行标准化有时也是必要的,这样的例 1 K1 z# j. V# ]6 B 子很多。曾经有个开发餐饮分析软件的活就是用非标准化表把查询时* P8 S/ Q$ f |% P/ V
间从平均 40 秒降低到了两秒左右。虽然我不得不这么做,但我绝不8 q4 @3 ?8 D: O" r% I/ a1 \6 H( N' }
把数据表的非标准化当作当然的设计理念。而具体的操作不过是一种 / l& n- z0 ^: k5 K b t7 t 派生。所以如果表出了问题重新产生非标准化的表是完全可能的。 1 \- W% ~# z. C! \ - A ^% v& M5 z Microsoft Visual FoxPro 报表技巧如果你正在使用 3 K6 z1 s# N N$ V' h" Z
Microsoft Visual FoxPro,你可以用对用户友好的字段名来代替编, t+ `; T* N h* T5 v3 D
号的名称:比如用 Customer Name 代替 txtCNaM。这样,当你用向: d/ \: b! P9 _3 s# e8 Y
导程序[Wizards,台湾人称为‘精灵’]创建表单和报表时,其名字 , W1 r! C: f; `8 {$ U 会让那些不是程序员的人更容易阅读。) g" s9 l. z# p% a8 ?1 w! T) K6 O
! ]1 p; v% ~3 x8 M9 ^) _4 u, u
■ 不活跃或者不采用的指示符6 U2 G O0 @: z* P# K
+ k: ^6 Z0 \% U3 h 增加一个字段表示所在记录是否在业务中不再活跃挺有用的。不 6 h ~0 Z* r2 w* G 管是客户、员工还是其他什么人,这样做都能有助于再运行查询的时 , Q6 j/ m7 W5 b7 o, I! {- B2 D 候过滤活跃或者不活跃状态。同时还消除了新用户在采用数据时所面! T. O& O8 a& l- h P
临的一些问题,比如,某些记录可能不再为他们所用,再删除的时候! q0 D m! Q! d3 [
可以起到一定的防范作用。& W" Y- O1 C6 C- B9 ?
9 y: q+ \% P4 X) i7 E 使用角色实体定义属于某类别的列[字段]在需要对属于特定类别$ ~2 x1 C0 c$ r6 {% k) K
或者具有特定角色的事物做定义时,可以用角色实体来创建特定的时* X, s7 t$ r( `; J" [! u* l. E
间关联关系,从而可以实现自我文档化。 " ]3 r. H' h) F% E ' }$ ~6 _5 n6 R0 W8 m2 o& ] 这里的含义不是让 PERSON 实体带有 Title 字段,而是说,为 % A5 b% i. D& I- g2 H$ z! A( O. v9 } 什么不用 PERSON 实体和 PERSON_TYPE 实体来描述人员呢?比方说,) f" ~/ G) S* L5 f' L* s
当 John Smith, Engineer 提升为 John Smit h, Director 乃至最 " Y( X4 f2 f6 F' S 后爬到 John Smith, CIO 的高位,而所有你要做的不过是改变两个 9 B! n c- W9 b) Z, B( t$ v; R% [ 表 PERSON 和 PERSON_TYPE 之间关系的键值,同时增加一个日期/时6 R5 q2 q8 e. i% r8 r" o4 I: `
间字段来知道变化是何时发生的。这样,你的 PERSON_TYPE 表就包 L+ I% m9 G* @( X 含了所有 PERSON 的可能类型,比如 Associ ate、Engineer、 6 \8 ^7 Y, W5 T0 g/ H2 q& r" m Director、CIO 或者 CEO 等。- o6 Y( s$ H0 d& J" r: |
5 J0 v7 K4 A5 E6 q$ o: c 还有个替代办法就是改变 PERSON 记录来反映新头衔的变化,不 / W* y7 |6 J- f1 h4 y" V; m6 ?8 N 过这样一来在时间上无法跟踪个人所处位置的具体时间。 - Y3 U5 [/ ?7 I8 }3 m3 [' L( ~7 a* x & R1 Q2 w) E/ Y f ■ 采用常用实体命名机构数据, ?$ J+ H% W% F+ x! d
" y9 h# j; X4 d 组织数据的最简单办法就是采用常用名字,比如:PERSON、 + h" E# Y" \9 e/ M ORGANIZATION、ADDRESS 和 P HONE 等等。当你把这些常用的一般名1 t I+ ]6 h. M7 b. v8 B/ l
字组合起来或者创建特定的相应副实体时,你就得到了自己用的特殊+ H6 ?8 a7 n: Y; U% \0 ]
版本。开始的时候采用一般术语的主要原因在于所有的具体用户都能! r' ~4 H' ?5 Y* B
对抽象事物具体化。 - |+ A1 d6 y) ~1 U Y+ c5 w 1 B" O1 \; P3 e 有了这些抽象表示,你就可以在第 2 级标识中采用自己的特殊* n9 X s/ Y7 z: P
名称,比如,PERSON 可能是 Employee、Spouse、Patient、 - m) H8 R2 p; N+ | Client、Customer、Vendor 或者 Teacher 等。同样的,/ \ ~# q w. @& z. ?% {8 A5 U
ORGANIZATION 也可能是 MyCompany、MyDepartment、Competitor、 & g8 o2 w; K+ n: z Hospital、Warehouse、Government 等。最后 ADDRESS 可以具体为 6 i, H: C# s, y
Site、Location、Home、Work、Client、 Vendor、Corporate 和 3 C/ \# [$ S; n1 S6 }
FieldOffice 等。 9 }: ^1 P, `5 T$ F8 b) q* ] ' I/ V3 [* [4 W2 N( k2 S1 S w 采用一般抽象术语来标识“事物”的类别可以让你在关联数据以 . K) C' ^& U7 M" R* \ 满足业务要求方面获得巨大的灵活性,同时这样做还可以显著降低数: E) G6 l' X! \1 h4 I
据存储所需的冗余量。+ T* S& E. ?5 B: t
# E9 q! X% m% q/ M/ ^# k8 L & ^+ w4 b- \) s2 X; y § 第 3 部分 - 选择键和索引 + s5 X Y; h8 I8 c' o+ h6 e ──────────────+ s) ]' a- } ~; T: X, j
3 |4 k2 O: H5 V! t) F$ z
■ 数据采掘要预先计划 6 N0 A% f0 A' j3 s" b# U1 N " O. b U4 h' d 我所在的某一客户部门一度要处理 8 万多份联系方式,同时填 1 [; [6 G8 \, E/ e, J 写每个客户的必要数据(这绝对不是小活)。我从中还要确定出一组 , [( d8 j; K) f5 D$ K. U* F& l 客户作为市场目标。当我从最开始设计表和字段的时候,我试图不在' D4 [* q! g( `) m
主索引里增加太多的字段以便加快数据库的运行速度。然后我意识到* A. p; M; e$ w5 l, `" W3 P l
特定的组查询和信息采掘既不准确速度也不快。结果只好在主索引中7 i8 S/ k0 ^4 {) }7 O/ [
重建而且合并了数据字段。我发现有一个指示计划相当关键——当我, v, A* u- d( Q; f2 ?; K0 I; n
想创建系统类型查找时为什么要采用号码作为主索引字段呢?我可以 3 h3 o- v$ q6 b. E' @4 o6 v5 A1 A4 v 用传真号码进行检索,但是它几乎就象系统类型一样对我来说并不重 9 `: ~+ v% |: I# w* g9 H5 j 要。采用后者作为主字段,数据库更新后重新索引和检索就快多了。 ) c' }3 i( \- G# x% b; T$ T9 E; |- K 2 O3 i3 s6 L- g+ o; ~* z' ] 可操作数据仓库(ODS)和数据仓库(DW)这两种环境下的数据7 I( X f- U) l6 Y, N% }% Q) Z1 ?
索引是有差别的。在 DW 环境下,你要考虑销售部门是如何组织销售! u+ f2 Z9 w5 a* O3 o# x
活动的。他们并不是数据库管理员,但是他们确定表内的键信息。这/ o& v- V3 J* g, o9 a- ?
里设计人员或者数据库工作人员应该分析数据库结构从而确定出性能 # H1 d J9 m O 和正确输出之间的最佳条件。 4 K3 L; f; F; ? ( _. T+ d2 t! L* X ■ 使用系统生成的主键 3 ~9 m, r! [1 s k! f# ?9 C. E# k$ J4 c. B; o: Z# t
这类同技巧 1,但我觉得有必要在这里重复提醒大家。假如你总0 |# @# v" w/ W% I: Z9 Y; ^
是在设计数据库的时候采用系统生成的键作为主键,那么你实际控制 # g4 Q7 v2 ~( m/ A( k1 X8 W! I 了数据库的索引完整性。这样,数据库和非人工机制就有效地控制了9 [# k8 `3 V. |5 n
对存储数据中每一行的访问。 + U f6 U0 |. S8 @5 v0 f0 s. @! O- S. n ~
采用系统生成键作为主键还有一个优点:当你拥有一致的键结构5 n D7 E0 m' o' R- ~2 v$ Y
时,找到逻辑缺陷很容易。! r; J5 ~8 e1 x" t& }% D6 B9 j
. O9 ~2 _8 n: o6 ?+ S
■ 分解字段用于索引 # L( Q+ j# x( w m! J+ V' o/ @6 q' D) L* t
为了分离命名字段和包含字段以支持用户定义的报表,请考虑分 : E" s$ l2 k" h( U+ S0 n+ f3 B 解其他字段(甚至主键)( W- l0 o, B4 t! E: Z: E W
3 G; s( h1 b5 v& G
为其组成要素以便用户可以对其进行索引。索引将加快 SQL 和; }9 m# g( ?! o- Y6 r" c1 H2 K/ o
报表生成器脚本的执行速度。比方说,我通常在必须使用 SQL - e/ f2 D+ u4 u' K: G! W LIKE 表达式的情况下创建报表,因为 case number 字段无法分解为 2 J7 K# f3 z. C* m) i
year、serial number、case type 和 defendant code 等要素。性$ B& U1 y+ Z" D5 D
能也会变坏。假如年度和类型字段可以分解为索引字段那么这些报表8 H& Z, h# n' H' I+ j
运行起来就会快多了。3 I* z) n8 t$ X9 [' Y- {. }0 C+ U
0 Z8 i0 E. }/ \8 r ■ 键设计 4 原则' B2 j" p$ }* h5 g7 s) O
: }8 o9 ]# R( e7 q# V4 t% B
1. 为关联字段创建外键。 0 F5 I4 B/ w, P 2. 所有的键都必须唯一。 0 N. v G1 @- h5 w 3. 避免使用复合键。 , t R+ {$ j: f 4. 外键总是关联唯一的键字段。, M' }4 ~1 P1 m
" K# t* [+ q3 ~# ?# ]
■ 别忘了索引 - k9 x- g. u0 f2 u. e 1 d. a3 p9 I* c* e 索引是从数据库中获取数据的最高效方式之一。95%的数据库性 }. W# C- Z0 `( f- `# i0 e+ O
能问题都可以采用索引技术得到解决。作为一条规则,我通常对逻辑! P* y6 q! T7 H2 e# `6 ?4 M( O
主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非 & M8 o7 m' }6 P/ ~4 ~3 ?2 Z 成组索引,对任何外键列[字段]采用非成组索引。不过,索引就象是 ; r% Z2 R+ o+ t" S% i" ? 盐,太多了菜就咸了。你得考虑数据库的空间有多大,表如何进行访 9 C8 x% F0 k, X% i c 问,还有这些访问是否主要用作读写。 / @* h+ y' ?% K0 x; j M# _& I! r# ~: L+ Z; ^
大多数数据库都索引自动创建的主键字段,但是可别忘了索引外 + S- I' r4 t- d& z1 [5 h" @ 键,它们也是经常使用的键,比如运行查询显示主表和所有关联表的7 z! W+ m" p3 {0 C( `4 V: O
某条记录就用得上。还有,不要索引 memo/no te 字段,不要索引大- ?8 g. G3 ~8 D( J1 d2 P2 w
型字段(有很多字符),这样作会让索引占用太多的存储空间。# w5 \& z! B7 h/ s1 a# O
% L, L# l7 I: }/ X ■ 不要索引常用的小型表 + b/ D# a5 A+ R5 ^# v4 I( p @! y, c( _$ x1 m ?2 H1 E
不要为小型数据表设置任何键,假如它们经常有插入和删除操作 ; T \6 y+ C% @" d5 ` 就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空) g! i( j' k! e8 y" |4 C7 n
间消耗更多的时间。 : M6 K0 `& y' S% l- m1 X( M" P/ z5 x p
不要把社会保障号码(SSN)或身份证号码(ID)选作键永远都 ) U, X/ G6 @4 T8 O L/ Z0 O* h 不要使用 SSN 或 ID 作为数据库的键。除了隐私原因以外,须知政* ]- B/ w( Q% p
府越来越趋向于不准许把 SSN 或 ID 用作除收入相关以外的其他目 2 c5 i1 M1 s { 的,SSN 或 ID 需要手工输入。永远不要使用手工输入的键作为主键, 1 g- q0 m9 E& [0 E 因为一旦你输入错误,你唯一能做的就是删除整个记录然后从头开始。 - R7 A. R3 A" T, v! n! V; l7 J3 @8 w 2 m+ |) L2 z) P, }$ p2 d 我在破解他人的程序时候,我看到很多人把 SSN 或 ID 还曾被 ' `0 I$ `$ X+ Y v5 i' b 用做系列号,当然尽管这么做是非法的。而且人们也都知道这是非法. x& B g( m1 e# d6 _: s5 B) `
的,但他们已经习惯了。后来,随着盗取身份犯罪案件的增加,我现 # r' B6 v9 \1 L' v 在的同行正痛苦地从一大摊子数据中把 SSN 或 ID 删除。 a! c" k% O' k- M
% N O' g0 t, D& Q ■ 不要用用户的键 , N t o2 _0 \8 C% b$ R" a' r. p# G3 W1 ^* m& }- ]% {! ^3 N
在确定采用什么字段作为表的键的时候,可一定要小心用户将要 + q l, O' i7 A/ t1 a* |8 C 编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。这样' ~7 M# i2 U6 Z% X4 f" k" ] C# t
做会迫使你采取以下两个措施: ( P/ m) O/ e" k & R/ Z5 s) [( G0 t" [ 1. 在创建记录之后对用户编辑字段的行为施加限制。假如你这么做 7 M. C1 b& {% g: k" j' j 了,你可能会发现你的应用程序在商务需求突然发生变化,而用0 p& I4 a7 Q( Y+ c0 l
户需要编辑那些不可编辑的字段时缺乏足够的灵活性。当用户在) A* u) H9 i6 ]8 ]
输入数据之后直到保存记录才发现系统出了问题他们该怎么想?! Z8 S4 u. y! H8 H, k( d. f
删除重建?假如记录不可重建是否让用户走开? 3 x8 l) Y. S# h1 r: e5 l 2. 提出一些检测和纠正键冲突的方法。通常,费点精力也就搞定了,8 T" i. w. @6 h# n: H
但是从性能上来看这样做的代价就比较大了。还有,键的纠正可 7 w( @2 F0 |# X7 y1 W" R7 o 能会迫使你突破你的数据和商业/用户界面层之间的隔离。 ; O& S7 `8 H. L/ P; K- r( P : z, Y" k% J+ a7 Y& u, N
所以还是重提一句老话:你的设计要适应用户而不是让用户来适4 l" U% u! q- W4 F6 [
应你的设计。 p2 e4 r! \. X7 S) q. m% B7 A2 q4 g+ `- J
不让主键具有可更新性的原因是在关系模式下,主键实现了不同, U$ W! o; ~" }4 ]4 L' u
表之间的关联。比如,Cu stomer 表有一个主键 CustomerID,而客 / l, B9 j3 j. Y P3 w 户的定单则存放在另一个表里。Order 表的主键可能是 OrderNo 或! X) t" V7 r+ ^* S3 p
者 OrderNo、CustomerID 和日期的组合。不管你选择哪种键设置,) l. {! ?$ W" K# Y; n& k% S
你都需要在 Order 表中存放 CustomerID 来保证你可以给下定单的 6 G# h' r+ O8 w6 d! o2 B 用户找到其定单记录。+ _" A+ s) q8 r- A" u
5 u1 B3 G( c" g9 B! v: O" m* ] 假如你在 Customer 表里修改了 CustomerID,那么你必须找出 - L5 U6 c9 n& @ W# F& l
Order 表中的所有相关记录对其进行修改。否则,有些定单就会不属/ M; s- ^! e. @8 B1 X1 ~1 ?
于任何客户——数据库的完整性就算完蛋了。 a: S0 t k+ Z) @
1 e) z6 G" r! P 如果索引完整性规则施加到表一级,那么在不编写大量代码和附; r' c8 `$ O' j- O% I
加删除记录的情况下几乎不可能改变某一条记录的键和数据库内所有 9 G; \; m" S% m3 d" F( g 关联的记录。而这一过程往往错误丛生所以应该尽量避免。 / z O+ p8 Z4 K( y' w* T$ {+ s6 X7 Z
■ 可选键(候选键)有时可做主键7 X7 O( T# l2 A
' {* P( V1 g7 i! k' ` 记住,查询数据的不是机器而是人。 4 G& Q" _9 ]; B9 N: T/ T8 t w+ J% p* x, H; J( W
假如你有可选键,你可能进一步把它用做主键。那样的话,你就 , J9 |( ^( B( d9 r/ } 拥有了建立强大索引的能力。这样可以阻止使用数据库的人不得不连& [( i- }' v2 {& i2 M
接数据库从而恰当的过滤数据。在严格控制域表的数据库上,这种负& V4 |% |& |% W1 b9 O; D/ q
载是比较醒目的。如果可选键真正有用,那就是达到了主键的水准。; @, I% \9 M& v
3 O& u% l$ F8 ?5 L* g; I$ `- \
我的看法是,假如你有可选键,比如国家表内的 state_code,+ X( K6 U5 S9 x6 o% O
你不要在现有不能变动的唯一键上创建后续的键。你要做的无非是创 4 ?4 ^2 j5 q1 Q6 Y+ h4 @ 建毫无价值的数据。如你因为过度使用表的后续键[别名]建立这种表: {% U, k. b) z. O5 R7 Q
的关联,操作负载真得需要考虑一下了。; ^- u; e; ]" ^- w- U
0 H3 y- ~# f: e" X4 Q4 ~; W3 ]1 n( @8 ^ ■ 别忘了外键/ r% Y9 _/ n7 S. l
4 g; o3 N+ _" i
大多数数据库索引自动创建的主键字段。但别忘了索引外键字段, " g |$ H9 n+ X/ {+ V v 它们在你想查询主表中的记录及其关联记录时每次都会用到。还有,8 }$ A& t5 f) k4 E
不要索引 memo/notes 字段而且不要索引大型文本字段(许多字符),0 [, z) }0 {- |; ]0 x6 h. c
这样做会让你的索引占据大量的数据库空间。4 g$ d+ M( L' ?& Q
8 I, X# q% D8 x3 j9 A8 C! l
: y* H: d' ^2 Z/ y) p: F$ }8 K
§ 第 4 部分 - 保证数据的完整性* c3 \# i. C0 o* f6 x: h
────────────── : l5 |, j7 v5 o4 [( |) g9 H 2 B% d/ V! ~9 V( a# B, b1 [! v ■ 用约束而非商务规则强制数据完整性 / j/ p6 N* \% }% F 5 H* y# K$ ?" ?: }% a6 t 如果你按照商务规则来处理需求,那么你应当检查商务层次/用9 b8 d5 u5 }% I: T: S
户界面:如果商务规则以后发生变化,那么只需要进行更新即可。假, j5 J x& C$ y+ V, q4 _3 c8 V$ \
如需求源于维护数据完整性的需要,那么在数据库层面上需要施加限 + J' U; U. h4 | f* Z) ]: v 制条件。如果你在数据层确实采用了约束,你要保证有办法把更新不 ( T+ R7 A' a! ~& w! Y 能通过约束检查的原因采用用户理解的语言通知用户界面。除非你的+ R% _5 W6 a% _! }! F4 ^
字段命名很冗长,否则字段名本身还不够。/ M: }' q, O) g' e& M4 u
; r4 e; X; Y' Z3 S1 | 只要有可能,请采用数据库系统实现数据的完整性。这不但包括 - {/ f4 P8 I2 D T 通过标准化实现的完整性而且还包括数据的功能性。在写数据的时候 9 O5 }! }& x0 Z/ N 还可以增加触发器来保证数据的正确性。不要依赖于商务层保证数据 ! p& E( U4 T/ M! G `4 t 完整性;它不能保证表之间(外键)的完整性所以不能强加于其他完 + ^0 h1 c/ S! O0 B6 n5 ^ 整性规则之上。 9 x6 u4 T: c. {; z. s - d- Q8 Z& Y8 M8 ~3 k; h ■ 分布式数据系统 `$ Q) _# v: \+ l& ?& h% k$ ^
* Q6 T4 Q" B% x v
对分布式系统而言,在你决定是否在各个站点复制所有数据还是: j! F& k7 {4 }" d. z9 ?- `- M
把数据保存在一个地方之前应该估计一下未来 5 年或者 10 年的数* M) |) ?2 H7 `8 J! a+ T
据量。当你把数据传送到其他站点的时候,最好在数据库字段中设置9 ?, P% L, O) b7 N: j6 `3 v3 ^! u9 s
一些标记。在目的站点收到你的数据之后更新你的标记。为了进行这 r+ K: m6 K- W. _# u$ x" j% l 种数据传输,请写下你自己的批处理或者调度程序以特定时间间隔运 : c; w; D* L1 }3 f& j 行而不要让用户在每天的工作后传输数据。本地拷贝你的维护数据, 9 O7 m! [% w/ G( \. W/ l+ @/ o4 [ 比如计算常数和利息率等,设置版本号保证数据在每个站点都完全一+ F, ~/ w: o: p6 t, p
致。/ Q' _& R( n3 z8 N2 L
: {. m, i& u2 Y0 I. U5 r0 [+ m ■ 强制指示完整性(参照完整性?) * G( }7 H4 _: L( V9 ?' g$ O9 x7 D# f9 J' ]; m. h+ ]
没有好办法能在有害数据进入数据库之后消除它,所以你应该在8 P9 p" F" w; R0 X1 I, k5 A
它进入数据库之前将其剔除。激活数据库系统的指示完整性特性。这) K" ^1 a5 a3 G- I: ~
样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条* l9 [/ v4 L5 @1 T& d9 p3 }
件。 3 M, k8 W4 {* o3 B " f/ A+ w# t# c' Q6 i+ U; q$ C ■ 关系6 E$ `5 i9 v; Z9 h, y0 H5 d
: m3 Z {( ?2 f I# ~( O 如果两个实体之间存在多对一关系,而且还有可能转化为多对多 1 y' y; m( I& P: v 关系,那么你最好一开始就设置成多对多关系。从现有的多对一关系 7 f1 x x0 R4 T5 c) X 转变为多对多关系比一开始就是多对多关系要难得多。 & |/ Q, y$ c9 V, m* `% g 9 f7 h& X/ c4 ~ ■ 采用视图( x) B( n$ C: P( K8 t