??xml version="1.0" encoding="utf-8" standalone="yes"?>
好,但条件约束只能对资料q行单的栏位核,当涉及到多表操作{复杂操
作时Q就要用到触发器?
一个数据库pȝ中有两个虚拟表用于存储在表中记录改动的信息,分别
是:
虚拟表Inserted 虚拟表Deleted
在表记录新增时 ?存放新增的记录 ?不存储记?br /> 修改时 ?存放用来更新的新记录 存放更新前的记录
删除时 ?不存储记录 ?存放被删除的记录
触发器的U类及触发时?br /> After触发器:触发时机在资料已变动完成后,它将对变动资料进行必要的
善后与处理,若发现有错误Q则用事务回滚(Rollback Transaction)
此ơ操作所更动的资料全部回复?br /> Istead of 触发器:触发时机在资料变动前发生Q且资料如何变动取决于触发器
现在介绍一下创发器的编写格式:
Aftercd:
Create Trigger 触发器名U?br /> on 表名
after 操作(insert,update)
as
Sql语句
Insteadcd
Create Trigger 触发器名U?br /> on 表名
Instead of 操作(update,delete)
as
Sql语句
实例1:
在订?表orders)中的订购数量(列名为num)有变动时Q触发器会先到客?表Customer)?br />取得该用L(fng)信用{(列名为Level),然后再到信用额度(Creit)中取{
许可的订购数量上下限Q最后比较订单中的订购数量是否符合限制?/p>
代码Q ?
Create Trigger num_check
on orders
after insert,update
as
if update(num)
begin
if exists(select a.* from orders a join customer b on a.customerid=b.customerid
join creit c on b.level=c.level
where a.num between c.up and c.down)
begin
rollback transaction
exec master..xp_sendmail 'administrator','客户的订购数量不W合限制'
end
end
实例2:
有工资管理系l中Q当公司Ҏ(gu)员工甲的月薪q行调整Ӟ通常会先在表员工中修改薪资列,然后?br /> 表员工记录中修改薪资调整旉与薪?br />
Create trigger compensation
on 员工
after update
as
if @@rowcount=0 return
if update(薪资)
begin
insert 员工记录
select 员工遍号,薪资,getdate()
from inserted
end
--列出SQL SERVER 所有表Q字D名Q主键,cdQ长度,数位数{信?/p>
--在查询分析器里运行即?可以生成一个表Q导出到EXCEL?/p>
-- ======================================================
SELECT
(case when a.colorder=1 then d.name else '' end)表名,
a.colorder 字段序号,
a.name 字段?
(case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '?else '' end) 标识,
(case when (SELECT count(*)
FROM sysobjects
WHERE (name in
(SELECT name
FROM sysindexes
WHERE (id = a.id) AND (indid in
(SELECT indid
FROM sysindexkeys
WHERE (id = a.id) AND (colid in
(SELECT colid
FROM syscolumns
WHERE (id = a.id) AND (name = a.name))))))) AND
(xtype = 'PK'))>0 then '? else '' end) 主键,
b.name cd,
a.length 占用字节?
COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,
isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 数位数,
(case when a.isnullable=1 then '?else '' end) 允许I?
isnull(e.text,'') 默认?
isnull(g.[value],'') AS 字段说明
FROM syscolumns a left join systypes b
on a.xtype=b.xusertype
inner join sysobjects d
on a.id=d.id and d.xtype='U' and d.name<>'dtproperties'
left join syscomments e
on a.cdefault=e.id
left join sysproperties g
on a.id=g.id AND a.colid = g.smallid
order by a.id,a.colorder
-------------------------------------------------------------------------------------------------
列出SQL SERVER 所有表、字D定义,cdQ长度,一个值等信息
q导出到Excel ?/p>
-- ======================================================
-- Export all user tables definition and one sample value
-- jan-13-2003,Dr.Zhang
-- ======================================================
在查询分析器里运行:
SET ANSI_NULLS OFF
GO
SET NOCOUNT ON
GO
SET LANGUAGE 'Simplified Chinese'
go
DECLARE @tbl nvarchar(200),@fld nvarchar(200),@sql nvarchar(4000),@maxlen int,@sample nvarchar(40)
SELECT d.name TableName,a.name FieldName,b.name TypeName,a.length Length,a.isnullable IS_NULL INTO #t
FROM syscolumns a, systypes b,sysobjects d
WHERE a.xtype=b.xusertype and a.id=d.id and d.xtype='U'
DECLARE read_cursor CURSOR
FOR SELECT TableName,FieldName FROM #t
SELECT TOP 1 '_TableName ' TableName,
'FieldName ' FieldName,'TypeName ' TypeName,
'Length' Length,'IS_NULL' IS_NULL,
'MaxLenUsed' AS MaxLenUsed,'Sample Value ' Sample,
'Comment ' Comment INTO #tc FROM #t
OPEN read_cursor
FETCH NEXT FROM read_cursor INTO @tbl,@fld
WHILE (@@fetch_status <> -1) --- failes
BEGIN
IF (@@fetch_status <> -2) -- Missing
BEGIN
SET @sql=N'SET @maxlen=(SELECT max(len(cast('+@fld+' as nvarchar))) FROM '+@tbl+')'
--PRINT @sql
EXEC SP_EXECUTESQL @sql,N'@maxlen int OUTPUT',@maxlen OUTPUT
--print @maxlen
SET @sql=N'SET @sample=(SELECT TOP 1 cast('+@fld+' as nvarchar) FROM '+@tbl+' WHERE len(cast('+@fld+' as nvarchar))='+convert(nvarchar(5),@maxlen)+')'
EXEC SP_EXECUTESQL @sql,N'@sample varchar(30) OUTPUT',@sample OUTPUT
--for quickly
--SET @sql=N'SET @sample=convert(varchar(20),(SELECT TOP 1 '+@fld+' FROM '+
--@tbl+' order by 1 desc ))'
PRINT @sql
print @sample
print @tbl
EXEC SP_EXECUTESQL @sql,N'@sample nvarchar(30) OUTPUT',@sample OUTPUT
INSERT INTO #tc SELECT *,ltrim(ISNULL(@maxlen,0)) as MaxLenUsed,
convert(nchar(20),ltrim(ISNULL(@sample,' '))) as Sample,' ' Comment FROM #t where TableName=@tbl and FieldName=@fld
END
FETCH NEXT FROM read_cursor INTO @tbl,@fld
END
CLOSE read_cursor
DEALLOCATE read_cursor
GO
SET ANSI_NULLS ON
GO
SET NOCOUNT OFF
GO
select count(*) from #t
DROP TABLE #t
GO
select count(*)-1 from #tc
select * into ##tx from #tc order by tablename
DROP TABLE #tc
--select * from ##tx
declare @db nvarchar(60),@sql nvarchar(3000)
set @db=db_name()
--请修改用户名和口?导出到Excel ?/p>
set @sql='exec master.dbo.xp_cmdshell ''bcp ..dbo.##tx out c:\'+@db+'_exp.xls -w -C936 -Usa -Psa '''
print @sql
exec(@sql)
GO
DROP TABLE ##tx
GO
-- ======================================================
--Ҏ(gu)表中数据生成insert语句的存储过E?/p>
--建立存储q程Q执?spGenInsertSQL 表名
--感谢playyuer
-- ======================================================
CREATE proc spGenInsertSQL (@tablename varchar(256))
as
begin
declare @sql varchar(8000)
declare @sqlValues varchar(8000)
set @sql =' ('
set @sqlValues = 'values (''+'
select @sqlValues = @sqlValues + cols + ' + '','' + ' ,@sql = @sql + '[' + name + '],'
from
(select case
when xtype in (48,52,56,59,60,62,104,106,108,122,127)
then 'case when '+ name +' is null then ''NULL'' else ' + 'cast('+ name + ' as varchar)'+' end'
when xtype in (58,61)
then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast('+ name +' as varchar)'+ '+'''''''''+' end'
when xtype in (167)
then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'
when xtype in (231)
then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'
when xtype in (175)
then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar) + '))+'''''''''+' end'
when xtype in (239)
then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar) + '))+'''''''''+' end'
else '''NULL'''
end as Cols,name
from syscolumns
where id = object_id(@tablename)
) T
set @sql ='select ''INSERT INTO ['+ @tablename + ']' + left(@sql,len(@sql)-1)+') ' + left(@sqlValues,len(@sqlValues)-4) + ')'' from '+@tablename
--print @sql
exec (@sql)
end
GO
-- ======================================================
--Ҏ(gu)表中数据生成insert语句的存储过E?/p>
--建立存储q程Q执?proc_insert 表名
--感谢Sky_blue
-- ======================================================
CREATE proc proc_insert (@tablename varchar(256))
as
begin
set nocount on
declare @sqlstr varchar(4000)
declare @sqlstr1 varchar(4000)
declare @sqlstr2 varchar(4000)
select @sqlstr='select ''insert '+@tablename
select @sqlstr1=''
select @sqlstr2=' ('
select @sqlstr1= ' values ( ''+'
select @sqlstr1=@sqlstr1+col+'+'',''+' ,@sqlstr2=@sqlstr2+name +',' from (select case
-- when a.xtype =173 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'
when a.xtype =104 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(1),'+a.name +')'+' end'
when a.xtype =175 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'
when a.xtype =61 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'
when a.xtype =106 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'
when a.xtype =62 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'
when a.xtype =56 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(11),'+a.name +')'+' end'
when a.xtype =60 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'
when a.xtype =239 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'
when a.xtype =108 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'
when a.xtype =231 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'
when a.xtype =59 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'
when a.xtype =58 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'
when a.xtype =52 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(12),'+a.name +')'+' end'
when a.xtype =122 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'
when a.xtype =48 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(6),'+a.name +')'+' end'
-- when a.xtype =165 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'
when a.xtype =167 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'
else '''NULL'''
end as col,a.colid,a.name
from syscolumns a where a.id = object_id(@tablename) and a.xtype <>189 and a.xtype <>34 and a.xtype <>35 and a.xtype <>36
)t order by colid
select @sqlstr=@sqlstr+left(@sqlstr2,len(@sqlstr2)-1)+') '+left(@sqlstr1,len(@sqlstr1)-3)+')'' from '+@tablename
-- print @sqlstr
exec( @sqlstr)
set nocount off
end
GO
W?1 部分 - 设计数据库之?br />q一部分|列?12 个基本技巧,包括命名规范和明业务需求等?
W?2 部分 - 设计数据库表
d 24 个指南性技巧,늛表内字段设计以及应该避免的常见问题等?
W?3 部分 - 选择?br />怎么选择键呢Q这里有 10 个技巧专门涉及系l生成的主键的正用法,q有?时以及如何烦引字D以获得最x能{?
W?4 部分 - 保证数据完整?br />讨论如何保持数据库的清晰和健壮,如何把有x据降低到最程度?
W?5 部分 - 各种技?br />不包括在以上 4 个部分中的其他技巧,五花八门Q有了它们希望你的数据库开发工作会更轻松一些?
W?1 部分 - 设计数据库之?br />考察现有环境
在设计一个新数据库时Q你不但应该仔细研究业务需求而且q要考察现有的系l。大多数数据库项目都不是从头开始徏立的Q通常Q机构内M存在用来满特定需求的现有pȝQ可能没有实现自动计)。显Ӟ现有pȝq不完美Q否则你׃必再建立新系l了。但是对旧系l的研究可以让你发现一些可能会忽略的细微问题。一般来_考察现有pȝ对你l对有好处?
定义标准的对象命名规?br />一定要定义数据库对象的命名规范。对数据库表来说Q从目一开始就要确定表名是采用复数q是单数形式。此外还要给表的别名定义单规则(比方_如果表名是一个单词,别名取单词的前 4 个字母;如果表名是两个单词,各取两个单词的前两个字母组?4 个字母长的别名;如果表的名字?3 个单词组成,你不妨从头两个单词中各取一个然后从最后一个单词中再取Z个字母,l果q是l成 4 字母长的别名Q其余依ơ类推)对工作用表来_表名可以加上前缀 WORK_ 后面附上采用该表的应用程序的名字。表内的列[字段]要针寚w采用一整套设计规则。比如,如果键是数字cdQ你可以?_N 作ؓ后缀Q如果是字符cd则可以采?_C 后缀。对列[字段]名应该采用标准的前缀和后~。再如,假如你的表里有好多“money”字D,你不妨给每个列[字段]增加一?_M 后缀。还有,日期列[字段]最好以 D_ 作ؓ名字打头?/p>
查表名、报表名和查询名之间的命名规范。你可能会很快就被这些不同的数据库要素的名称搞糊涂了。假如你坚持l一地命名这些数据库的不同组成部分,臛_你应该在q些对象名字的开头用 Table、Query 或?Report {前~加以区别?/p>
如果采用?Microsoft AccessQ你可以?qry、rpt、tbl ?mod {符h标识对象Q比?tbl_EmployeesQ。我在和 SQL Server 打交道的时候还用过 tbl 来烦引表Q但我用 sp_company Q现在用 sp_feft_Q标识存储过E,因ؓ在有的时候如果我发现了更好的处理办法往往会保存好几个拯。我在实?SQL Server 2000 时用 udf_ Q或者类似的标记Q标识我~写的函数?
工欲善其? 必先利其?br />采用理想的数据库设计工具Q比如:SyBase 公司?PowerDesignQ她支持 PB、VB、Delphe {语aQ通过 ODBC 可以q接市面上流行的 30 多个数据库,包括 dBase、FoxPro、VFP、SQL Server {,今后有机会我着重介l?PowerDesign 的用?
获取数据模式资源手册
正在LCZ模式的h可以阅读《数据模式资源手册》一书,该书?Len Silverston、W. H. Inmon ?Kent Graziano ~写Q是一本值得拥有的最x据徏模图书。该书包括的章节늛多种数据领域Q比如h员、机构和工作效能{。其他的你还可以参考:[1]萨师煊 王珊著 数据库系l概?W二?高等教育出版C?1991、[2][] Steven M.Bobrowski ?Oracle 7 与客P服务器计技术从入门到精通 刘徏元等译 ?sh)子工业出版C,1996、[3]周中元 信息pȝ建模Ҏ(gu)(? ?sh)子与信息化 1999q第3期,1999
畅想未来Q但不可忘了q去的教?br />我发现询问用户如何看待未来需求变化非常有用。这样做可以辑ֈ两个目的Q首先,你可以清楚地了解应用设计在哪个地方应该更L(fng)zL以及如何避免性能瓉Q其ơ,你知道发生事先没有确定的需求变更时用户和你一h到吃惊?/p>
一定要Cq去的经验教训!我们开发h员还应该通过分n自己的体会和l验互相帮助。即使用戯Z们再也不需要什么支持了Q我们也应该对他们进行这斚w的教Ԍ我们都曾l面临过q样的时删Z当初要是这么做了该多好..”?
在物理实践之前进行逻辑设计
在深入物理设计之前要先进行逻辑设计。随着大量?CASE 工具不断涌现出来Q你的设计也可以辑ֈ相当高的逻辑水准Q你通常可以从整体上更好C解数据库设计所需要的Ҏ(gu)面面?
了解你的业务
在你癑ֈ癑֜定pȝ从客戯度满_需求之前不要在你的 ERQ实体关p)模式中加入哪怕一个数据表Q怎么Q你q没有模式?那请你参看技?9Q。了解你的企业业务可以在以后的开发阶D节U大量的旉。一旦你明确了业务需求,你就可以自己做出许多决策了?/p>
一旦你认ؓ你已l明了业务内容Q你最好同客户q行一ơ系l的交流。采用客L(fng)术语q且向他们解释你所惛_的和你所听到的。同时还应该用可能、将会和必须{词汇表辑ևpȝ的关pd数。这样你可以让你的客户U正你自q理解然后做好下一步的 ER 设计?
创徏数据字典?ER 图表
一定要q旉创徏 ER 图表和数据字典。其中至应该包含每个字D늚数据cd和在每个表内的主外键。创?ER 图表和数据字典确实有点费时但对其他开发h员要了解整个设计却是完全必要的。越早创能有助于避免今后面(f)的可能乱,从而可以让M了解数据库的人都明确如何从数据库中获得数据?/p>
有一份诸?ER 图表{最新文其重要性如何强调都不过分,q对表明表之间关pd有用Q而数据字典则说明了每个字D늚用途以及Q何可能存在的别名。对 SQL 表达式的文化来说这是完全必要的?
创徏模式
一张图表胜q千a万语Q开发h员不仅要阅读和实现它Q而且q要用它来帮助自己和用户对话。模式有助于提高协作效能Q这样在先期的数据库设计中几乎不可能出现大的问题。模式不必弄的很复杂Q甚臛_以简单到手写在一张纸上就可以了。只是要保证其上的逻辑关系今后能生效益?
从输入输Z?br />在定义数据库表和字段需求(输入Q时Q首先应查现有的或者已l设计出的报表、查询和视图Q输出)以决定ؓ了支持这些输出哪些是必要的表和字DcD个简单的例子Q假如客户需要一个报表按照邮政编码排序、分D和求和Q你要保证其中包括了单独的邮政编码字D而不要把邮政~码p进地址字段里?
报表技?br />要了解用户通常是如何报告数据的Q批处理q是在线提交报表Q时间间隔是每天、每周、每月、每个季度还是每q_如果需要的话还可以考虑创徏ȝ表。系l生成的主键在报表中很难理。用户在hpȝ生成主键的表内用副键q行索往往会返回许多重复数据。这L(fng)索性能比较低而且Ҏ(gu)引v混ؕ?
理解客户需?br />看v来这应该是显而易见的事,但需求就是来自客Pq里要从内部和外部客L(fng)角度考虑Q。不要依赖用户写下来的需求,真正的需求在客户的脑袋里。你要让客户解释光求,而且随着开发的l箋Q还要经常询问客户保证其需求仍然在开发的目的之中。一个不变的真理是:“只有我看见了我才知道我惌的是什么”必然会D大量的返工,因ؓ数据库没有达到客户从来没有写下来的需求标准。而更p的是你对他们需求的解释只属于你自己Q而且可能是完全错误的?
W?2 部分 - 设计表和字段
查各U变?br />我在设计数据库的时候会考虑到哪些数据字D将来可能会发生变更。比方说Q姓氏就是如此(注意是西方h的姓氏,比如x结婚后从夫姓等Q。所以,在徏立系l存储客户信息时Q我們于在单独的一个数据表里存储姓氏字D,而且q附加v始日和终止日{字D,q样可以跟t这一数据条目的变化?
采用有意义的字段?br />有一回我参加开发过一个项目,其中有从其他E序员那里承的E序Q那个程序员喜欢用屏q上昄数据指示用语命名字段Q这也不赖,但不q的是,她还喜欢用一些奇怪的命名法,其命名采用了匈牙利命名和控制序号的组合Ş式,比如 cbo1、txt2、txt2_b {等?br />除非你在使用只面向你的羃写字D名的系l,否则请尽可能地把字段描述的清楚些。当Ӟ也别做过头了Q比?Customer_Shipping_Address_Street_Line_1Q虽然很富有说明性,但没人愿意键入这么长的名字,具体度在你的把握中?
采用前缀命名
如果多个表里有好多同一cd的字D(比如 FirstNameQ,你不妨用特定表的前缀Q比?CusLastNameQ来帮助你标识字Dc?/p>
时效性数据应包括“最q更新日?旉”字Dc时间标记对查找数据问题的原因、按日期重新处理/重蝲数据和清除旧数据特别有用?
标准化和数据驱动
数据的标准化不仅方便了自p且也方便了其他人。比方说Q假如你的用L(fng)面要讉K外部数据源(文g、XML 文、其他数据库{)Q你不妨把相应的q接和\径信息存储在用户界面支持表里。还有,如果用户界面执行工作之cȝdQ发送邮件、打CW、修改记录状态等Q,那么产生工作的数据也可以存攑֜数据库里。预先安排总需要付出努力,但如果这些过E采用数据驱动而非编码的方式Q那么策略变更和l护都会方便得多。事实上Q如果过E是数据驱动的,你就可以把相当大的责Ll用Pqhl护自己的工作流q程?
标准化不能过?br />寚w些不熟?zhn)标准化一词(normalizationQ的言Q标准化可以保证表内的字D都是最基础的要素,而这一措施有助于消除数据库中的数据冗余。标准化有好几种形式Q但 Third Normal FormQ?NFQ通常被认为在性能、扩展性和数据完整性方面达C最好^衡。简单来_3NF 规定Q?br />* 表内的每一个值都只能被表达一ơ?br />* 表内的每一行都应该被唯一的标识(有唯一键)?br />* 表内不应该存储依赖于其他键的非键信息?br />遵守 3NF 标准的数据库h以下特点Q有一l表专门存放通过键连接v来的兌数据。比方说Q某个存攑֮户及其有兛_单的 3NF 数据库就可能有两个表QCustomer ?Order。Order 表不包含定单兌客户的Q何信息,但表内会存放一个键|该键指向 Customer 表里包含该客户信息的那一行?br />更高层次的标准化也有Q但更标准是否就一定更好呢Q答案是不一定。事实上Q对某些目来说Q甚臛_q?3NF 都可能给数据库引入太高的复杂性?/p>
Z效率的缘故,对表不进行标准化有时也是必要的,q样的例子很多。曾l有个开发餐饮分析Y件的zd是用非标准化表把查询旉从^?40 U降低到了两U左叟뀂虽然我不得不这么做Q但我绝不把数据表的非标准化当作当然的设计理c而具体的操作不过是一U派生。所以如果表Z问题重新产生非标准化的表是完全可能的?
Microsoft Visual FoxPro 报表技?br />如果你正在?Microsoft Visual FoxProQ你可以用对用户友好的字D名来代替编L(fng)名称Q比如用 Customer Name 代替 txtCNaM。这P当你用向导程?[WizardsQ台湾hUCؓ‘精灵’] 创徏表单和报表时Q其名字会让那些不是E序员的人更Ҏ(gu)阅读?
不活跃或者不采用的指C符
增加一个字D表C所在记录是否在业务中不再活跃挺有用的。不是客户、员工还是其他什么hQ这样做都能有助于再q行查询的时候过滤活跃或者不z跃状态。同时还消除了新用户在采用数据时所面(f)的一些问题,比如Q某些记录可能不再ؓ他们所用,再删除的时候可以vC定的防范作用?
使用角色实体定义属于某类别的列[字段]
在需要对属于特定cd或者具有特定角色的事物做定义时Q可以用角色实体来创建特定的旉兌关系Q从而可以实现自我文化?br />q里的含义不是让 PERSON 实体带有 Title 字段Q而是_Z么不?PERSON 实体?PERSON_TYPE 实体来描qCh员呢Q比方说Q当 John Smith, Engineer 提升?John Smith, Director 乃至最后爬?John Smith, CIO 的高位,而所有你要做的不q是改变两个?PERSON ?PERSON_TYPE 之间关系的键|同时增加一个日?旉字段来知道变化是何时发生的。这P你的 PERSON_TYPE 表就包含了所?PERSON 的可能类型,比如 Associate、Engineer、Director、CIO 或?CEO {?br />q有个替代办法就是改?PERSON 记录来反映新头衔的变化,不过q样一来在旉上无法跟t个人所处位|的具体旉?
采用常用实体命名机构数据
l织数据的最单办法就是采用常用名字,比如QPERSON、ORGANIZATION、ADDRESS ?PHONE {等。当你把q些常用的一般名字组合v来或者创建特定的相应副实体时Q你得C自己用的Ҏ(gu)版本。开始的时候采用一般术语的主要原因在于所有的具体用户都能Ҏ(gu)象事物具体化?br />有了q些抽象表示Q你可以在W?2 U标识中采用自己的特D名Uͼ比如QPERSON 可能?Employee、Spouse、Patient、Client、Customer、Vendor 或?Teacher {。同L(fng)QORGANIZATION 也可能是 MyCompany、MyDepartment、Competitor、Hospital、Warehouse、Government {。最?ADDRESS 可以具体?Site、Location、Home、Work、Client、Vendor、Corporate ?FieldOffice {?br />采用一般抽象术语来标识“事物”的cd可以让你在关联数据以满业务要求斚w获得巨大的灵zL,同时q样做还可以显著降低数据存储所需的冗余量?
用户来自世界各地
在设计用到网l或者具有其他国际特性的数据库时Q一定要C大多数国安有不同的字段格式Q比如邮政编码等Q有些国Ӟ比如新西兰就没有邮政~码一说?
数据重复需要采用分立的数据?br />如果你发现自己在重复输入数据Q请创徏新表和新的关pR?
每个表中都应该添加的 3 个有用的字段
* dRecordCreationDateQ在 VB 下默认是 Now()Q而在 SQL Server 下默认ؓ GETDATE()
* sRecordCreatorQ在 SQL Server 下默认ؓ NOT NULL DEFAULT USER
* nRecordVersionQ记录的版本标记Q有助于准确说明记录中出?null 数据或者丢失数据的原因
对地址和电(sh)话采用多个字D?br />描述街道地址q短一行记录是不够的。Address_Line1、Address_Line2 ?Address_Line3 可以提供更大的灵zL。还有,?sh)话L(fng)和邮件地址最好拥有自q数据表,光h自n的类型和标记cd?/p>
q分标准化可要小心,q样做可能会D性能上出现问题。虽然地址和电(sh)话表分离通常可以辑ֈ最佳状态,但是如果需要经常访问这cM息,或许在其父表中存䏀首选”信息(比如 Customer {)更ؓ妥当些。非标准化和加速访问之间的妥协是有一定意义的?
使用多个名称字段
我觉得很吃惊Q许多h在数据库里就l?name 留一个字Dc我觉得只有刚入门的开发h员才会这么做Q但实际上网上这U做法非常普遍。我应该把姓氏和名字当作两个字段来处理,然后在查询的时候再把他们组合v来?/p>
我最常用的是在同一表中创徏一个计列[字段]Q通过它可以自动地q接标准化后的字D,q样数据变动的时候它也跟着变。不q,q样做在采用建模软g时得很机灉|行。MQ采用连接字D늚方式可以有效的隔ȝ户应用和开发h员界面?
提防大小写用的对象名和Ҏ(gu)字符
q去最令我恼火的事情之一是数据库里有大写L(fng)的对象名Q比?CustomerData。这一问题?Access ?Oracle 数据库都存在。我不喜Ƣ采用这U大写L(fng)的对象命名方法,l果q不得不手工修改名字。想想看Q这U数据库/应用E序能到采用更强大数据库的那一天吗Q采用全部大写而且包含下划W的名字h更好的可L(CUSTOMER_DATAQ,l对不要在对象名的字W之间留I格?
心保留?br />要保证你的字D名没有和保留词、数据库pȝ或者常用访问方法冲H,比如Q最q我~写的一?ODBC q接E序里有个表Q其中就用了 DESC 作ؓ说明字段名。后果可惌知QDESC ?DESCENDING ~写后的保留词。表里的一?SELECT * 语句倒是能用Q但我得到的却是一大堆毫无用处的信息?
保持字段名和cd的一致?br />在命名字Dƈ为其指定数据cd的时候一定要保证一致性。假如字D在某个表中叫做“agreement_number”,你就别在另一个表里把名字Ҏ(gu)“ref1”。假如数据类型在一个表里是整数Q那在另一个表里可别变成字符型了。记住,你干完自qzMQ其他hq要用你的数据库呢?
仔细选择数字cd
?SQL 中?smallint ?tinyint cd要特别小心,比如Q假如你想看看月销售总额Q你的总额字段cd?smallintQ那么,如果总额过?$32,767 你就不能q行计算操作了?
删除标记
在表中包含一个“删除标记”字D,q样可以把行标Cؓ删除。在关系数据库里不要单独删除某一行;最好采用清除数据程序而且要仔l维护烦引整体性?
避免使用触发?br />触发器的功能通常可以用其他方式实现。在调试E序时触发器可能成ؓq扰。假如你实需要采用触发器Q你最好集中对它文化?
包含版本机制
你在数据库中引入版本控制机制来确定用中的数据库的版本。无论如何你都要实现q一要求。时间一长,用户的需求L会改变的。最l可能会要求修改数据库结构。虽然你可以通过查新字段或者烦引来定数据库结构的版本Q但我发现把版本信息直接存放到数据库中不更ؓ方便吗??
l文本字D늕余?br />ID cd的文本字D,比如客户 ID 或定单号{等都应该设|得比一般想象更大,因ؓ旉不长你多半就会因d额外的字W而难堪不巌Ӏ比方说Q假设你的客?ID ?10 位数ѝ那你应该把数据库表字段的长度设?12 或?13 个字W长。这浪费空间吗Q是有一点,但也没你惌的那么多Q一个字D加?3 个字W在?1 百万条记录,再加上一点烦引的情况下才不过让整个数据库多占?3MB 的空间。但q额外占据的I间却无需来重构整个数据库就可以实现数据库规模的增长了。n份证的号码从 15 位变?18 位就是最好和最惨痛的例子?
列[字段]命名技?br />我们发现Q假如你l每个表的列[字段]名都采用l一的前~Q那么在~写 SQL 表达式的时候会得到大大的简化。这样做也确实有~点Q比如破坏了自动表连接工L(fng)作用Q后者把公共列[字段]名同某些数据库联pv来,不过pq些工具有时不也q接错误嘛。D个简单的例子Q假设有两个表:
Customer ?Order。Customer 表的前缀?cu_Q所以该表内的子D名如下Qcu_name_id、cu_surname、cu_initials 和cu_address {。Order 表的前缀?or_Q所以子D名是:
or_order_id、or_cust_name_id、or_quantity ?or_description {?br />q样从数据库中选出全部数据?SQL 语句可以写成如下所C:
Select * From Customer, Order Where cu_surname = "MYNAME" ;
and cu_name_id = or_cust_name_id and or_quantity = 1
在没有这些前~的情况下则写成这个样子(用别名来区分Q:
Select * From Customer, Order Where Customer.surname = "MYNAME" ;
and Customer.name_id = Order.cust_name_id and Order.quantity = 1
W?1 ?SQL 语句没少键入多少字符。但如果查询涉及?5 个表乃至更多的列[字段]你就知道q个技巧多有用了?
W?3 部分 - 选择键和索引
数据采掘要预先计?br />我所在的某一客户部门一度要处理 8 万多份联pL式,同时填写每个客户的必要数据(q绝对不是小z)。我从中q要定Zl客户作为市场目标。当我从最开始设计表和字D늚时候,我试图不在主索引里增加太多的字段以便加快数据库的q行速度。然后我意识到特定的l查询和信息采掘既不准确速度也不快。结果只好在ȝ引中重徏而且合ƈ了数据字Dc我发现有一个指C划相当关键——当我想创徏pȝcd查找时ؓ什么要采用L(fng)作ؓȝ引字D呢Q我可以用传真号码进行检索,但是它几乎就象系l类型一样对我来说ƈ不重要。采用后者作Z字段Q数据库更新后重新烦引和索就快多了?/p>
可操作数据仓库(ODSQ和数据仓库QDWQ这两种环境下的数据索引是有差别的。在 DW 环境下,你要考虑销售部门是如何l织销售活动的。他们ƈ不是数据库管理员Q但是他们确定表内的键信息。这里设计h员或者数据库工作人员应该分析数据库结构从而确定出性能和正输Z间的最x件?
使用pȝ生成的主?br />q类同技?1Q但我觉得有必要在这里重复提醒大家。假如你L在设计数据库的时候采用系l生成的键作Z键,那么你实际控制了数据库的索引完整性。这P数据库和非h工机制就有效地控制了对存储数据中每一行的讉K?br />采用pȝ生成键作Z键还有一个优点:当你拥有一致的键结构时Q找到逻辑~陷很容易?
分解字段用于索引
Z分离命名字段和包含字D以支持用户定义的报表,误虑分解其他字段Q甚至主键)为其l成要素以便用户可以对其q行索引。烦引将加快 SQL 和报表生成器脚本的执行速度。比方说Q我通常在必M?SQL LIKE 表达式的情况下创建报表,因ؓ case number 字段无法分解?year、serial number、case type ?defendant code {要素。性能也会变坏。假如年度和cd字段可以分解为烦引字D那么这些报表运行v来就会快多了?
键设?4 原则
* 为关联字D创建外键?br />* 所有的键都必须唯一?br />* 避免使用复合键?br />* 外键L兌唯一的键字段?
别忘了烦?br />索引是从数据库中获取数据的最高效方式之一?5% 的数据库性能问题都可以采用烦引技术得到解冟뀂作Z条规则,我通常寚w辑主键使用唯一的成l烦引,对系l键Q作为存储过E)采用唯一的非成组索引Q对M外键列[字段]采用非成l烦引。不q,索引p是盐Q太多了菜就怺。你得考虑数据库的I间有多大,表如何进行访问,q有q些讉K是否主要用作d?/p>
大多数数据库都烦引自动创建的主键字段Q但是可别忘了烦引外键,它们也是l常使用的键Q比如运行查询显CZ表和所有关联表的某条记录就用得上。还有,不要索引 memo/note 字段Q不要烦引大型字D(有很多字W)Q这样作会让索引占用太多的存储空间?
不要索引常用的小型表
不要为小型数据表讄M键,假如它们l常有插入和删除操作更别这样作了。对q些插入和删除操作的索引l护可能比扫描表I间消耗更多的旉?
不要把社会保障号码(SSNQ或w䆾证号码(IDQ选作?br />永远都不要?SSN ?ID 作ؓ数据库的键。除了隐U原因以外,ȝ政府来趋向于不准许把 SSN ?ID 用作除收入相关以外的其他目的QSSN ?ID 需要手工输入。永q不要用手工输入的键作Z键,因ؓ一旦你输入错误Q你唯一能做的就是删除整个记录然后从头开始?/p>
我在破解他h的程序时候,我看到很多h?SSN ?ID q曾被用做系列号Q当然尽这么做是非法的。而且Z也都知道q是非法的,但他们已l习惯了。后来,随着盗取w䆾犯罪案g的增加,我现在的同行正痛苦地从一大摊子数据中?SSN ?ID 删除?
不要用用L(fng)?br />在确定采用什么字D作的键的时候,可一定要心用户要~辑的字Dc通常的情况下不要选择用户可编辑的字段作ؓ键。这样做会迫使你采取以下两个措施Q?br />* 在创录之后对用户~辑字段的行为施加限制。假如你q么做了Q你可能会发C的应用程序在商务需求突然发生变化,而用户需要编辑那些不可编辑的字段时缺乏够的灉|性。当用户在输入数据之后直C存记录才发现pȝZ问题他们该怎么惻I删除重徏Q假如记录不可重建是否让用户走开Q?br />* 提出一些检和U正键冲H的Ҏ(gu)。通常Q费点精力也搞定了Q但是从性能上来看这样做的代价就比较大了。还有,键的U正可能会迫使你H破你的数据和商?用户界面层之间的隔离?br />所以还是重提一句老话Q你的设计要适应用户而不是让用户来适应你的设计?/p>
不让主键h可更新性的原因是在关系模式下,主键实现了不同表之间的关联。比如,Customer 表有一个主?CustomerIDQ而客L(fng)定单则存攑֜另一个表里。Order 表的主键可能?OrderNo 或?OrderNo、CustomerID 和日期的l合。不你选择哪种键设|,你都需要在 Order 表中存放 CustomerID 来保证你可以l下定单的用h到其定单记录?br />假如你在 Customer 表里修改?CustomerIDQ那么你必须扑և Order 表中的所有相兌录对其进行修攏V否则,有些定单׃不属于Q何客户——数据库的完整性就完蛋了?br />如果索引完整性规则施加到表一U,那么在不~写大量代码和附加删除记录的情况下几乎不可能改变某一条记录的键和数据库内所有关联的记录。而这一q程往往错误丛生所以应该尽量避免?
可选键(候选键)有时可做主键
CQ查询数据的不是机器而是人?br />假如你有可选键Q你可能q一步把它用做主键。那L(fng)话,你就拥有了徏立强大烦引的能力。这样可以阻止用数据库的h不得不连接数据库从而恰当的qo数据。在严格控制域表的数据库上,q种负蝲是比较醒目的。如果可选键真正有用Q那是辑ֈ了主键的水准?br />我的看法是,假如你有可选键Q比如国家表内的 state_codeQ你不要在现有不能变动的唯一键上创徏后箋的键。你要做的无非是创徏毫无价值的数据。如你因度用表的后l键[别名]建立q种表的兌Q操作负载真得需要考虑一下了?
别忘了外?br />大多数数据库索引自动创徏的主键字Dc但别忘了烦引外键字D,它们在你x询主表中的记录及其关联记录时每次都会用到。还有,不要索引 memo/notes 字段而且不要索引大型文本字段Q许多字W)Q这样做会让你的索引占据大量的数据库I间?
W?4 部分 - 保证数据的完整?br />用约束而非商务规则强制数据完整?br />如果你按照商务规则来处理需求,那么你应当检查商务层?用户界面Q如果商务规则以后发生变化,那么只需要进行更新即可。假如需求源于维护数据完整性的需要,那么在数据库层面上需要施加限制条件。如果你在数据层实采用了约束,你要保证有办法把更新不能通过U束查的原因采用用户理解的语a通知用户界面。除非你的字D命名很冗长Q否则字D名本nq不够?/p>
只要有可能,请采用数据库pȝ实现数据的完整性。这不但包括通过标准化实现的完整性而且q包括数据的功能性。在写数据的时候还可以增加触发器来保证数据的正性。不要依赖于商务层保证数据完整性;它不能保证表之间Q外键)的完整性所以不能强加于其他完整性规则之上?
分布式数据系l?br />对分布式pȝ而言Q在你决定是否在各个站点复制所有数据还是把数据保存在一个地方之前应该估计一下未?5 q或?10 q的数据量。当你把数据传送到其他站点的时候,最好在数据库字D中讄一些标记。在目的站点收到你的数据之后更新你的标记。ؓ了进行这U数据传输,请写下你自己的批处理或者调度程序以特定旉间隔q行而不要让用户在每天的工作后传输数据。本地拷贝你的维护数据,比如计算常数和利息率{,讄版本号保证数据在每个站点都完全一致?
强制指示完整?参照完整?)
没有好办法能在有x据进入数据库之后消除它,所以你应该在它q入数据库之前将其剔除。激zL据库pȝ的指C完整性特性。这样可以保持数据的清洁而能q开发h员投入更多的旉处理错误条g?
关系
如果两个实体之间存在多对一关系Q而且q有可能转化为多对多关系Q那么你最好一开始就讄成多对多关系。从现有的多对一关系转变为多对多关系比一开始就是多对多关系要难得多?
采用视图
Z在你的数据库和你的应用程序代码之间提供另一层抽象,你可以ؓ你的应用E序建立专门的视图而不必非要应用程序直接访问数据表。这样做q等于在处理数据库变更时l你提供了更多的自由?
l数据保有和恢复制定计划
考虑数据保有{略q包含在设计q程中,预先设计你的数据恢复q程。采用可以发布给用户/开发h员的数据字典实现方便的数据识别同时保证对数据源文档化。编写在U更新来“更新查询”供以后万一数据丢失可以重新处理更新?
用存储过E让pȝ做重z?br />解决了许多麻烦来产生一个具有高度完整性的数据库解x案之后,我决定封装一些关联表的功能组Q提供一整套常规的存储过E来讉K各组以便加快速度和简化客L(fng)序代码的开发。数据库不只是一个存放数据的地方Q它也是化编码之地?
使用查找
控制数据完整性的最x式就是限制用L(fng)选择。只要有可能都应该提供给用户一个清晰的价值列表供光择。这样将减少键入代码的错误和误解同时提供数据的一致性。某些公共数据特别适合查找Q国家代码、状态代码等?
W?5 部分 - 各种技?br />文、文档、文?br />Ҏ(gu)有的快捷方式、命名规范、限制和函数都要~制文?/p>
采用l表、列[字段]、触发器{加注释的数据库工具。是的,q有点费事,但从长远来看Q这样做对开发、支持和跟踪修改非常有用?/p>
取决于你使用的数据库pȝQ可能有一些Y件会l你一些供你很快上手的文档。你可能希望先开始在_然后获得来多的细节。或者你可能希望周期性的预排Q在输入新数据同旉着你的q展Ҏ(gu)一部分l节化。不你选择哪种方式Q总要对你的数据库文档化,或者在数据库自w的内部或者单独徏立文档。这P当你q了一q多旉后再回过头来做第 2 个版本,你犯错的Z大大减?
使用常用pQ或者其他Q何语aQ而不要用编?br />Z么我们经帔R用编码(比如 9935A 可能是‘青岛啤酒’的供应代码Q?XF788-Q 可能是帐目编码)Q理由很多。但是用户通常都用pq行思考而不是编码。工?5 q的会计或许知道 4XF788-Q 是什么东西,但新来的可就不一定了。在创徏下拉菜单、列表、报表时最好按照英语名排序。假如你需要编码,那你可以在编码旁附上用户知道的英语?
保存常用信息
让一个表专门存放一般数据库信息非常有用。我常在q个表里存放数据库当前版本、最q检?修复Q对 FoxProQ、关联设计文的名称、客L(fng)信息。这样可以实CU简单机制跟t数据库Q当客户抱怨他们的数据库没有达到希望的要求而与你联pLQ这样做寚w客户?服务器环境特别有用?
试、测试、反复测?br />建立或者修订数据库之后Q必ȝ用户新输入的数据试数据字段。最重要的是Q让用户q行试q且同用户一道保证你选择的数据类型满_业要求。测试需要在把新数据库投入实际服务之前完成?
查设?br />在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原型检查数据库。换句话_针对每一U最l表达数据的原型应用Q保证你查了数据模型q且查看如何取出数据?
Microsoft Visual FoxPro 设计技?br />对复杂的 Microsoft Visual FoxPro 数据库应用程序而言Q可以把所有的主表攑֜一个数据库容器文g里,然后增加其他数据库表文g和装载同原有数据库有关的Ҏ(gu)文g。根据需要用q些文gq接C文g中的主表。比如数据输入、数据烦引、统计分析、向理层或者政府部门提供报表以及各cdL询等。这一措施化了用户和组权限的分配,而且有利于应用程序函敎ͼ存储q程Q的分组和划分,从而在E序必须修改的时候易于管理?
字段1 | 字段2 | 字段3 | 字段4 |
字段1 | 字段2 | 字段3 | 字段4 | |
字段3.1 | 字段3.2 |
范式应用
很显Ӟ在当前的M关系数据?/font>理pȝQDBMSQ中Q傻瓜也不可能做ZW合W一范式?a target="_blank">数据?/font>Q因些DBMS不允怽?a target="_blank">数据?/font>表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式?a target="_blank">数据?/font>都是不可能的?br />
W二范式Q?NFQ:数据?/font>表中不存在非关键字段对Q一候选关键字D늚部分函数依赖Q部分函C赖指的是存在l合关键字中的某些字D决定非关键字段的情况)Q也x有非关键字段都完全依赖于L一l候选关键字。假定选课关系表ؓSelectCourse(学号, 姓名, q龄, 评名称, 成W, 学分)Q关键字为组合关键字(学号, 评名称)Q因为存在如下决定关p:
(学号, 评名称) ?(姓名, q龄, 成W, 学分)
q个数据?/font>表不满W二范式Q因为存在如下决定关p:
(评名称) ?(学分)
(学号) ?(姓名, q龄)
卛_在组合关键字中的字段军_非关键字的情c?br />
׃不符?NFQ这个选课关系表会存在如下问题Q?br />
(1) 数据冗余Q?br />
同一门课E由n个学生选修Q?学分"重复n-1ơ;同一个学生选修了m门课E,姓名和年龄就重复了m-1ơ?br />
(2) 更新异常Q?br />
若调整了某门评的学分,数据表中所有行?学分"值都要更斎ͼ否则会出现同一门课E学分不同的情况?br />
(3) 插入异常Q?br />
假设要开设一门新的课E,暂时q没有h选修。这P׃q没?学号"关键字,评名称和学分也无法记录?a target="_blank">数据?/font>?br />
(4) 删除异常Q?br />
假设一批学生已l完成课E的选修Q这些选修记录应该从数据?/font>表中删除。但是,与此同时Q课E名U和学分信息也被删除了。很昄Q这也会D插入异常?
把选课关系表SelectCourse改ؓ如下三个表:
学生QStudent(学号, 姓名, q龄)Q?br />
评QCourse(评名称, 学分)Q?br />
选课关系QSelectCourse(学号, 评名称, 成W)?br />
q样?a target="_blank">数据?/font>表是W合W二范式的,消除了数据冗余、更新异常、插入异常和删除异常?br />
另外Q所有单关键字的数据?/font>表都W合W二范式Q因Z可能存在l合关键字?br />
W三范式Q?NFQ:在第二范式的基础上,数据表中如果不存在非关键字段对Q一候选关键字D늚传递函C赖则W合W三范式。所谓传递函C赖,指的是如果存?A ?B ?C"的决定关p,则C传递函C赖于A。因此,满W三范式?a target="_blank">数据?/font>表应该不存在如下依赖关系Q?br />
关键字段 ?非关键字Dx ?非关键字Dy
假定学生关系表ؓStudent(学号, 姓名, q龄, 所在学? 学院地点, 学院?sh)?Q关键字为单一关键?学号"Q因为存在如下决定关p:
(学号) ?(姓名, q龄, 所在学? 学院地点, 学院?sh)?
q个数据?/font>是符?NF的,但是不符?NFQ因为存在如下决定关p:
(学号) ?(所在学? ?(学院地点, 学院?sh)?
卛_在非关键字段"学院地点"?学院?sh)?对关键字D?学号"的传递函C赖?br />
它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知?br />
把学生关p表分ؓ如下两个表:
学生Q?学号, 姓名, q龄, 所在学?Q?br />
学院Q?学院, 地点, ?sh)??br />
q样?a target="_blank">数据?/font>表是W合W三范式的,消除了数据冗余、更新异常、插入异常和删除异常?br />
鲍依?U得范式QBCNFQ:在第三范式的基础上,数据?/font>表中如果不存在Q何字D对M候选关键字D늚传递函C赖则W合W三范式?br />
假设仓库理关系表ؓStorehouseManage(仓库ID, 存储物品ID, 理员I(y)D, 数量)Q且有一个管理员只在一个仓库工作;一个仓库可以存储多U物品。这?a target="_blank">数据?/font>表中存在如下军_关系Q?br />
(仓库ID, 存储物品ID) ?理员I(y)D, 数量)
(理员I(y)D, 存储物品ID) ?(仓库ID, 数量)
所以,(仓库ID, 存储物品ID)?理员I(y)D, 存储物品ID)都是StorehouseManage的候选关键字Q表中的唯一非关键字Dؓ数量Q它是符合第三范式的。但是,׃存在如下军_关系Q?br />
(仓库ID) ?(理员I(y)D)
(理员I(y)D) ?(仓库ID)
卛_在关键字D决定关键字D늚情况Q所以其不符合BCNF范式。它会出现如下异常情况:
(1) 删除异常Q?br />
当仓库被清空后,所?存储物品ID"?数量"信息被删除的同时Q?仓库ID"?理员I(y)D"信息也被删除了?br />
(2) 插入异常Q?br />
当仓库没有存储Q何物品时Q无法给仓库分配理员?br />
(3) 更新异常Q?br />
如果仓库换了理员,则表中所有行的管理员I(y)D都要修改?br />
把仓库管理关p表分解Z个关p表Q?br />
仓库理QStorehouseManage(仓库ID, 理员I(y)D)Q?br />
仓库QStorehouse(仓库ID, 存储物品ID, 数量)?br />
q样?a target="_blank">数据?/font>表是W合BCNF范式的,消除了删除异常、插入异常和更新异常?/p>
我们来逐步搞定一个论坛的数据?/font>Q有如下信息Q?br />
Q?Q?用户Q用户名QemailQ主,?sh)话Q联pd址
Q?Q?帖子Q发帖标题,发帖内容Q回复标题,回复内容
W一ơ我们将数据?/font>设计Z仅存在表Q?br /> 用户? email 主页 ?sh)?/td> 联系地址 发帖标题 发帖内容 回复标题 回复内容
q个数据?/font>表符合第一范式Q但是没有Q何一l候选关键字能决?a target="_blank">数据?/font>表的整行Q唯一的关键字D는户名也不能完全决定整个元l。我们需要增?发帖ID"?回复ID"字段Q即表修改为:用户?/td> email 主页 ?sh)?/td> 联系地址 发帖ID 发帖标题 发帖内容 回复ID 回复标题 回复内容
q样数据表中的关键字(用户名,发帖IDQ回复ID)能决定整行:
(用户?发帖ID,回复ID) ?(email,主页,?sh)?联系地址,发帖标题,发帖内容,回复标题,回复内容)
但是Q这L(fng)设计不符合第二范式,因ؓ存在如下军_关系Q?br />
(用户? ?(email,主页,?sh)?联系地址)
(发帖ID) ?(发帖标题,发帖内容)
(回复ID) ?(回复标题,回复内容)
即非关键字段部分函数依赖于候选关键字D,很明显,q个设计会导致大量的数据冗余和操作异常?
我们?a target="_blank">数据?/font>表分解ؓQ带下划U的为关键字Q:
Q?Q?用户信息Q用户名QemailQ主,?sh)话Q联pd址
Q?Q?帖子信息Q发帖IDQ标题,内容
Q?Q?回复信息Q回复IDQ标题,内容
Q?Q?发脓(chung)Q用户名Q发帖ID
Q?Q?回复Q发帖IDQ回复ID
q样的设计是满W???范式和BCNF范式要求的,但是q样的设计是不是最好的呢?
不一定?br />
观察可知Q第4?发帖"中的"用户??发帖ID"之间?QN的关p,因此我们可以?发帖"合ƈ到第2的"帖子信息"中;W??回复"中的 "发帖ID"?回复ID"之间也是1QN的关p,因此我们可以?回复"合ƈ到第3的"回复信息"中。这样可以一定量地减数据冗余,新的设计为:
Q?Q?用户信息Q用户名QemailQ主,?sh)话Q联pd址
Q?Q?帖子信息Q用户名Q发帖IDQ标题,内容
Q?Q?回复信息Q发帖IDQ回复IDQ标题,内容
数据?/font>?昄满所有范式的要求Q?br />
数据?/font>?中存在非关键字段"标题"?内容"对关键字D?发帖ID"的部分函C赖,即不满W二范式的要求,但是q一设计q不会导致数据冗余和操作异常Q?br />
数据?/font>?中也存在非关键字D?标题"?内容"对关键字D?回复ID"的部分函C赖,也不满W二范式的要求,但是?a target="_blank">数据?/font>?怼Q这一设计也不会导致数据冗余和操作异常?br />
由此可以看出Qƈ不一定要满范式的要求,对于1QN关系Q当1的一边合q到N的那边后QN的那边就不再满W二范式了,但是q种设计反而比较好Q?br />
对于MQN的关p,不能M一Ҏ(gu)N一边合q到另一边去Q这样会D不符合范式要求,同时D操作异常和数据冗余?
对于1Q?的关p,我们可以左边的1或者右边的1合ƈ到另一边去Q设计导致不W合范式要求Q但是ƈ不会D操作异常和数据冗余?br />
l论
满范式要求?a target="_blank">数据?/font>设计是结构清晰的Q同时可避免数据冗余和操作异常。这q意味着不符合范式要求的设计一定是错误的,?a target="_blank">数据?/font>表中存在1Q??QN关系q种较特D的情况下,合ƈD的不W合范式要求反而是合理的?br />
在我们设?a target="_blank">数据?/font>的时候,一定要时刻考虑范式的要求?/p>
]]>
下列语句部分是Mssql语句Q不可以在access中用?/p>
SQL分类Q?
DDL—数据定义语a(CREATEQALTERQDROPQDECLARE)
DML—数据操U语a(SELECTQDELETEQUPDATEQINSERT)
DCL—数据控制语a(GRANTQREVOKEQCOMMITQROLLBACK)
首先,要介l基语句Q?br />1、说明:创徏数据?br />CREATE DATABASE database-name
2、说明:删除数据?br />drop database dbname
3、说明:备䆾sql server
--- 创徏 备䆾数据?device
USE master
EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat'
--- 开?备䆾
BACKUP DATABASE pubs TO testBack
4、说明:创徏新表
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
Ҏ(gu)已有的表创徏新表Q?
AQcreate table tab_new like tab_old (使用旧表创徏新表)
BQcreate table tab_new as select col1,col2?from tab_old definition only
5、说明:删除新表drop table tabname
6、说明:增加一个列
Alter table tabname add column col type
注:列增加后不能删除。DB2中列加上后数据类型也不能改变Q唯一能改变的是增加varcharcd的长度?br />7、说明:d主键Q?Alter table tabname add primary key(col)
说明Q删除主键: Alter table tabname drop primary key(col)
8、说明:创徏索引Qcreate [unique] index idxname on tabname(col?)
删除索引Qdrop index idxname
注:索引是不可更改的Q想更改必须删除重新建?br />9、说明:创徏视图Qcreate view viewname as select statement
删除视图Qdrop view viewname
10、说明:几个单的基本的sql语句
选择Qselect * from table1 where 范围
插入Qinsert into table1(field1,field2) values(value1,value2)
删除Qdelete from table1 where 范围
更新Qupdate table1 set field1=value1 where 范围
查找Qselect * from table1 where field1 like ?value1%?---like的语法很_֦Q查资料!
排序Qselect * from table1 order by field1,field2 [desc]
LQselect count as totalcount from table1
求和Qselect sum(field1) as sumvalue from table1
q_Qselect avg(field1) as avgvalue from table1
最大:select max(field1) as maxvalue from table1
最:select min(field1) as minvalue from table1
11、说明:几个高查询q算?br />AQ?UNION q算W?
UNION q算W通过l合其他两个l果表(例如 TABLE1 ?TABLE2Qƈ消去表中M重复行而派生出一个结果表。当 ALL ?UNION 一起用时Q即 UNION ALLQ,不消除重复行。两U情况下Q派生表的每一行不是来?TABLE1 是来自 TABLE2?
BQ?EXCEPT q算W?
EXCEPT q算W通过包括所有在 TABLE1 中但不在 TABLE2 中的行ƈ消除所有重复行而派生出一个结果表。当 ALL ?EXCEPT 一起用时 (EXCEPT ALL)Q不消除重复行?
CQ?INTERSECT q算W?br />INTERSECT q算W通过只包?TABLE1 ?TABLE2 中都有的行ƈ消除所有重复行而派生出一个结果表。当 ALL ?INTERSECT 一起用时 (INTERSECT ALL)Q不消除重复行?
注:使用q算词的几个查询l果行必L一致的?
12、说明:使用外连?
A、left outer joinQ?
左外q接Q左q接Q:l果集几包括q接表的匚w行,也包括左q接表的所有行?
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
BQright outer join:
叛_q接(双?Q结果集既包括连接表的匹配连接行Q也包括双接表的所有行?
CQfull outer joinQ?
全外q接Q不仅包括符可接表的匹配行Q还包括两个q接表中的所有记录?/p>
其次Q大家来看一些不错的sql语句
1、说明:复制?只复制结?源表名:a 新表名:b) (Access可用)
法一Qselect * into b from a where 1<>1
法二Qselect top 0 * into b from a
2、说明:拯?拯数据,源表名:a 目标表名Qb) (Access可用)
insert into b(a, b, c) select d,e,f from b;
3、说明:跨数据库之间表的拯(具体数据使用l对路径) (Access可用)
insert into b(a, b, c) select d,e,f from b in ‘具体数据库?where 条g
例子Q?.from b in '"&Server.MapPath(".")&"\data.mdb" &"' where..
4、说明:子查?表名1Qa 表名2Qb)
select a,b,c from a where a IN (select d from b ) 或? select a,b,c from a where a IN (1,2,3)
5、说明:昄文章、提交h和最后回复时?br />select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
6、说明:外连接查?表名1Qa 表名2Qb)
select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
7、说明:在线视图查询(表名1Qa )
select * from (SELECT a,b,c FROM a) T where t.a > 1;
8、说明:between的用?between限制查询数据范围时包括了边界?not between不包?br />select * from table1 where time between time1 and time2
select a,b,c, from table1 where a not between 数? and 数?
9、说明:in 的用方?br />select * from table1 where a [not] in (‘??’??’??’??
10、说明:两张兌表,删除主表中已l在副表中没有的信息
delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 )
11、说明:四表联查问题Q?br />select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
12、说明:日程安排提前五分钟提?
SQL: select * from 日程安排 where datediff('minute',f开始时?getdate())>5
13、说明:一条sql 语句搞定数据库分?br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段
14、说明:?0条记?br />select top 10 * form table1 where 范围
15、说明:选择在每一lb值相同的数据中对应的a最大的记录的所有信?cMq样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成l排?{等.)
select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)
16、说明:包括所有在 TableA 中但不在 TableB和TableC 中的行ƈ消除所有重复行而派生出一个结果表
(select a from tableA ) except (select a from tableB) except (select a from tableC)
17、说明:随机取出10条数?br />select top 10 * from tablename order by newid()
18、说明:随机选择记录
select newid()
19、说明:删除重复记录
Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)
20、说明:列出数据库里所有的表名
select name from sysobjects where type='U'
21、说明:列出表里的所有的
select name from syscolumns where id=object_id('TableName')
22、说明:列示type、vender、pcs字段Q以type字段排列Qcase可以方便地实现多重选择Q类似select 中的case?br />select type,sum(case vender when 'A' then pcs else 0 end),sum(case vender when 'C' then pcs else 0 end),sum(case vender when 'B' then pcs else 0 end) FROM tablename group by type
昄l果Q?br />type vender pcs
?sh)?A 1
?sh)?A 1
光盘 B 2
光盘 A 2
手机 B 3
手机 C 3
23、说明:初始化表table1
TRUNCATE TABLE table1
24、说明:选择?0?5的记?br />select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc
随机选择数据库记录的Ҏ(gu)Q用Randomize函数Q通过SQL语句实现Q?br /> 对存储在数据库中的数据来_随机数特性能l出上面的效果,但它们可能太慢了些。你不能要求ASP“找个随机数”然后打印出来。实际上常见的解x案是建立如下所C的循环Q?
Randomize
RNumber = Int(Rnd*499) +1
While Not objRec.EOF
If objRec("ID") = RNumber THEN
... q里是执行脚?...
end if
objRec.MoveNext
Wend
q很Ҏ(gu)理解。首先,你取??00范围之内的一个随机数Q假?00是数据库内记录的LQ。然后,你遍历每一记录来测试ID 的倹{检查其是否匚wRNumber。满x件的话就执行由THEN 关键字开始的那一块代码。假如你的RNumber {于495Q那么要循环一遍数据库q旉可就长了。虽?00q个数字看v来大了些Q但相比更ؓE_的企业解x案这q是个小型数据库了,后者通常在一个数据库内就包含了成千上万条记录。这时候不死定了Q?
采用SQLQ你可以很快地扑և准确的记录ƈ且打开一个只包含该记录的recordsetQ如下所C:
Randomize
RNumber = Int(Rnd*499) + 1
SQL = "SELECT * FROM Customers WHERE ID = " & RNumber
set objRec = ObjConn.Execute(SQL)
Response.WriteRNumber & " = " & objRec("ID") & " " & objRec("c_email")
不必写出RNumber 和IDQ你只需要检查匹配情况即可。只要你对以上代码的工作满意Q你自可按需操作“随机”记录。Recordset没有包含其他内容Q因此你很快p扑ֈ你需要的记录q样大大降低了处理旉?
再谈随机?
现在你下定决心要榨干Random 函数的最后一滴aQ那么你可能会一ơ取出多条随录或者想采用一定随围内的记录。把上面的标准Random CZ扩展一下就可以用SQL应对上面两种情况了?
Z取出几条随机选择的记录ƈ存放在同一recordset内,你可以存储三个随机数Q然后查询数据库获得匚wq些数字的记录:
SQL = "SELECT * FROM Customers WHERE ID = " & RNumber & " OR ID = " & RNumber2 & " OR ID = " & RNumber3
假如你想选出10条记录(也许是每ơ页面装载时?0条链接的列表Q,你可以用BETWEEN 或者数学等式选出W一条记录和适当数量的递增记录。这一操作可以通过好几U方式来完成Q但?SELECT 语句只显CZU可能(q里的ID 是自动生成的L(fng)Q:
SQL = "SELECT * FROM Customers WHERE ID BETWEEN " & RNumber & " AND " & RNumber & "+ 9"
注意Q以上代码的执行目的不是查数据库内是否有9条ƈ发记录?/p>
随机d若干条记录,试q?br />Access语法QSELECT top 10 * From 表名 ORDER BY Rnd(id)
Sql server:select top n * from 表名 order by newid()
mysqlelect * From 表名 Order By rand() Limit n
Access左连接语?最q开发要用左q接,Access帮助什么都没有,|上没有Access的SQL说明,只有自己试, 现在C以备后查)
语法elect table1.fd1,table1,fd2,table2.fd2 From table1 left join table2 on table1.fd1,table2.fd1 where ...
使用SQL语句 ?..代替q长的字W串昄
语法Q?br />SQL数据库:select case when len(field)>10 then left(field,10)+'...' else field end as news_name,news_id from tablename
Access数据库:SELECT iif(len(field)>2,left(field,2)+'...',field) FROM tablename;
Conn.Execute说明
ExecuteҎ(gu)
该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集Q该Ҏ(gu)的用格式分Z下两U:
1Q执行SQL查询语句Ӟ返回查询得到的记录集。用法ؓQ?br /> Set 对象变量?q接对象.Execute("SQL 查询语言")
ExecuteҎ(gu)调用后,会自动创录集对象Qƈ查询结果存储在该记录对象中Q通过SetҎ(gu)Q将记录集赋l指定的对象保存Q以后对象变量就代表了该记录集对象?/p>
2Q执行SQL的操作性语aӞ没有记录集的q回。此时用法ؓQ?br /> q接对象.Execute "SQL 操作性语? [, RecordAffected][, Option]
·RecordAffected 为可选项Q此出可攄一个变量,SQL语句执行后,所生效的记录数会自动保存到该变量中。通过讉K该变量,可知道SQL语句队多条记录q行了操作?br /> ·Option 可选项Q该参数的取值通常为adCMDTextQ它用于告诉ADOQ应该将ExecuteҎ(gu)之后的第一个字W解释ؓ命o文本。通过指定该参敎ͼ可执行更高效?/p>
·BeginTrans、RollbackTrans、CommitTransҎ(gu)
q三个方法是q接对象提供的用于事务处理的Ҏ(gu)。BeginTrans用于开始一个事物;RollbackTrans用于回滚事务QCommitTrans用于提交所有的事务处理l果Q即认事务的处理?br /> 事务处理可以一l操作视Z个整体,只有全部语句都成功执行后Q事务处理才成功;若其中有一个语句执行失败,则整个处理就失败,q恢复到处里前的状态?br /> BeginTrans和CommitTrans用于标记事务的开始和l束Q在q两个之间的语句Q就是作Z务处理的语句。判断事务处理是否成功,可通过q接对象的Error集合来实玎ͼ若Error集合的成员个C?Q则说明有错误发生,事务处理p|。Error集合中的每一个Error对象Q代表一个错误信息?