sqlsql server中数据类型表的数据类型没有设置长度,那是默认长度还是什么? 比如float

7、SQL Server索引、表压缩_SQL技巧_动态网站制作指南
7、SQL Server索引、表压缩
来源:人气:837
7、SQL Server索引、表压缩索引什么是索引?索引是一种磁盘上的数据结构,建立在表或视图的基础上。使用索引可以使数据的获取更快更高校,也会影响其他的一些性能,如插入或更新等。索引主要分为两种类型:聚集索引和非聚集索引。字典的目录就是一个索引,按照拼音查询想要的字就是聚集索引(物理连续,页码与目录一一对应),偏旁部首就是一个非聚集索引(逻辑连续,页码与目录不连续)。聚集索引存储记录是物理上连续存在的,而非聚集索引是逻辑上的连续,物理存储并不连续。聚集索引一个表中只能有一个,而非聚集索引一个表中可以有多个。索引的利弊使用索引是为了避免全表扫描,因为全表扫描是从磁盘上读取表的每一个数据页,如果有索引指向数据值,则只需要读少次数的磁盘就可以。带索引的表在中占用更多的空间,同样增、删、改数据的命令所需时间会更长。索引的存储机制书中的目录是一个字词以及所在的页码列表,数据库中的索引是表中的值以及各值存储位置的列表。聚集索引是在数据库中新开辟一个物理空间,用来存放他排列的值,当有新数据插入时,他会重新排列整个物理存储空间。非聚集索引只包含原表中的非聚集索引的列和指向实际物理表的一个指针。数据表的基本结构当一个新的数据表创建时,系统将在磁盘中分配一段以8k为单位的连续空间。当一个8k用完的时候,数据库指针会自动分配一个8k的空间,每个8k的空间称为一个数据页,并分配从0-7的页号,每个文件的第0页记录引导信息叫页头,每8个数据页由64k组成形成扩展区。全部数据页的组合形成堆。SQLServer规定行不能跨越数据页,所以每行记录的最大数量只能是8k,这就是为什么char和varchar这两种字符类型容量要限制在8k以内的原因,存储超过8k的数据应使用text类型,其实text类型的字段值不能直接录入和保存,它是存储一个指针,指向由若干个8k的数据页所组成的扩展区,真正的数据其实放在这些数据页中。什么情况下设置索引1、定义主键的数据列(sql server默认会给主键一个聚集索引)。2、定义有外键的数据列3、对于经常查询的数据列4、对于需要在指定范围内频繁查询的数据列5、经常在where子句中出现的数据列6、经常出现在关键字 order by、group by、distinct后面的字段。什么情况下不要设置索引1、查询中很少涉及的列,重复值比较多的列。2、text、image、bit数据类型的列3、经常存取的列4、经常更新操作的表,索引一般不要超过3个、最多不要5个。虽说提高了访问速度,但会影响更新操作。聚集索引1、使用SSMS创建聚集索引展开要创建索引的表-&右击索引-&选择新建索引-&聚集索引-&新建索引点添加-&选择列-&选择升序或降序-&输入名字-&确定。默认情况下,生成主键的同时将自动创建一个聚集索引。2、使用T-SQL创建聚集索引use webDBgocreate clustered index index_name /*聚集索引名*/on table_name(
id desc)with(drop_existing=on); /*如果存在则删除*/每张表或者视图只能包含一个聚集索引,因为聚集索引改变了数据存储与排列方式。无论是聚集还是非聚集索引,都将信息存储在平衡树或B-树中,B-树识别类似数据并将他们组合在一起,正是由于B-树中的检索基于键值,因此索引可以提升数据访问的速度。B-树将具有类似键的组合起来,所以数据库引擎只需搜索少量页面即可找到目标记录。非聚集索引每张表上可以有多个非聚集索引,可以在某个列上创建一个索引,也可以在已经是现有索引组成部分的多列上创建索引。SSMS创建方法同上,T-SQL创建方法如下:use webDBgocreate nonclustered index fei /*聚集索引名*/on defualt(
hits desc)添加索引选项fillfactor:用于在创建索引时,每个索引页的数据占索引大小的百分比,默认100.当需要频繁修改表时,建议设置为70-80,不经常更新时建议90.pad_index:用于索引中间级中每个页上保持开放的空间。不能设置值,他的值继承自fillfactor。小例子use webDBgocreate table ceshi --新建表(
id int identity(1,1) imary key,
name varchar(20),
code varchar(20),
[date] datetime)--插入10w条测试数据declare @n intset @n = 1while @n &100000beginInsert into ceshi (name,code,[date]) values ('name'+cast(@n as varchar(20)),'code'+cast(@n as varchar(20)),getutcdate())set @n=@n+1end--查看索引情况set statistics io on --查看磁盘ioset statistics time on --查看sql语句分析编译和执行时间select * from ceshi--查看索引情况exec sp_helpindex ceshiselect * from ceshi where name = 'name1'--ctrl+l 查看执行计划 聚集索引扫描开销100%,考虑优化为索引查找,在name上建立非聚集索引--建立非聚集索引create index name_index on ceshi(
name)--再次查看索引情况 多出来新建的非聚集索引exec sp_helpindex ceshi--在运行上面的语句select * from ceshi where name = 'name1'--明显发现速度变快了 , ctrl+l 发现聚集索引和非聚集索引各占50%管理索引exec sp_helpindex ceshi --查看该表中的索引exec sp_rename 'ceshi.name_index','new_name' --改名drop index ceshi.new_name --删除索引dbcc showcontig(ceshi,new_name) --检查碎片dbcc indexdefrag(webDB,ceshi,new_name) --整理碎片update statistics ceshi --更新表中所有索引的统计表压缩SQLServer的主要性能取决于磁盘I/O效率,SQLServer 2008提供了数据压缩功能来提高磁盘I/O效率。表压缩意味着减小数据的磁盘占有量,所以压缩可以用在堆表、聚集索引的表、非聚集索引的表、索引视图、分区表上。可压缩的数据类型smallint、int、Bigint、decimal、numeric、real、float、money、smallmoeny、bit、datetime、datetime2、datetimeoffset、char、nchar、binary、rowversion。SQLServer中有两种压缩类型:数据与备份行压缩压缩会改变数据的物理存储方式,但不需要对代码做任何修改。行压缩流程:首先识别表中每一列的数据类型,然后转换为可变长度,最后将存储空间的请求总量减少到实际需求量。如:固定长度的类型int、char、nchar等,在数据页中以不定长度的方式存储(存储真实数据长度)。1、SSMS行压缩表-&右键-&存储-&管理压缩-&对所有分区使用相同压缩类型-&右侧选择row-&立即执行-&完成。在压缩堆表或聚集索引时并不同时包含非聚集索引,因此需要另外单独对非聚集索引进行操作。索引-&右键-&存储-&管理压缩-&对所有分区使用相同压缩类型-&右侧选择row-&立即执行-&完成。2、T-SQL行压缩--在现有表进行压缩--聚集alter table ceshi rebuild with(data_compression=row)--非聚集alter index new_name on ceshi rebuild with(data_compression=row) --在创建表时进行压缩create table yasuo(
id int primary key,
name varchar(50),
mail varchar(50))with (data_compression=row)创建时指定行压缩方式,这时并未发生改变。只要数据插入表中,该行即被压缩。页压缩页压缩通过执行额外的一些步骤增强了行压缩的功能。页压缩步骤:行压缩、前缀压缩、字典压缩。首先对于每一列将确定一个值,此值可以减少每一列中值的存储空间。一旦确定该值后,每一列的前缀值的行将被存储在页头中。所有的信息称为压缩信息,存储在页头之下。标识的值(前缀值)位于没列中,将由指向压缩信息部分中对应值的引用进行替换。下一步字典压缩,搜索整个页面而非单个列,重复值被移动到页头的压缩信息部分,取而代之的是指向该值的引用。在SSMS中页压缩步骤与行压缩步骤一致,只是选择压缩方式为Page。T-SQL中将row改成page即可。需要注意1、如果保留在内存中的数据是压缩的,一旦被选中,则必须先进行解压缩。2、在插入新行时,数据也是行或页压缩的。3、当更新或删除时,行压缩对象保留当前的压缩级别。但是页压缩可能需要重新计算,取决于发生变化的数据量。用哪种压缩需要频繁更新的对象应该使用行压缩。只是执行读取操作的应该使用页压缩。
优质网站模板Microsoft Access、MySQL 以及 SQL Server 所使用的数据类型和范围。
Microsoft Access 数据类型
数据类型描述存储
用于文本或文本与数字的组合。最多 255 个字符。
Memo 用于更大数量的文本。最多存储 65,536 个字符。
注释:无法对 memo 字段进行排序。不过它们是可搜索的。
允许 0 到 255 的数字。
允许介于 -32,768 到 32,767 之间的数字。
允许介于 -2,147,483,648 与 2,147,483,647 之间的全部数字
单精度浮点。处理大多数小数。
双精度浮点。处理大多数小数。
用于货币。支持 15 位的元,外加 4 位小数。
提示:您可以选择使用哪个国家的货币。
AutoNumber
AutoNumber 字段自动为每条记录分配数字,通常从 1 开始。
用于日期和时间
逻辑字段,可以显示为 Yes/No、True/False 或 On/Off。
在代码中,使用常量 True 和 False (等价于 1 和 0)
注释:Yes/No 字段中不允许 Null 值
Ole Object
可以存储图片、音频、视频或其他 BLOBs (Binary Large OBjects)
包含指向其他文件的链接,包括网页。
Lookup Wizard
允许你创建一个可从下列列表中进行选择的选项列表。
MySQL 数据类型
在 MySQL 中,有三种主要的类型:文本、数字和日期/时间类型。
Text 类型:
数据类型描述
CHAR(size)
保存固定长度的字符串(可包含字母、数字以及特殊字符)。在括号中指定字符串的长度。最多 255 个字符。
VARCHAR(size)
保存可变长度的字符串(可包含字母、数字以及特殊字符)。在括号中指定字符串的最大长度。最多 255 个字符。
注释:如果值的长度大于 255,则被转换为 TEXT 类型。
存放最大长度为 255 个字符的字符串。
存放最大长度为 65,535 个字符的字符串。
用于 BLOBs (Binary Large OBjects)。存放最多 65,535 字节的数据。
MEDIUMTEXT
存放最大长度为 16,777,215 个字符的字符串。
MEDIUMBLOB
用于 BLOBs (Binary Large OBjects)。存放最多 16,777,215 字节的数据。
存放最大长度为 4,294,967,295 个字符的字符串。
用于 BLOBs (Binary Large OBjects)。存放最多 4,294,967,295 字节的数据。
ENUM(x,y,z,etc.)
允许你输入可能值的列表。可以在 ENUM 列表中列出最大 65535 个值。如果列表中不存在插入的值,则插入空值。
注释:这些值是按照你输入的顺序存储的。
可以按照此格式输入可能的值:ENUM('X','Y','Z')
与 ENUM 类似,SET 最多只能包含 64 个列表项,不过 SET 可存储一个以上的值。
Number 类型:
数据类型描述
TINYINT(size)
-128 到 127 常规。0 到 255 无符号*。在括号中规定最大位数。
SMALLINT(size)
-32768 到 32767 常规。0 到 65535 无符号*。在括号中规定最大位数。
MEDIUMINT(size)
-8388608 到 8388607 普通。0 to
无符号*。在括号中规定最大位数。
常规。0 到
无符号*。在括号中规定最大位数。
BIGINT(size)
-4775808 到 4775807 常规。0 到
无符号*。在括号中规定最大位数。
FLOAT(size,d)
带有浮动小数点的小数字。在括号中规定最大位数。在 d 参数中规定小数点右侧的最大位数。
DOUBLE(size,d)
带有浮动小数点的大数字。在括号中规定最大位数。在 d 参数中规定小数点右侧的最大位数。
DECIMAL(size,d)
作为字符串存储的 DOUBLE 类型,允许固定的小数点。
* 这些整数类型拥有额外的选项 UNSIGNED。通常,整数可以是负数或正数。如果添加 UNSIGNED 属性,那么范围将从 0 开始,而不是某个负数。
Date 类型:
数据类型描述
日期。格式:YYYY-MM-DD
注释:支持的范围是从 '' 到 ''
DATETIME()
*日期和时间的组合。格式:YYYY-MM-DD HH:MM:SS
注释:支持的范围是从 ' 00:00:00' 到 ' 23:59:59'
TIMESTAMP()
*时间戳。TIMESTAMP 值使用 Unix 纪元(' 00:00:00' UTC) 至今的描述来存储。格式:YYYY-MM-DD HH:MM:SS
注释:支持的范围是从 ' 00:00:01' UTC 到 ' 03:14:07' UTC
时间。格式:HH:MM:SS注释:支持的范围是从 '-838:59:59' 到 '838:59:59'
2 位或 4 位格式的年。
注释:4 位格式所允许的值:1901 到 2155。2 位格式所允许的值:70 到 69,表示从 1970 到 2069。
* 即便 DATETIME 和 TIMESTAMP 返回相同的格式,它们的工作方式很不同。在 INSERT 或 UPDATE 查询中,TIMESTAMP 自动把自身设置为当前的日期和时间。TIMESTAMP 也接受不同的格式,比如 YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD 或 YYMMDD。
SQL Server 数据类型
Character 字符串:
数据类型描述存储
固定长度的字符串。最多 8,000 个字符。
varchar(n)
可变长度的字符串。最多 8,000 个字符。
varchar(max)
可变长度的字符串。最多 1,073,741,824 个字符。
可变长度的字符串。最多 2GB 字符数据。
Unicode 字符串:
数据类型描述存储
固定长度的 Unicode 数据。最多 4,000 个字符。
nvarchar(n)
可变长度的 Unicode 数据。最多 4,000 个字符。
nvarchar(max)
可变长度的 Unicode 数据。最多 536,870,912 个字符。
可变长度的 Unicode 数据。最多 2GB 字符数据。
Binary 类型:
数据类型描述存储
允许 0、1 或 NULL
固定长度的二进制数据。最多 8,000 字节。
varbinary(n)
可变长度的二进制数据。最多 8,000 字节。
varbinary(max)
可变长度的二进制数据。最多 2GB 字节。
可变长度的二进制数据。最多 2GB。
Number 类型:
数据类型描述存储
允许从 0 到 255 的所有数字。
允许从 -32,768 到 32,767 的所有数字。
允许从 -2,147,483,648 到 2,147,483,647 的所有数字。
允许介于 -9,223,372,036,854,775,808 和 9,223,372,036,854,775,807 之间的所有数字。
decimal(p,s)
固定精度和比例的数字。允许从 -10^38 +1 到 10^38 -1 之间的数字。
p 参数指示可以存储的最大位数(小数点左侧和右侧)。p 必须是 1 到 38 之间的值。默认是 18。
s 参数指示小数点右侧存储的最大位数。s 必须是 0 到 p 之间的值。默认是 0。
numeric(p,s)
固定精度和比例的数字。允许从 -10^38 +1 到 10^38 -1 之间的数字。
p 参数指示可以存储的最大位数(小数点左侧和右侧)。p 必须是 1 到 38 之间的值。默认是 18。
s 参数指示小数点右侧存储的最大位数。s 必须是 0 到 p 之间的值。默认是 0。
smallmoney
介于 -214,748.3648 和 214,748.3647 之间的货币数据。
介于 -922,337,203,685,477.5808 和 922,337,203,685,477.5807 之间的货币数据。
从 -1.79E + 308 到 1.79E + 308 的浮动精度数字数据。 参数 n 指示该字段保存 4 字节还是 8 字节。float(24) 保存 4 字节,而 float(53) 保存 8 字节。n 的默认值是 53。
4 或 8 字节
从 -3.40E + 38 到 3.40E + 38 的浮动精度数字数据。
Date 类型:
数据类型描述存储
从 1753 年 1 月 1 日 到 9999 年 12 月 31 日,精度为 3.33 毫秒。
从 1753 年 1 月 1 日 到 9999 年 12 月 31 日,精度为 100 纳秒。
smalldatetime
从 1900 年 1 月 1 日 到 2079 年 6 月 6 日,精度为 1 分钟。
仅存储日期。从 0001 年 1 月 1 日 到 9999 年 12 月 31 日。
仅存储时间。精度为 100 纳秒。
datetimeoffset
与 datetime2 相同,外加时区偏移。
8-10 bytes
存储唯一的数字,每当创建或修改某行时,该数字会更新。timestamp 基于内部时钟,不对应真实时间。每个表只能有一个 timestamp 变量。
其他数据类型:
数据类型描述
sql_variant
存储最多 8,000 字节不同数据类型的数据,除了 text、ntext 以及 timestamp。
uniqueidentifier
存储全局标识符 (GUID)。
存储 XML 格式化数据。最多 2GB。
存储对用于数据库操作的指针的引用。
存储结果集,供稍后处理。
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?&p&之前给团队编制过一份数据库设计规范,详细总结了数据库设计、使用过程中的种种细节问题,并给出了相应的解决方案。编写制定本文档的过程中,从网络上翻阅了数百份数据库设计相关的文章、帖子,敢说是目前网络中流传的最认真用心全面的一份关系型数据库设计相关的文档。&/p&&h2&一 文档说明&/h2&&p&&b&1、引言&/b&&/p&&p&“文档说明”部分将给出本数据库设计规范的编写背景,并从全局介绍数据库设计过程中各环节、各模块的规范要求。&/p&&p&&b&2、编写背景&/b&&/p&&p&“HEBSMJYPT”的项目告一段落后,着手开始“JCFGTGYLJRDSJ”项目。所在单位所领团队之前的数据库设计工作是按业务模块划分后交由不同的开发人员负责,没有统一规范,组合在一起的完整设计相对杂乱。数据库设计的完整性、统一性被破坏后,程序部分自然也会受到影响,进而影响整个项目的稳定性、协调性。即便是过去独立负责设计的数据库,在细节上也有诸多不合理的地方,其中一个重要的诱导因素就是不统一——设计理念、字段类型、命名方式、通用表通用字段的处理方法等等因为没有规范的约束而导致的不统一。&/p&&p&所以在开始“JCFGTGYLJRDSJ”项目的设计之前,着手制定出一套标准合理的数据库设计规范,至此之后的项目,数据库设计工作无论由一人负责还是多人协作,都严格依此规范进行,籍此避免再次出现早前的许多问题。&/p&&p&在制定本规范之前,从各种渠道梳理汇总了数百份数据库设计规范相关的文档,也回看了过去负责或参与项目的数据库结构图,把好的设计方案加以总结沉淀、把尚有问题的地方列出并给出相应的解决方法,根据以往经验和现实需要将这些种种去其糟粕取其精华,汇成一家。&/p&&p&&b&3、文档概述&/b&&/p&&p&整个规范的制定可以分成两部分,第一部分是设计规范,第二部分是命名规范。命名规范也属于设计规范的模块,但因其比较重要,且涉及的内容较多,故将其剥离单独介绍。本来文档编辑之初还想写一个操作规范模块,操作规范用来约束数据库设计完成之后的工作,比如程序编写过程中SQL语句对数据的增删改查,开发过程中对表结构的增删改,维护过程中对数据库的备份迁移等等。但因为时间和个人缘故,最终决定拿掉此模块,此模块中的一些涉及点融合到了设计规范和命名规范中进行介绍。在文档的最后,也会给出此规范编写过程中的参考文献,多是网络上个人撰写的数据库设计规范相关的非正式文章。&/p&&p&中小型的Web项目,数据库以MySQL、SQLServer及Oralce为主,非关系型数据库不在本规范的考虑范围之内,而不同的关系型数据库设计理念大同小异,不过具体到细节上,又可以说差异巨大。鉴于当前开发面临的实际情况,本规范的制定主要针对(但不限于)MySQL,对于特别部分,会对比不同的关系型数据库具体说明,如无特别说明,则默认表示MySQL数据库。&/p&&p&此为1.0版本,制定基本规范,后面会持续更新,根据不同的项目、不同的数据库、不同的程序架构、不同的开发模式、不同的数据量并发访问量等实际情况,在现有规范的基础上完善修正,越是后期的版本,规范会愈加全面、愈加灵活、愈加合理、适应性愈加更广阔。&/p&&h2&二 设计规范&/h2&&p&&b&1、引言&/b&&/p&&p&设计规范部分目前八个子模块:设计理念、基本原则、字段设置、相关注释、约束控制、索引添加、特别说明及梳理总结部分,命名规范部分单独列出一个大模块,在后面的章节介绍。本章节是文档的核心部分,详细说明数据库设计步骤、设计原理、设计过程中各环节要遵守的原则、注意事项等。&/p&&p&目前的设计规范并不全面,存储过程、触发器、函数、视图、事件等高级功能的设计规范都未特别说明,原因后面会讲。在后续版本的规范文档中,可根据实际情况修改扩充。&/p&&p&&b&2、设计理念&/b&&/p&&blockquote&A.设计流程&/blockquote&&p&从项目启动之时,数据库设计工作就已经开始,贯穿于项目前期阶段的需求调研、分析、确认、业务梳理过程,只不过这时的设计大都停留在脑海中,正式的设计过程起始于最终的需求确认完成、业务梳理清晰之后。&/p&&p&就目前来看,最好的关系型数据库设计工具还是PowerDesigner(以后简称PD),我们要求正式数据库设计过程中必须使用此工具,先出CDM(Concept Data Model,概念数据模型),再根据实际的数据库类型由CDM导出PDM(Physical Data Model,物理数据模型),最后由PDM将设计成果直接导入到数据库中。同时导出相应的数据库文档,以供项目组开发人员查阅。&/p&&p&CDM设计过程中只做最简单必要的设计,约束、关系、主键、外键、命名规则等在转换成PDM过程中PD可自行处理的部分均交由工具自行处理。就是说数据库设计者只负责工具无法处理的少许部分,工具可完成的部分则用工具统一处理,这样设计工作会更高效省时,最终的设计成果也会更规范合理。 &/p&&p&初版数据库设计完成之后进入项目开发阶段,如果前期的需求调研分析、业务梳理没问题,后期数据库结构发生大规模改动的情况不应该频繁出现的。但需求总是在变,意外才是唯一的法则,即便设计者在前期调研、了解、分析、设计的过程中再过谨慎,怕只能减少这种情况出现的风险,却不能完全避免。还有随着开发的深入,数据库结构发生局部变动,比如增删改些表字段等也是再正常不过的了。针对这些,都应该有相应的对策,才能以不变应万变。&/p&&p&第一版的数据库设计完成,由PDM生成相应的SQL脚本在数据库中执行之后,随开发深入而再进行的数据库改动分两种情况:一种是大改,比如原有的业务有变动,或因在设计时考虑不周、对需求了解不清导致设计出错,表及引用关系都要发生重大变化,刚也说了这种情况不应该出现;另一种是小改,比如有新增业务的情况要新建些表,有拿掉部分业务的情况可能会删除些表,还有更常出现的是增删改部分表的部分字段。&/p&&p&对于以上两种,有增加表或大规模业务变动的情况,建议是在PD中修改PDM,然后重新生成SQL脚本在数据库中执行,当然只生成新增表或发生业务变动部分的即可,而对于简单的增删改些字段或业务变动不大的情况,建议直接在数据库中对表进行修改。然后通过PD菜单中的Database——Update Model from Database……连接数据库逆向更新PDM,使PDM和最新的数据库结构保持一致,而后再生成最新的数据库文档。但要注意的是,使用此功能逆向更新PDM,只会更新修改或新增的表字段,而不更新删除的字段。就是说,如果在数据库中对一个表的字段进行了修改,或者新增了一些字段,PD会同步在PDM中的相应表中做出相应的修改或新增,但如果在数据库中删除了表中的某个字段,PD是不会删除PDM中相应表的相应字段的。为什么这般处理,自己也觉得很奇怪。&/p&&p&后期的数据库改动,一般发生在程序开发启动后,如果主程序可自动生成,第二种小改的情况还是比较容易处理的,重新生成下主程序,而手写的部分(非自动生成部分)一般不会受到太大影响。但如果是第一种情况,业务发生了变动,那可能意味着手写的程序(非自动生成部分)要重写。之前的文章中也有提及,一定要注意前期的需求调研了解分析系统设计,后期的问题几乎都是由前期的不慎造成,有经验的项目经理可以在前期预料到后面可能的问题而提前采取相应的预防措施。防之于未有,治之于未乱。项目想要做的出色,有太多不可测因素,但如果手底下的项目都很稳当,其能力必是值得肯定的。&/p&&a class=&video-box& href=&///?target=http%3A///v_show/id_XMjY3NjYxNTAyOA%3D%3D.html%3Fspm%3Da2hzp..0%26from%3Dy1.7-2%23paction& target=&_blank& data-video-id=&706944& data-video-playable=&true& data-name=&龚鼎孳.mp4& data-poster=&/v2-5ec3655680cbb17553fa.jpg& data-lens-id=&&&
&img class=&thumbnail& src=&/v2-5ec3655680cbb17553fa.jpg&&&span class=&content&&
&span class=&title&&龚鼎孳.mp4&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&/v_show/id_XMjY3NjYxNTAyOA==.html?spm=a2hzp..0&from=y1.7-2#paction&/span&
&/a&&p&这里还要提一点,后期数据库表小范围的修改一般是由开发人员发现,比如在开发过程中发现少了一些字段,或局部业务有些问题等等,应该禁止开发人员擅自直接更改数据库,所有更改无论大小必须经过数据库主设计师的审核同意,以避免可能影响到全局的更改出现。&/p&&p&数据库的设计工作虽然集中在项目的业务梳理清晰之后、正式开发之前,但相关细枝末节的工作却不止于此,很可能会贯穿于整个项目的起始流程。&/p&&blockquote&B.三种关系&/blockquote&&p&有人说数据库难以设计,其实难的并不是数据库的设计,而是业务流程的梳理。再复杂的业务,只要理得清,表现在数据库中,无外乎是表与表间的三种关系:一对一(one-to-one)、一对多(one-to-many )以及多对多(many-to-many)。更进一步的,many-to-many实际上就是两个one-to-many。&/p&&p&在Java中万事万物皆对象,在关系型数据库中万事万物皆是二维表,而事物之间的联系系就是表与表间的这三种关系。&/p&&p&后面还会多次提及,我们的设计原则是尽可能让粒度小、容忍度高,比如在“设计规范”——“字段设置”——“通用字段处理”中有关于日期时间类型设置的说明,要求日期时间类型的字段,尽可能用datetime类型,精确到时分秒,而不要用date类型。表现在这里,处理业务关系时,对于核心业务部分尚不能明确表与表关系的,能一对多就不要一对一,能多对多就不要一对多。这样开发的复杂度会增加,却消除了后面可能的修改扩展的隐患。对于非核心业务也不能明确关系的,可根据实际情况,综合考量开发实现的烦琐程度及未来的可变性再做决定。&/p&&p&PD细化了这三种关系的表述,表现在CDM关系中Cardinalities选项卡的Cardinality选项中、表现在PDM引用中Integrity选项卡的Cardinality选项中。Cardinality,基数,在CDM的选项中表示另一方对于当前方的每个实例,可能拥有的实例的最少和最多数;在PDM的选项中表示父表中的每个实例,子表中可能拥有的实例的最少和最多数。&/p&&p&比如“病人”与“会诊单”两个实体之间的联系是one-to-many联系,换个方向说“会诊单”和“病人”之间的联系是many-to-one联系。而且一个会诊单必须属于一个病人,并且只能属于一个病人,不能属于零个病人,所以从“会诊单”实体至“病人”实体的基数为“1,1”;从联系的另一方向考虑,一个病人可以拥有多个会诊单,也可以没有任何会诊单,即零个会诊单,所以该方向联系的基数就为“0,n”。CDM中的表示如下图所示:&/p&&img src=&/v2-dfe58cbbe02a155faabc9a_b.png& data-rawwidth=&1003& data-rawheight=&812& class=&origin_image zh-lightbox-thumb& width=&1003& data-original=&/v2-dfe58cbbe02a155faabc9a_r.png&&&p&在构建CDM、选择两个实体之间的关系时,这部分会自动赋值的。&/p&&p&类似的,一个品牌必需且只能属于一个企业,一个企业却可以有一个或多个品牌,又或者一个也没有。在PDM的选项中表示父表为企业、子表为品牌,父表(企业)中的每个实例,子表(品牌)中可能拥有的实例的最少和最多数。如下图所示:&/p&&img src=&/v2-84bc75c2ac640_b.png& data-rawwidth=&1003& data-rawheight=&812& class=&origin_image zh-lightbox-thumb& width=&1003& data-original=&/v2-84bc75c2ac640_r.png&&&p&PD的细化在于,one-to-one关系中到底是must have one and only one还是may have at most one,one-to-many关系中到底是must have one or more还是may have one or more。many-to-many关系需要借助中间表实现,分解成两个one-to-many组合,类似的,也可被细分成严格的多对多还是模糊的多对多。可以拿用户和角色的例子说明,严格的多对多是一个用户至少有一个角色、一个角色至少被一个用户拥有,模糊的多对多是一个用户可以没有任何角色、一个角色可以不被任何用户拥有。&/p&&p&个人认为对one-to-many和many-to-many两种关系进行细化的意义不大,因为对于one-to-many、many-to-one,无论是否严格,都要在many方加入引用one方主键的外键;而对于many-to-many,无论是否严格,中间表都是必须的。所以对于这两种关系,无论是否为严格的,设计方案是确定的。&/p&&p&但对于one-to-one,严格的和非严格关系之间设计上是有区别的。比如订单和取消原因,一个订单可以有一个或零个取消原因(may have at most one ),本来如果是严格的一对一关系,取消原因是可以整合到订单表中的,而无需单建表存储。 再比如班级与班长,一个班级只有一个正班长, 一个班长只在一个班中任职,两边都是must have one and only one,那班长表完全可以省略,而直接在班级表中加入相应描述班长信息的字段即可,比如班长名称、性别等等,又或者直接有个班长的外键字段指向学生表。&/p&&blockquote&C.如何设计&/blockquote&&p&拿到项目后,完成早期的需求调研,在分析设计的过程中,先考虑的是要实现所需功能、需要的实体有哪些。比如要实现登录功能,那必需要有用户实体。继续扩展思路,登录往往伴随着角色划分,为此要有角色、权限相关的实体。为了记录登录请求,又要有登录日志实体。这些实体表现在数据库即为相应的表,实体确定,同步考虑实体与实体间的关系。一个用户可以有多个角色,一个角色可以被多个用户拥有,所以用户和角色是多对多关系,多对多关系意味着出现中间表。一个用户可以有多条登录日志,一条登录日志有且只能对应一个用户,所以用户和登录日志是一对多的关系,登录日志表中要有外键引用用户表。这就是数据库设计的过程。&/p&&p&数据库的设计往往和前端界面的设计并行,前者稍晚于后者,两者的进行伴随在业务梳理的过程中、前期需求确认之后,为确保对需求理解的准确性、设计的准确性,此过程中应该继续和需求提出者保持沟通。如果在设计数据库时不考虑前端界面设计、或者是在设计界面时不考虑数据库设计,且不说两边对业务需求的理解可能有偏差,即便完全没有,最终怕也不能完好无误的进行融合,这也是为什么觉得项目经理、产品经理、技术经理的角色一人扮演要比三人分饰更好的原因。成熟的社会体制下,人与人间的分工应该更加明确,这无可厚非,问题就出在很多公司分工提前明确了,协作体制却不完善。如此这般,做一件事情参与的人越多,不但不会节省工时,反而导致的问题越多,大大降低了工作效率。&/p&&p&负责数据库设计工作的人应该是最懂项目、最懂业务需求、最有设计经验的人,此人必须跟踪整个项目的设计开发过程,产品界面的设计、程序的开发都要和其沟通确认方可。项目设计开发实施过程中,很多决定,不是参与决定的人越多越好,而是由一个最懂项目的人决定最好。有时为了某个决定组织集体讨论,最终的决定往往来自于主导讨论者,而非是最懂项目者。做项目和带兵打仗一样,最怕饭桶主导局势。而做为项目主负责人,你可以不是最懂项目的,但一定要清楚的是这个项目安排给谁去懂最合适,他有没有这个能力、是否能听从安排。一旦让他去懂,在项目推进过程中观察他是否能胜任、各子环节最懂的那个人又是谁。明白谁最懂、谁肯听众安排,则把相应部分的决定权交到相应最懂、最听从安排的人手中。而后除非出现特殊情况,自己不要去干涉、也不要让其他人去干涉。&/p&&blockquote&D.主设计师&/blockquote&&p&原则上讲,百张表内的数据库最好由一人来设计。一百张表,即使每张有一百个字段,总共也不过一万个,如果用工具生成,三五天内足够。当然数据库设计工作的难易并非是由表个数、字段个数决定的,而更多在于业务的复杂程度。表个数、字段个数在一定程度上可以反应出业务的复杂程度,但却非决定性因素。这里讲百张表内的设计工作量并不大,是说业务理清后将设计具体化成CDM、PDM的体力工作。要求由一人来设计是为了确保最终数据库的统一性、完整性、协调性,如果不能保证统一,最终项目的稳定性绝对得不到保障。不仅数据库的设计如此,架构、程序、前端、样式、脚本、UI都一样。在项目设计阶段,通常情况下,局部模块设计的优良并不会提升整个项目的质量,然而局部模块设计出现的问题最终却可能撼动整个项目的稳定。千里之堤毁于蚁穴,务必要确保设计工作的谨慎协调统一。再说大部分企业内部应用系统,项目规模有限,一个人主设计足够。&/p&&p&特殊情况下,如果项目大到一定程度,所有数据库设计工作交到一人手上着实过量,不得不安排多人参与其中,那主设计师也必须只有一位,且所有参与人员都要严格遵守相应的数据库设计规范。要利用PD的版本控制功能协调统一,最终由主设计师汇总校验所有人的设计,最终的数据库设计应该看起来像一个人的杰作,这也是程序、脚本、样式、UI设计开发所追求的目标。&/p&&p&主设计师汇总校验之后,还应再组织会议对设计成果校验,包括所有参与设计的人员、程序开发人员等,一起讨论。查找可能出现的不合理的地方,比如有部分可能和需求业务不合、可能会影响到开发实现等等。数据库的校验审核工作,参与人员尽可能多点,鼓励提问,有助于查缺补漏,发现问题。&/p&&blockquote&E.关于DBA&/blockquote&&p&有公司规定所有项目的数据库设计,DBA必须参与,但数据库设计工作重在对业务的把控、了解,其次才是对数据库本身的了解,这两项缺一不可,且前者更为重要。DBA的专业技能或许会更好一点,但是对业务的了解呢?还有公司的运维团队中,DBA会脱离项目实际,设置一些不必要的数据库规范,强加给开发团队。&/p&&p&在做项目时,一方面非常讨厌外部强加的规范,另一方面又不停的给自己、给团队设定规范。讨厌外部强加的规范是因为这些规范大都是些PMO指定的人凭空设想出来、脱离项目实际的,这些规范只会给项目带来更多的麻烦。而给自己和团队设定规范,则是为了约束设计和开发行为,确保项目最终实现的合理统一协调,这在后面的“命名规范”——“引言”中还有进一步的说明。&/p&&p&大的项目、大的团队可能会有多个专业DBA负责数据库维护工作,但就自己接触,即便公司、团队本身有DBA,绝大部分规模的项目中数据库维护工作还是多由程序开发人员兼顾。再者,真正优秀的DBA少之又少,有时不够专业的DBA过多介入反而会阻碍项目的正常开发。&/p&&p&DBA出现的时机应该是在开发人员无法解决数据库出现的问题时,比如当数据量大到一定程度,项目运行缓慢,仅凭程序优化已遇到瓶颈,这时可以向公司申请DBA介入,优化数据库设计、SQL语句等。再比如遇到数据备份问题、出现数据丢失问题等,也可申请DBA协助。如果项目规模大到一定程度,出现性能瓶颈问题是很正常的,这时专业DBA的作用才开始突显,且会在其中扮演一个非常重要的角色。&/p&&p&总之,DBA应该是在被需要时出现,而不应该被强制需要。&/p&&p&我的项目开发思路非常明确,关于团队,人越少越优秀越好,人员明确分工;关于开发模式,前端、后端、数据库明确分工。在项目规模、工作量允许的范围内人越少越好、涉及合作的部门越少越好,以便统一管理控制,节约沟通协调的成本。开发过程中的规范和约定采用大一统的方式,严格限制脱离中央管控的脚本或代码出现。&/p&&p&&b&3、基本原则&/b&&/p&&blockquote&A.数据编码&/blockquote&&p&这里建议将MySQL数据库编码设置为utf8, SQLServer、Oralce可先采用默认设置,有需要则根据实际情况做相应变动。如果是MySQL在安装时就应注意,下图是当前电脑中数据库的编码设置,安装时没有改过,是默认的,所以显示的是latin1。&/p&&img src=&/v2-dfb3deb9a7ab7f70da6724affa6979f7_b.png& data-rawwidth=&1918& data-rawheight=&1019& class=&origin_image zh-lightbox-thumb& width=&1918& data-original=&/v2-dfb3deb9a7ab7f70da6724affa6979f7_r.png&&&p&编码问题设置不妥当,数据库导入导出备份很容易出现乱码,尤其是旧版本的MySQL数据库。日常的数据库维护操作推荐使用其官方工具MySQL Workbench,Oracle的话推荐SQL Developer,也是官方工具。如果使用MySQL Workbench创建新的数据库,会要求选择Collation,默认即可。Collation的字面意思是字符序,用于指定数据集如何排序,以及字符串的比对规则。这地方的设置会影响到数据查询时大小写敏感的问题,这里简单一提,具体介绍的会在命名规范中给出。&/p&&p&如果是MySQL数据库,表类型统一使用InnoDB。InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。其区别在于MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持及外部键等高级数据库功能。没有进行过额外设置,数据库默认安装的就是InnoDB类型。&/p&&blockquote&B.高级功能&/blockquote&&p&在本章节的引言中提到过,存储过程、触发器、函数、视图、事件等高级功能的设计规范都未给出详细说明,因为本规范中要求,在当前阶段的所有项目中,禁止使用存储过程、触发器、函数、视图、事件等高级功能,数据库中只有表,数据库只做数据存储、标明业务关系。而具体业务逻辑相关的处理均由程序实现,比如分页、日志记录等等。&/p&&p&某些情况下,一些业务逻辑放在数据库中处理比放在程序中处理要简单许多,但这会额外增加数据库的维护工作,从全局来看,也就是增加增个项目的维护工作。尤其是项目要求支持多数据库时,不同数据库存储过程、触发器、函数等的编写方式差异巨大,很难统一控制。业务逻辑下压到数据库的部分越多,这种维护就愈加困难,到一定程度必然要求有专业的DBA配合,额外增加了不必要的麻烦。视图的添加更是没有必要,连接查询可以实现的功能,用视图只会从全局上扰乱原有的数据库表关系,让程序多维护一套查询程序,徒增麻烦,而所得收益完全可由单纯的程序实现。&/p&&p&前面已提到所坚持的开发模式,给前端、程序、数据库明确的分工:前端只负责数展示、人机交互,由前端脚本处理展示交互相关的逻辑;后台程序负责桥接前端界面和数据库,处理核心业务逻辑;数据库部分只用来数据存储、标明业务关系。三部分各司其职,不要在数据库中直接处理业务逻辑,不要在后台程序中直接处理前端展示或人机交互的逻辑。&/p&&p&当项目庞大到一定程度、业务逻辑复杂到一定程度、数据量并发访问量增加到一定程度,单纯的从程序、前端中优化效果有限,需要多管齐下时,再来动数据库。即便到那时,最好也是在PD中统一设计这些功能,可以自由导入。否则,如果是多数据库支持的项目,每种数据库都得分别手动编写不同的代码,这样配置会很麻烦,也很难统一控制。而对于绝大部分中小型项目,这些麻烦本是没有必要出现的。&/p&&blockquote&C.遵守范式&/blockquote&&p&目前关系数据库有六种范式:&/p&&p&第一范式(1NF)属性不可分。是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项。&/p&&p&第二范式(2NF)在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)&/p&&p&第三范式(3NF)在1NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)&/p&&p&巴斯-科德范式(BCNF)Boyce-Codd Normal Form(巴斯-科德范式)在1NF基础上,任何非主属性不能对主键子集依赖(在3NF基础上消除对主码子集的依赖)&/p&&p&第四范式(4NF),要求把同一表内的多对多关系删除。&/p&&p&第五范式(5NF),又称完美范式,从最终结构重新建立原始结构。&/p&&p&这些范式仔细讲起来很耗文笔,关于范式的详细介绍可以参考文档末尾的参考文献。在设计数据库时,尽可能做到前四个即可。但范式并非硬性要求,而是设计时尽可能遵守的大原则。对于这些范式也没必要刻意去记,关系型数据库说到底只是二维的行列组合,参与的数据库设计多了,设计理念自然成型,会不自觉的去向这些范式中靠拢,设计过程中可以以此为依据校验自己的设计是否合理,及时修正不当设计。&/p&&p&特殊地方,为了操作方便,在不影响核心业务的情况下,允许小范围的不遵守范式。平时的多图片上传功能,可能只设计一个字段存储图片名称,这样字段值中就会包含多个图片的名称,里面用|或其它符号分隔,这样属性值就可分了,也就违反了第一范式——属性不可分;还有地区表中的,地区编码字段也隐含有等级信息;再就是菜单相关表,里面分等级互相引用等等;严格来讲这些都是不符合规范的。&/p&&p&在小范围内,不影响到核心业务流程,为了设计开发的方便,允许一些特殊的设计出现。规范是用来约束设计的,约束的目的是为了最终整体设计的合理性、为了最终项目的稳定性,如果这种全局的规范约束在局部影响到开发实现,可以适当调整,但前提是设计者要有足够的经验和能力驾驭住这种不当调整。&/p&&img src=&/v2-c7df58d71001_b.png& data-rawwidth=&1049& data-rawheight=&362& class=&origin_image zh-lightbox-thumb& width=&1049& data-original=&/v2-c7df58d71001_r.png&&&br&&blockquote&D.连接查询&/blockquote&&p&因为不允许使用视图,所以在表设计时要尽可能的考虑到后面的连接查询,目前所接触项目的数据量都尚在可控范围之内,一般的业务系统,允许三张表以内的连接查询,五张表以内的要限制,超过五张的禁止。禁止使用子查询,子查询转换成关联查询。&/p&&p&如果要尽可能避免多表连接查询,那在设计数据库时,有关联关系的地方,一般从表中除了有引用主表主键的外键字段外,还要有一个或多个字段存放主表中的关键信息,比如病人表中有所属医院所属科室主键的外键字段,但还可能会有所属医院名称所属科室名称的字段。因为嫌数据冗余、维护不易,之前自己一般不设计除外键外存放主表信息的其它字段,但这样查询时又会多添好些麻烦。可如果加上这些字段,不单单数据冗余、维护麻烦,也不好保证数据的准确协调统一性。比如如果医院的名称被修改了,那按常理病人表中的医院名称也得做相应的修改才可以,这样,如果医院表被许多表引用,那就得对所有的表执行修改动作,很是麻烦,实现起来也不现实。如果都加上引用约束,依赖数据库自己的关联自动更新,觉得也不是很好,影响程序执行速度且不易维护。可如果是在会诊单表(类似于订单表的功能)中出现类似的情况,就无须有这种顾虑,如果这个会诊单在产生时医院是这个名称,后面名称有了更改,那会诊单中的医院名称还是显示早前的即可,无须做相应更改,这也是符合逻辑的。&/p&&p&类似情况经常出现在字典相关信息的存取中,平时只在表中存字典编码,但查询时又往往要求同时提供字典文本,核心业务表中的字典字段往往比较多,暂时没有好的方法一次高效提取完整信息。后面在做类似设计时,核心业务表,字典字段比较多的,根据实际情况,考虑同时存入字典编码和字典文本,这样可避免部分连接查询。但同时,还是会出现上面提到的问题,一方面是数据冗余,一方面业务表中的字典文本有可能会和字典表中的文本不一致——如果字典信息有更改的话。&/p&&p&这里很难找到一种两全其美的办法,既能避免数据冗余,又能让程序在执行读和写动作时都方便高效,如何在各方面之间拿捏均衡是个人经验问题。通常情况下,是在数据的协调性、准确性允许,跨表查询又不容易(从表外键较多)的业务模块,采用在从表中附加额外字段的处理方式;在对数据显示的同步性、准确性有严格要求,跨表查询也相对容易(从表外键较少)的业务模块,采用从表中不设外键以外的附加字段、而使用关联查询的方式获取完整数据信息。&/p&&p&在普通的业务系统之外,还有一种情况,可能不得不大量使用函数、子查询、嵌套查询。和可视化部门合作过的一些项目,系统前端引入ECharts,大量的环形图、柱状图、折线图等用于展现数据统计分析结果。有些图形需要的数据很难用简单的SQL一次提取出来,可如果多次提取后再由程序组装处理,又太过麻烦,最后还得考验SQL,此种情况是允许子查询、嵌套查询、多表连接等复杂SQL出现的。&/p&&img src=&/v2-c879f2144b53eca2d28f_b.png& data-rawwidth=&467& data-rawheight=&301& class=&origin_image zh-lightbox-thumb& width=&467& data-original=&/v2-c879f2144b53eca2d28f_r.png&&&p&过去曾借助外部框架设计出一套异常灵活的架构,封装了查询对象,消除掉了SQL语句,应付通用的业务系统足够,但在做类似这种统计分析功能时却变得各种不自在,到后来还是觉得直接写SQL更方便。此处只能在设计的架构上放开一个口,让开发人员可以自由编写SQL语句提取数据,最终的查询结果统一封装成一个List&Map&String, Object&&,然后交由程序自动序列化成JSON格式(包含多个对象的数组)返回给前端。&/p&&blockquote&E.主键外键&/blockquote&&p&每张表必须有唯一主键,此主键只用来标识记录唯一,没有其它任何功能,业务表中其它任何的惟一字段均不能做主键,比如用户表中的身份证号、地区表中的编码,虽然也是唯一,但均不可做主键。主键为UUID,统一命名为id,统一为char(32)类型。相较于自增主键类型,使用UUID会导致查询速度稍慢,可用自增主键的话,如果同时支持多数据库就比较麻烦了,比如Oracle实现自增功能必须用序列辅助处理,还有,备份合并不同数据库中相同表数据也会出现冲突,设计分布式架构、横向拆分表也会出现类似问题等等。关于主键,还有一点要强调,原则上讲,多对多关系中间表外的任何其它表不允许出现复合主键。&/p&&p&外键,多和主键对应,多是引用另外一个表的主键,那么这个外键及所有表的主键的类型也应该是一样的,都规定成char(32)类型。再就是外键的命名也要统一,这个也可以通过PD由CDM转换成PDM时统一处理,使用“主表名_主键名”的命名方式,比如user_id,如果一个从表中有多个字段同时引用主表的同一个字段,那再用其它标识,比如在会诊单申请表中会诊发起医院(sender_hopital_id)和会诊接收医院(receiver_hospital_id)。&/p&&p&主键和外键是数据库设计中非常关键的两个地方,在后面的约束控制和命名规范中还有更详细的介绍。&/p&&p&三种数据库关系中,多对多关系是最麻烦的,中间表往往使用复合主键,在程序架构上对这部分业务的处理不太容易,设计程序架构时要特别注意。&/p&&blockquote&F.逻辑删除&/blockquote&&p&关于针对数据库的删除动作,仔细考虑后还是决定,现阶段暂时仍旧使用物理删除。严格意义上来讲,所有的软件项目都不应该有物理删除动作,目的很明确,就是为了数据的安全性完整性。如果使用逻辑删除,给每张数据库表加上一个is_active字段,数据的安全性完整性虽然可以得到保障,但却跟程序编写带来许多麻烦。比如所有的查询方法都需要额外加上is_active=’0’的判断条件,在涉及多表连接查询时就很不方便。再有,执行假删除动作时的级联处理也会相对麻烦。还有,如果数据表的某个字段要求唯一,并强制约束,比如用户表中的登录用户名字段,设计为逻辑删除的话,一旦有新的同用户名记录就无法插入。但如果不将该字段设置为唯一性约束的,那么在每次插入数据的时候,都需先进行一次查询,看看有无未(逻辑)删除的同名记录存在,低效率是一回事,而且在高并发的系统中,很难保证其正确性。&/p&&p&此外,使用逻辑删除,随着项目应用时间的增加,将会有大量无效数据产生,如何处理?如果数据量大到影响查询速度,程序如何做优化?像日志表,很容易就到上百万。如果定期将逻辑删除的数据转移到另外的表中,觉得处理起来也不简单,还可能会有附加问题出现。如果使用脚本定期彻底删除逻辑删除的数据,如何叠加备份这些删除的数据?&/p&&p&综合考虑,对于现阶段的项目,逻辑删除所带来的好处有限,但带来的问题却很多。如果平时做好数据备份工作,还是可以预防物理删除隐患的。但心里应该清除,当项目大到一定程度,对数据安全性的要求高到一定程度,使用逻辑删除代替物理删除是必然的,在后面的数据库设计中,可以先小范围的尝试使用逻辑删除,一旦开发模式成熟,就全面使用逻辑删除代替物理删除。&/p&&blockquote&G.性能安全&/blockquote&&p&对于中小型项目,数据库设计过程中过多的考虑性能问题往往弊大于利,程序部分的设计开发也是如此。不是不考虑,而是不要过多考虑。性能及安全问题太大,这些问题的重要性都是随着项目规模增长的,在中小型项目中过分的考虑只会给开发带来不必要的麻烦。中小型团队,时间人力成本有限,要用在关键环节上,用最短的时间做出成型的项目是最重要的。至于设计开发,尽可能保证好其协调统一灵活稳定性,一旦出现问题或需求调整,可以随时灵活的更改就好。&/p&&p&&b&4、字段设置&/b&&/p&&blockquote&A.字段类型介绍&/blockquote&&p&不同的关系型数据库在字段类型的具体化上差异较多,这里无法一一详述,但具体化的字段类型再多,无外乎几种:字符、数字、日期、二进制。&/p&&img src=&/v2-a602ed2d76a66f7d6a6c_b.png& data-rawwidth=&860& data-rawheight=&753& class=&origin_image zh-lightbox-thumb& width=&860& data-original=&/v2-a602ed2d76a66f7d6a6c_r.png&&&p&下面有三张截图,前两张是一张典型的用户表结构,上面的是PD中的PDM,下面的是导入到MySQL的具体表结构,第三张截图是SQLServer数据库中的一张用户表的结构图:&/p&&img src=&/v2-ca6dc174c4efd_b.png& data-rawwidth=&1920& data-rawheight=&1020& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&/v2-ca6dc174c4efd_r.png&&&br&&img src=&/v2-ae903cdae25e_b.png& data-rawwidth=&1920& data-rawheight=&1020& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&/v2-ae903cdae25e_r.png&&&br&&img src=&/v2-87bb5f9b6dfaa96fb044_b.png& data-rawwidth=&1920& data-rawheight=&1020& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&/v2-87bb5f9b6dfaa96fb044_r.png&&&p&个数据库的某个具体类型,这里无法详述其适用情况,本模块只制定通用的字段设置规范。&/p&&blockquote&B.不为空默认值&/blockquote&&p&表中应该尽可能避免可为NULL的列,且尽可能显示设置默认值,尤其是被索引的列。&/p&&p&在MySQL数据库中,空值是不占用空间的,而NULL其实是占用空间的。再者,MySQL表的列中包含NULL的话,该列就不会包含在索引中,也就是说使用索引是无效的,现在不确定其它数据库是否也是如此。 所以考虑今后可能会使用索引的字段,就要设置字段属性是NOT NULL。比如,如果某个字段后面可能会作为查询关键字使用LIKE的形式进行搜索,就要将该字段定义成索引以提高查询速度,那这个字段属性就是NOT NULL的。&/p&&p&除以下数据类型的字段外:timestamp、image、datetime、smalldatetime、uniqueidentifier、 binary、sql_variant、binary 、varbinary,表字段应尽可能显示设置默认值。建议数值型的默认值为数值0,布尔型的默认值为数值1(通常情况下,系统中所有逻辑型中数值0表示为“真”、“正常的”;数值1表示为“假”、“异常的”,这种编码后面还会有介绍),datetime、smalldatetime类型的字段没有默认值,必须为NULL。&/p&&p&如果数据库中某个字段有默认值,那么觉得在程序开发过程中,对应实体类的属性应该设置同样的初始化值才合理,记得动软代码生成工具中的框架就是这样设置。之前自己的程序设计中没有注意到这点,自动生成的所有实体类的属性都没有默认值。&/p&&p&注意区分NULL和空字符串是不同的,数值型字段中NULL和0更是两码事。如果在数据库设计过程中不允许出现NULL字段还好,但如果有允许NULL而没有设置默认值的字符型字段,程序对记录执行了写空字符串动作和压根未执行写动作是两码事;如果有允许NULL且设置默认值为空字符串的字段,则无法做这种区分。当然,通常情况下,我们认为文本框中空字符串的提交动作等同于未执行写入。有些类似的情况是,在程序开发中,一个空的List对象,或者一个new出来的空对象,和NULL也是不同的,要注意。&/p&&p&像订单(会诊单)这种表,取消、退回、安排这些字段的信息都不是必须有的,可以分流到子表中存储,放在一张表中会导致出现很多可为NULL或空值的列。之前并不赞成这种过分分流的方法,因为这会另信息的维护变得麻烦,如再有类似情况,应该根据实际综合判断取舍。也要在设计时尽可能遵守第二、三范式,非主属性完全依赖于码(主键)、消除传递依赖,不要让某张表过分臃肿。&/p&&blockquote&C.类型长度设置&/blockquote&&p&当字段定义为字符串类型时建议使用varchar而不用nvarchar以节省空间,通常情况下,都要用尽量少的存储空间来存储一个字段的数据,能用int类型的就不用char类型,能用char类型就不用varchar类型,能用varchar(20)的就不用varchar(25) 。char和varchar长度设计需要根据业务实际需要进行长度控制,禁止预留过长空间。比如主键要求用UUID,那就统一为char(32),可以固定的部分就都固定下来。varchar类型虽然根据实际长度进行存储,但内存分配则是根据指定长度,不合理的长度设计会导致内存的不合理占用。&/p&&p&varchar是变长存储,字段长度是数据库一种约束,定义合理的长度也可以让人容易理解字段的用途。MySQL中定义的长度如果小于255,字段长度用1个字节表示,如果超过255,字段的长度将固定用2个字节表示。Oracle没有这样的问题。字段定义的长度对索引也有较大影响,MySQL数据库索引存储的长度都是定义的长度,不是实际字符的长度,这是一个非常大的问题,估计主要原因是为了实现简单,所以MySQL在索引上会浪费大量的空间保存字符串。&/p&&p&前台、程序以及数据库各部分之间对字段大小的限制务必处理恰当,为了节省存储空间,选取的数据库字段容量在一定范围内应该尽可能小,而为了对程序提供更好的扩展支持,又应该尽可能的设置大些,具体字段类型、字段长度如何设置,根据实际情况取得均衡。而后台程序部分,对数值大小长度应该做好校验处理,确保插入数据库的值大小长度不要超过限制。同时前端也应该给出明确的校验提醒,让用户按提示输入,决不允许不提醒用户而擅自把数据处理后插入数据库中(这种错误真有人犯过)。这样,前端、程序、数据库全方位校验处理,自然可以保证数值的准备性、存取的合理性。&/p&&p&除非要保存文章内容, text字段尽量少用,如果要用能拆到冗余表中最好。禁止使用blob类型保存大文本、附件、图片等,对于图片、文档等附件数据库中只保留原始文件名和存储路径。网上也有建议使用其他存储方式的,比如TFS、SFS等,可以参考。&/p&&p&禁止使用float、double类型,建议使用decimal替代。decimal(a,b) ,a指定指定小数点左边和右边可以存储的十进制数字的最大个数,最大精度38。 b指定小数点右边可以存储的十进制数字的最大个数。小数位数必须是从 0 到 a之间的值。默认小数位数是 0。比如decimal(5,2)规定了存储的值将不会超过5位数字,并且小数点后面有2位数字。&/p&&blockquote&D.Oracle的CLOB&/blockquote&&p&在Oracle中,CHAR 为定长字符串,最长2000字节 。VARCHAR2 为变长字符串,最长4000字节。NCHAR和NVARCHAR2分别与CHAR和VARCHAR2相对应,但存储的数据为 NLS字符 。&/p&&p&目前VARCHAR是VARCHAR2的同义词。工业标准的VARCHAR类型可以存储空字符串,但是Oracle不这样做,尽管它保留以后这样做的权利。Oracle自己开发了一个数据类型VARCHAR2,这个类型不是一个标准的VARCHAR,它将数据库中VARCHAR类型的列可以存储空字符串的特性改为存储NULL值。如果你想有向后兼容的能力,Oracle建议使用VARCHAR2而不是VARCHAR。&/p&&p&在Oracle中没有TEXT类型,但有用于大文本存储的CLOB类型。Clob是指大字符对象,也就是英文Character Large Object的缩写;Blob是指二进制大对象,也就是英文Binary Large Object的缩写;由此可见这两个类型都是用来存储大量数据而设计的。&/p&&p&LONG 最大存储2G字符数据,但现在已不推荐使用(改用CLOB);CLOB 在Oracle 9i 及以前,最大可存储4G字符数据 ,在Oracle10g及以后,最大可存储4G*数据库块大小的字符数据;NCLOB 基本同CLOB,就是存储的数据为NLS。&/p&&p&在Oracle数据库表中使用CLOB类型字段,最大的问题是备份数据时不好处理。在有些情况下,给政府、企业做项目,只给你Oracle的访问权限,而不给你Oracle所在服务器的操作权限,也就是说自己无法操作Oracle服务端工具。但Oracle的客户端中又没有exp、expdp命令,这样备份导出数据库就不好弄了(此处不提沟通协调甲方处理)。SQL Developer是Oracle的官方工具,用其导出数据库,如果导出的是SQL格式,那CLOB类型字段的数据将直接被忽略——这绝对是无法接受的。官网上有文章说可以将数据库导出为loader或pdf格式,自己尝试导出这两种格式,发现不能导出成单个文件,会导出很多的文件。而且导入时也需要用到额外的工具——Oracle服务端的sqlldr.exe,这样只借助客户端也是不行的。&/p&&p&在SQL Developer“工具”菜单下,还有两个选项:“数据库Diff”及“数据库复制”,如果所处网络可同时访问源数据库和目标数据库,可用这种方法互相拷贝数据,但是同样的问题,这种数据库复制方法,仍然是不能处理COLB、BLOB的字段。而且我发现,凡是带有这两种字段的表,在复制时都没有数据,不是相应字段没有数据、是整个表的数据都没有复制,其它没有BLOB、CLOB字段的表,数据拷贝都正常。&/p&&p&也曾想使用的PL/SQL Developer工具进行备份,导出了PL/SQL Developer自己的格式(pde)。可是却提示stream read error,到网上一查,原来PL/SQL Developer自己的格式也是不支持COLB 、BLOB类型字段的导出的。&/p&&p&之前同事介绍过Navicat for Oralce工具,但其在导出CLOB、BLOB类型的字段时,如果字段中的数据过长,也是无法再正常导入的。这个小工具看似简单轻巧,在执行一些操作时问题却很多,不宜作为一款常用的Oracle管理工具。&/p&&p&这样看来,只有expdp命令才能有效导出clob、blob格式的字段了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&cd D:\app\bsc\product\12.1.0\dbhome_1\BIN\
exp username/password@tnsname file=C:\Users\Administrator\Desktop\.dmp owner=username
imp username/password @tnsname file=C:\Users\Administrator\Desktop\tnsname.dmp ignore=y full=y statistics=none
&/code&&/pre&&/div&&p&不过,如果你虽然没有源数据库服务端的访问权限,却有目标数据库服务端的访问权限,且两个库可在一个网络中访问,也是有办法用EXP命令备份源数据库的。就是让目标数据库服务端的TNS监听源数据库的实例,再利用目标数据库Oracle服务端的exp.exe工具远程导出源数据库,导出导入命令和上面类似:&/p&&div class=&highlight&&&pre&&code class=&language-text&&cd D:\app\bsc\product\12.1.0\dbhome_1\BIN\
exp origin_username/origin_password@origin_tnsname file=C:\Users\Administrator\Desktop\.dmp owner=origin_username
imp target_username/target_password@target_tnsname file=C:\Users\Administrator\Desktop\.dmp ignore=y full=y statistics=none
&/code&&/pre&&/div&&p&此外,用PL/SQL Developer工具备份数据时,Export User Objects菜单命令导出的是SQL文件,在这里你可以将建表、序列、触发器、存储过程等的SQL语句全部导出成一个文件,但是这里面并不包括数据。要想导出数据,必须用Export Tables……菜单命令,导出DMP文件。当然也可以导出其它格式的文件(SQL、PDE),但建议用DMP格式,因为前面已经说过,如果表中有CLOB类型字段的话,用其它格式的导出方式恐怕有问题。&/p&&p&如果一个表不存在,而这个表中没有CLOB、BLOB这种特殊数据类型的字段,用DMP导入数据时PL/SQL会自动建立这个表。但如果一个表不存在,而这个表中又有CLOB、BLOB这种特殊字段,直接导入DMP格式的文件会报错 IMP-00003: 遇到 ORACLE 错误 959。所以在Oracle中导入数据库时应该先执行用Export User Objects导出的SQL文件,这样相关的序列、触发器、表结构都已经建好了,再导入用Export Tables…… 导出的DMP文件,也就是导入其中的数据,就万全了。&/p&&blockquote&E.数值类型选择&/blockquote&&p&float:浮点型,含字节数为4,32bit,数值范围为-3.4E38~3.4E38(7个有效位)&/p&&p&double:双精度实型,含字节数为8,64bit数值范围-1.7E308~1.7E308(15个有效位)&/p&&p&decimal:数字型,128bit,不存在精度损失,常用于银行帐目计算。(28个有效位)&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span class=&c1&&//结果显示为345.9876,只显示7个有效位,对最后一位数四舍五入。&/span&
&span class=&kt&&float&/span& &span class=&n&&f&/span& &span class=&o&&=&/span& &span class=&mf&&345.98756f&/span&&span class=&o&&;&/span&
&span class=&c1&&//结果显示为345.,只显示15个有效位,对最后一位四舍五入。&/span&
&span class=&kt&&double&/span& &span class=&n&&d&/span&&span class=&o&&=&/span&&span class=&mf&&345.442d&/span&&span class=&o&&;&/span&
&span class=&c1&&//可以支持28位,对最后一位四舍五入。&/span&
&span class=&n&&decimal&/span& &span class=&n&&dd&/span&&span class=&o&&=&/span&&span class=&mf&&345.&/span&&span class=&err&&……&/span&
&/code&&/pre&&/div&&p&float和double的相乘操作,数字溢出不会报错,会有精度的损失。当对decimal类型进行操作时,数值会因溢出而且报错。&/p&&p&Oracle中的数值类型,Oracle只是在语法上支持decimal类型,但是在底层实际上它就是NUMBER类型,支持decimal类型是为了能把数据从Oracle数据库移到其他数据库中(如MySQL、DB2等)。Oracle的NUMBER数据类型的精度:NUMBER(P,S) ,P: 1---38, S:-84---127。S代表的是小数位数,P代表的是总位数(整数位数和小数位数)。所以,平时如果在Oracle中用自增主键,长度设为NUMBER(10)的话,相当于NUMBER(10,0),表示最高可记录到十亿级的数据量。&/p&&p&下图是MySQL中的整数型数值类型详述:&/p&&img src=&/v2-4f2f01dd5ab69d3df565dc5_b.png& data-rawwidth=&917& data-rawheight=&363& class=&origin_image zh-lightbox-thumb& width=&917& data-original=&/v2-4f2f01dd5ab69d3df565dc5_r.png&&&blockquote&F.通用字段处理&/blockquote&&p&日期时间类型字段,网上有建议,采用int来记录unix_timestamp,自己还是习惯用datetime。不过设计原则是粒度越小越好,所以这里要求日期时间类型的字段,尽可能精确到时分秒,用datetime类型。即便是像生日(birth_date)这种字段,一般只存储到年月日,但在选择字段类型时建议还是用datetime而非date,以防万一。如有部分时间字段着实无须记录到时分秒,则用date类型。严禁使用varchar等字符串类型记录日期时间,更不要把时间猜分,年在单独的字段、月在单独的字段、日又是单独字段,老实讲TM想不明白这种人的设计思路是什么样的。&/p&&p&网络IP字段,网上有建议,除特殊情况一律用bigint来记录inet_aton值,但这种存储方式貌似只在MySQL中适用,这里要求还是用varchar存储。关于inet_aton想了解的话可以看下参考文献中的“MySQL的IP处理函数inet_aton()和inet_ntoa() ”。&/p&&p&字典编码字段,之前在SQLServer中设计数据库时统一使用char(2)类型,Oracle数据库中统一使用number(2),在MySQL中统一使用tinyint(2)。现在想来最合理的还是设置为tinyint(2),以后数据库字典编码字段统一按此设置。就是Oracle中没有tinyint类型,不知道如果在PD中设置此种类型,导入到Oracle时会自动转换处理还是直接报错。&/p&&p&备注字段,尽可能在所有表中都保留这个字段,也是给前端信息录入预留一个可扩展部分。统一命名为remark,字段类型为varchar(200),最多100个中文字符。再多的话说明有额外信息,就不适合放在备注字段中了,要再加新字段存储。&/p&&p&排序字段,不是每个表中都需要额外的排序字段,但有些表这必须有,比如记录菜单信息的表、门户网站中存放文章内容的表等。这里推荐统一使用int(10)做为所有表中的排序字段类型。&/p&&p&字段设置部分撰述内容较多,相对详细,这是比较重要的一部分。以后的数据库设计,字段类型选择、字段长度设置部分都要以此为依据。&/p&&p&&b&5、相关注释&/b&&/p&&p&数据库是整个项目的地基,后面程序架构设计及具体开发工作都要在此基础上进行,如果没有注释或者注释混乱,会给开发者带来很多困扰,无形中影响项目进度。&/p&&p&在相对成熟的技术团队中,所用开发模式、架构、框架也都已经成型,如果接手的项目类型不超出平时常规的范畴,以往的开发模式、架构及所用框架等都是无须做大的变动即可拿来复用的。这样,在数据库设计过程中或完成之后,程序的具体设计开始,再到进入正式开发过程中,需要频繁组织开发人员参与的会议。会议讨论的都是具体的开发工作,数据库文档是参考依据、必须的会议资料。&/p&&p&数据库文档一般根据PD中的数据库文档模板生成,PD自带的数据库文档模板太杂乱,所以最好单独设计一套适合自己团队使用的模板。比如平时在数据库设计过程中不用存触过程、触发器、序列、函数等高级功能,那我设计的模板自然不包括展示这些信息,只显示表结构、约束、表注释、字段注释等信息,生成的文档也会简单明了。如果不想单独设计,网上也有一些他人整理的模板,或可根据实际情况拿来使用。 &/p&&p&团队内部使用的数据库文档一般是HTML格式,查看比较方便,而对外提供的,向PMO或客户提交留存的,一般是RTF或DOC格式的。&/p&&p&用PD完成设计之后,将PDM导入到数据库,同时生成数据库文档,后期所有针对数据库的讨论都会以这份数据库文档和PDM图为依据。后面数据库表结构如有变化,文档会同步更新。而这份文档中的文字说明内容,就来自于数据库设计过程中的相关注释。再有,现在的程序设计好之后,具体代码大都是由工具生成,程序中的实体类、方法说明等等最终也都是由数据库中的相关注释演化而成。所以注释说明的语气,甚至标点符号都要注意,只有数据库注释规范了,数据库文档、程序注释、程序文档、对外接口文档才能规范,因为这些的起始点都是在数据库中的注释上面。&/p&&p&数据库中的关键注释其实就两部分,一部分是表注释,一部分是表字段注释。表注释对应程序中实体类的注释,字段对应程序中实体类属性的注释。&/p&&blockquote&A.表注释&/blockquote&&p&对于表的注释,要求简单明了,先说明表中文名称,句号分隔,然后跟功能说明,无需特别说明的,则只说明中文名称即可。同时,为了最终生成代码注释的可读性,不允许在说明中出现“表”字眼。比如订单表,直接注释“订单”即可,如核心业务表需要其它特殊详细的功能说明,要考虑到最终生成程序实体类注释的可读性。再有,如果数据库由多人协作设计,那表注释中必须标明创建者和创建时间信息,示例如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&创建者:孟宪志。创建时间: 13:30。说明:系统日志。用于记录用户对系统的所有操作。
&/code&&/pre&&/div&&p&数据库设计完成进入开发阶段,后续扩展新增的表也都要按如上格式注释,同时说明是后续新增表,及新增原因等。&/p&&blockquote&B.主键字段&/blockquote&&p&主键字段,无须特别注释,注释就两个字:主键。&/p&&blockquote&C.外键字段&/blockquote&&p&外键字段,注释先说明字段中文名称,句号分隔,后面要特别说明是外键,然后说明引用哪个表的哪个字段,再句号分隔,如还有其它说明,则继续跟后面。比如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&` applicant_doctor_id` char(32) DEFAULT NULL COMMENT '申请医生。外键,引用医生表(doctor)的主键(id)。理论上可填多个,以“|”分隔'。
&/code&&/pre&&/div&&p&前面说过为了避免多表连接查询,有关联关系的地方,一般从表中除了有引用主表主键的外键字段外,还要有一个或多个扩展字段存放主表中的关键信息,这个扩展字段通常都是和对应外键字段并列存放,注释时先说明字段中文名称,句号分隔,后面要特别说明是名称(或其它),然后说明和哪个表的哪个字段对应,再句号分隔,如还有其它说明,则继续跟后面。格式如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&` applicant_doctor_name` char(32) DEFAULT NULL COMMENT '申请医生。名称,和医生表(doctor)的医生名称字段(name)对应。理论上可填多个,以“|”分隔'。
&/code&&/pre&&/div&&blockquote&D.字典字段&/blockquote&&p&字典字段在数据库表中出现非常频繁,要求所有表中的字典字段注释也要统一格式。注释先说明字段中文名称,句号分隔,后面要特别说明是编码,对应哪个字典表的哪个字段,再句号分隔,后面跟详细说明,如果字典项不多的话,要求先枚举说明每种字典编码代表的不同意义,句号分隔,如还有其它说明,则继续跟后面。比如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&`status_code` varchar(5) DEFAULT NULL COMMENT '转诊单状态。编码,对应数据字典表(dictionary)中的编码字段(code)。目前先定义6个状态:01 已提交,02 已取消,03 已审请,04已安排,05 已拒绝,06 已结束。03是向HIS系统执行审请,04是HIS系统已安排入院日期,下转诊时这两个字段没有区别。'
&/code&&/pre&&/div&&p&如果有字典文本扩展字段(类似于外键的扩展信息字段),格式与上类似:&/p&&div class=&highlight&&&pre&&code class=&language-text&&`status_text` varchar(50) DEFAULT NULL COMMENT '转诊单状态。文本,对应数据字典表(dictionary)中的字典项名称字段(name)。目前先定义6个状态:01 已提交,02 已取消,03 已审请,04已安排,05 已拒绝,06 已结束。03是向HIS系统执行审请,04是HIS系统已安排入院日期,下转诊时这两个字段没有区别。'
&/code&&/pre&&/div&&p&字典表细分的话也有好多种类型,后面特别说明中还会细讲。除了数据库中的通用字典表,还有一些常见表,比如地区表、ICD字典等,也是一种字典表,其共性是:表结构设计完成后、程序开发前即完成数据填充,且后期很少改动;主键外至少要有编码和文本两个非空字段,如果字典表中有细分子项的话,还会有字典项所属类型字段。&/p&&blockquote&E.普通字段&/blockquote&&p&除以上三种常见类型的字段外,其它字段,都可用统一的方式注释。先说明字段中文名称,句号分隔,后面跟详细说明。比如:&/p&&div class=&highlight&&&pre&&code class=&language-text&&`bed_id` varchar(50) DEFAULT NULL COMMENT '入院床位号。HIS系统安排并反馈的入院床位号,如果是下转诊,此部分由医生手动填写'。
&/code&&/pre&&/div&&p&还要特别说明的是,如果项目进入开发阶段后需要对现有表新增某些字段,或对现有表的某些字段进行修改,和新增数据库表一样,也必须特别说明。&/p&&div class=&highlight&&&pre&&code class=&language-text&&创建者:孟宪志。创建时间: 15:38 。说明:会员职务。交易平台新增字段,个人用户此字段为空。
&/code&&/pre&&/div&&p&相关注释部分,先说明了数据库中注释对项目开发的重要性,然后分五个子模块:表注释、主键字段、外键字段、字典字段、普通字段,详细介绍。如今后所有的数据库设计都能严格按此规范要求、按给出的格式添加注释,则相关数据库文档、程序文档、接口文档都可随之走向正规。&/p&&p&&b&6、约束控制&/b&&/p&&p&建立约束的目的是为了防止数据库中出现不符合语义规定、不符合业务逻辑的数据,为了确保数据的完整性、合法性。如果数据具有完整性,则表示数据有效——正确并且准确、数据库的关系结构是完整的。完整性约束可以加强数据库的关系结构,这些规则使数据在各个表之间保持一致。&/p&&p&总的来说有五种约束类型:唯一性和主键约束、外键约束、检查约束、空值约束、默认值约束,对应五大关键词,UNIQUE和Primary Key, Foreign Key, CHECK, NOT NULL, DEFAULT。这五种约束类型,除检查约束外,其余的在前面的章节中都有过介绍,比如主键、外键字段类型的选择,字段长度的设计,注释的规范等等,在后面的命名规范中也还会提到。&/p&&p&检查约束(CHECK),用于检查列的类型和范围,语法:CONSTRAINT [constraint_name] CHECK (condition); 比如:check(Age &2)。过去没有在数据库中添加检查约束的习惯,在本规范中,也暂不做此要求,这会增加数据库设计过程中顾虑点,还是交由程序和前端来控制这种细节上的数据合法性。&/p&&p&本规范中要求,除检查约束外,其余四种都要在数据库中加强约束,同时,程序也会根据数据库中约束的设置做相应处理。&/p&&p&主键不用说,一般情况下都会加约束的。要注意的是唯一字段,比如用户表中的电话、邮箱、登录账号等等明显唯一的信息,都要加强约束,还有多对多关系中间表里的复合主键,也要加强约束。再就是,前面也提到过,本规范中所有主键字段统一使用UUID,char(32)类型,具体命名方式后面也会讲。&/p&&p&外键部分,类型自然要和主键一样统一为char(32),除了外键字段,为了保存主表其它信息的扩展字段类型也要和主表中的相应字段类型统一。过去参与的项目中有的在数据库中加了外键的强约束,有的则没有,有的是在用PD设计时加上了主从引用关系,但在导入到库中时去掉了外键的强约束,而只保留表结构。&/p&&p&表面看,数据库中加上外键、非空、唯一等强约束会给程序编写带来麻烦,实则不然。约束是为了保证数据的合理性,如果数据库设计的约束本身没问题,那程序编写中因约束而照成的不便就多是程序本身的不合理造成的。万千世界,所有约束规范都是为了确保被约束规范的对象将所参与的事情做的更好,这种最终的好不是只针对某个对象的,而是针对所有参与其中的。假如某种规范不是这样,那它本身就是不合理的;假如它是这样的,而被约束规范的对象觉得在这种约束下做事情不便,那就是他自己做事方法的问题了,要由他自己根据规范修正。&/p&&p&所以强约束带来的不便只幻象,这种不便不但不是坏事,还会倒逼程序开发更加趋于合理,修正开发中的错误。而不加强约束,程序可以肆无忌惮的对数据库进行任何操作时,如果架构设计师及开发人员的经验、技能较强还好,否则的话,程序很可能会因有意无意读写错误信息,造成针对数据库的无效或错误操作,破坏掉数据的完整性、合理性。&/p&&p&有些情况下,加不加强约束设计者是决定不了的,比如公司有通用的权限管理系统对所有子项目统一管理,那在设计子项目的数据库时就不能添加和用户相关的外键约束了。还有其它类似的情况:两个项目之间有逻辑交差,但数据库相互隔离,只能通过互相调用接口处理业务。这种情况下,如果想保证好数据的完整性准确性,只能由程序在业务逻辑层控制,必须设计开发足够精密才能做好。&/p&&p&空值约束和默认值约束不再多说,在前面字段设置中的NOT NULL子模块讲的很详细了,只要记得设置字段为NOT NULL、并给字段设置默认值可以提高查询速度、节省表空间就可以了。&/p&&p&&b&7、索引添加&/b&&/p&&p&索引是一个单独的、物理的数据库结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。数据库中索引的概念与书索引的概念非常类似,不同之处在于数据库索引用来在表中查找特定的行。&/p&&p&在各种类型的数据库中,索引又可细分为好多类型,比如之前系统整理过Oracle中的索引分类:非唯一索引(最常用)、唯一索引、位图索引、局部有前缀分区索引、局部无前缀分区索引、全局有前缀分区索引、 散列分区索引、基于函数的索引。Oracle存储索引的数据结构是B*树,位图索引也是如此,只不过是叶子节点不同B*数索引。&/p&&p&并未做过严格对比,不清楚添加索引后对查询速度的提升到底有多少,对插入删除修改的速度影响又有多少。就过去的经验,对于Oracle来讲,百万级数据量一般不会出现明显性能问题的,无需做特别的优化;MySQL的话,超过五十万条数据就有点卡了,超过一百万就得做优化了。对于诸多中小型的企业内部应用系统,如果数据量未达到这个级别而出现查询性能问题的,多是因为程序,开发模式、架构、框架、编码细节或SQL语句中肯定有特别不合理的地方。所以对于当前阶段的项目,在出现查询性能问题之前,不建议在数据库设计过程中手动添加索引,而且大多数数据库是会自动给主键和有唯一约束的字段添加索引的。&/p&&p&当数据量达到一定程度,仅通过程序优化对性能提升有限时,可以考虑同时对数据库进行调校,这时会优先考虑建立索引。&/p&&p&在前面的字段设置中的NOT NULL子模块提到过NULL值对索引的影响,下面再列出一些索引使用注意事项,大都是从网络上整理出、自己认为比较合理的,如果要建立索引,尽可能遵循这些规范(第一条没大看明白):&/p&&p&a. 逻辑主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非成组索引,对任何外键列采用非成组索引。考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用作读写。&/p&&p&b. 大多数数据库都索引自动创建的主键字段,但是可别忘了索引外键,它们也是经常使用的键,比如运行查询显示主表和所有关联表的某条记录就用得上。在经常进行连接,但是没有指定为外键的列上也要建立索引,而不经常连接的字段则由优化器自动生成索引。&/p&&p&c. 在频繁进行排序或分组(即进行 GROUP BY 或 ORDER BY 操作)的列上建立索引。&/p&&p&d. 不要索引blob/text 等字段,不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间。&/p&&p&e. 不要索引常用的小型表,不要为小型数据表设置任何键,假如它们经常有插入和删除操作就更别这样做了,因为对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。&/p&&p&f. 在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引,因为建立索引不但不会提高查询效率,反而会严重降低更新速度。&/p&&p&g. 如果待排序的列有多个,可以在这些列上建立复合索引(Compound Index)。&/p&&p&h. 索引不是越多越好,添加或删除表记录时,必须花费额外的时间来更新该行的索引。读的方便,写的就会有麻烦,反之,亦然。&/p&&p&&b&8、特别说明&/b&&/p&&blockquote&A.数据字典表&/blockquote&&p&通用字典表的表结构如下:&/p&&div class=&highlight&&&pre&&code class=&language-sql&&&span class=&cm&&/*==============================================================*/&/span&
&span class=&cm&&/* Table: Dictionary */&/span&
&span class=&cm&&/*==============================================================*/&/span&
&span class=&k&&CREATE&/span& &span class=&k&&TABLE&/span& &span class=&p&&[&/span&&span class=&n&&dbo&/span&&span class=&p&&].[&/span&&span class=&k&&Dictionary&/span&&span class=&p&&](&/span&
&span class=&p&&[&/span&&span class=&n&&dictId&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&50&/span&&span class=&p&&)&/span& &span class=&k&&NOT&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&dictTypeId&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&50&/span&&span class=&p&&)&/span& &span class=&k&&NOT&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&code&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&50&/span&&span class=&p&&)&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&name&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&200&/span&&span class=&p&&)&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&remark&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&1024&/span&&span class=&p&&)&/span& &span class=&k&&NULL&/span&
&span class=&p&&)&/span&
&span class=&cm&&/*==============================================================*/&/span&
&span class=&cm&&/* Table: DictionaryType */&/span&
&span class=&cm&&/*==============================================================*/&/span&
&span class=&k&&CREATE&/span& &span class=&k&&TABLE&/span& &span class=&p&&[&/span&&span class=&n&&dbo&/span&&span class=&p&&].[&/span&&span class=&n&&DictionaryType&/span&&span class=&p&&](&/span&
&span class=&p&&[&/span&&span class=&n&&dictTypeId&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&50&/span&&span class=&p&&)&/span& &span class=&k&&NOT&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&name&/span&&span class=&p&&]&/span& &span class=&p&&[&/span&&span class=&nb&&varchar&/span&&span class=&p&&](&/span&&span class=&mi&&50&/span&&span class=&p&&)&/span& &span class=&k&&NULL&/span&&span class=&p&&,&/span&
&span class=&p&&[&/span&&span class=&n&&remark&/span&&span class=&p&&]&/span& &span class=&}

我要回帖

更多关于 sql server 数据类型 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信