﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-nbtymm-随笔分类-数据库技术</title><link>http://www.blogjava.net/nbtymm/category/11240.html</link><description>&lt;/br&gt;&lt;a href="http://www.fastonlineusers.com"&gt;&lt;b&gt;&lt;font color=red&gt;共有&lt;script src=http://fastonlineusers.com/online.php?d=bluedavy.blogjava.net&gt;&lt;/script&gt;人在同时阅读此Blog&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 03:10:01 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 03:10:01 GMT</pubDate><ttl>60</ttl><item><title>触发器设计技巧与实例</title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62144.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62144.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62144.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62144.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62144.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62144.html</trackback:ping><description><![CDATA[
		<p>     在数据库设计中，有两种方法可设定自动化的资料处理规则，一种是条件约束，<br />一种是触发器,一般而言，条件约束比触发器较容易设定及维护，且执行效率较<br />好，但条件约束只能对资料进行简单的栏位检核，当涉及到多表操作等复杂操<br />作时，就要用到触发器了.<br />      一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息，分别<br /> 是：<br />                 虚拟表Inserted                    虚拟表Deleted</p>
		<p>在表记录新增时   存放新增的记录                        不存储记录<br />        修改时   存放用来更新的新记录                  存放更新前的记录<br />        删除时   不存储记录                            存放被删除的记录<br /> <br />      触发器的种类及触发时机<br /> After触发器：触发时机在资料已变动完成后，它将对变动资料进行必要的<br />              善后与处理，若发现有错误，则用事务回滚（Rollback Transaction)<br />              将此次操作所更动的资料全部回复。<br /> Istead of 触发器：触发时机在资料变动前发生，且资料如何变动取决于触发器</p>
		<p>现在介绍一下创建触发器的编写格式：</p>
		<p>After类型:<br />    Create Trigger  触发器名称<br />    on 表名<br />    after 操作(insert,update)<br />    as  <br />      Sql语句</p>
		<p>Instead类型<br />    Create Trigger 触发器名称<br />    on 表名<br />    Instead of 操作(update,delete)<br />    as<br />      Sql语句</p>
		<p>实例1:<br />     在订单(表orders)中的订购数量(列名为num)有变动时，触发器会先到客户(表Customer)中<br />取得该用户的信用等级(列名为Level),然后再到信用额度(Creit)中取出该等级<br />许可的订购数量上下限，最后比较订单中的订购数量是否符合限制。</p>
		<p>代码：          <br />           Create  Trigger num_check<br />           on orders<br />           after insert,update<br />           as <br />           if update(num)<br />             begin<br />               if exists(select a.* from orders a join customer b on a.customerid=b.customerid<br />                                                  join creit c on b.level=c.level<br />                                                  where a.num between c.up and c.down)<br />               begin<br />                  rollback transaction<br />                  exec master..xp_sendmail 'administrator','客户的订购数量不符合限制'<br />               end   <br />             end  <br />实例2:<br />      有工资管理系统中，当公司对某员工甲的月薪进行调整时，通常会先在表员工中修改薪资列,然后在<br />      表员工记录中修改薪资调整时间与薪资<br />            <br />          Create trigger compensation<br />          on 员工<br />          after update<br />          as<br />            if @@rowcount=0 return<br />            if update(薪资)<br />               begin <br />                 insert 员工记录<br />                 select 员工遍号,薪资,getdate()<br />                 from inserted<br />               end</p>
<img src ="http://www.blogjava.net/nbtymm/aggbug/62144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 12:09 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>收藏几段SQL Server语句和存储过程 </title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62143.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62143.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62143.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62143.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62143.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62143.html</trackback:ping><description><![CDATA[
		<p>(转载自--http://www.cnblogs.com/qiubole/articles/157309.html)<br /><br /><br />-- ======================================================</p>
		<p>--列出SQL SERVER 所有表，字段名，主键，类型，长度，小数位数等信息</p>
		<p>--在查询分析器里运行即可,可以生成一个表，导出到EXCEL中</p>
		<p>-- ======================================================</p>
		<p>SELECT </p>
		<p>       (case when a.colorder=1 then d.name else '' end)表名,</p>
		<p>       a.colorder 字段序号,</p>
		<p>       a.name 字段名,</p>
		<p>       (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,</p>
		<p>       (case when (SELECT count(*)</p>
		<p>       FROM sysobjects</p>
		<p>       WHERE (name in</p>
		<p>                 (SELECT name</p>
		<p>                FROM sysindexes</p>
		<p>                WHERE (id = a.id) AND (indid in</p>
		<p>                          (SELECT indid</p>
		<p>                         FROM sysindexkeys</p>
		<p>                         WHERE (id = a.id) AND (colid in</p>
		<p>                                   (SELECT colid</p>
		<p>                                  FROM syscolumns</p>
		<p>                                  WHERE (id = a.id) AND (name = a.name))))))) AND</p>
		<p>              (xtype = 'PK'))&gt;0 then '√' else '' end) 主键,</p>
		<p>       b.name 类型,</p>
		<p>       a.length 占用字节数,</p>
		<p>       COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,</p>
		<p>       isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,</p>
		<p>       (case when a.isnullable=1 then '√'else '' end) 允许空,</p>
		<p>       isnull(e.text,'') 默认值,</p>
		<p>       isnull(g.[value],'') AS 字段说明    </p>
		<p> </p>
		<p>FROM  syscolumns  a left join systypes b </p>
		<p>on  a.xtype=b.xusertype</p>
		<p>inner join sysobjects d </p>
		<p>on a.id=d.id  and  d.xtype='U' and  d.name&lt;&gt;'dtproperties'</p>
		<p>left join syscomments e</p>
		<p>on a.cdefault=e.id</p>
		<p>left join sysproperties g</p>
		<p>on a.id=g.id AND a.colid = g.smallid  </p>
		<p>order by a.id,a.colorder</p>
		<p>-------------------------------------------------------------------------------------------------</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>列出SQL SERVER 所有表、字段定义，类型，长度，一个值等信息</p>
		<p>并导出到Excel 中</p>
		<p>-- ======================================================</p>
		<p>-- Export all user tables definition and one sample value</p>
		<p>-- jan-13-2003,Dr.Zhang</p>
		<p>-- ======================================================</p>
		<p>在查询分析器里运行：</p>
		<p>SET ANSI_NULLS OFF </p>
		<p>GO</p>
		<p>SET NOCOUNT ON</p>
		<p>GO</p>
		<p> </p>
		<p>SET LANGUAGE 'Simplified Chinese'</p>
		<p>go</p>
		<p>DECLARE @tbl nvarchar(200),@fld nvarchar(200),@sql nvarchar(4000),@maxlen int,@sample nvarchar(40)</p>
		<p> </p>
		<p>SELECT d.name TableName,a.name FieldName,b.name TypeName,a.length Length,a.isnullable IS_NULL INTO #t</p>
		<p>FROM  syscolumns  a,  systypes b,sysobjects d  </p>
		<p>WHERE  a.xtype=b.xusertype  and  a.id=d.id  and  d.xtype='U' </p>
		<p> </p>
		<p>DECLARE read_cursor CURSOR</p>
		<p>FOR SELECT TableName,FieldName FROM #t</p>
		<p> </p>
		<p>SELECT TOP 1 '_TableName                     ' TableName,</p>
		<p>            'FieldName                      ' FieldName,'TypeName             ' TypeName,</p>
		<p>            'Length' Length,'IS_NULL' IS_NULL, </p>
		<p>            'MaxLenUsed' AS MaxLenUsed,'Sample Value          ' Sample,</p>
		<p>             'Comment   ' Comment INTO #tc FROM #t</p>
		<p> </p>
		<p>OPEN read_cursor</p>
		<p> </p>
		<p>FETCH NEXT FROM read_cursor INTO @tbl,@fld</p>
		<p>WHILE (@@fetch_status &lt;&gt; -1)  --- failes</p>
		<p>BEGIN</p>
		<p>       IF (@@fetch_status &lt;&gt; -2) -- Missing</p>
		<p>       BEGIN</p>
		<p>              SET @sql=N'SET @maxlen=(SELECT max(len(cast(<a href="mailto:'+@fld+'"><font color="#1d58d1">'+@fld+'</font></a> as nvarchar))) FROM <a href="mailto:'+@tbl+')'"><font color="#1d58d1">'+@tbl+')'</font></a></p>
		<p>              --PRINT @sql</p>
		<p>              EXEC SP_EXECUTESQL @sql,N'@maxlen int OUTPUT',@maxlen OUTPUT</p>
		<p>              --print @maxlen</p>
		<p>              SET @sql=N'SET @sample=(SELECT TOP 1 cast(<a href="mailto:'+@fld+'"><font color="#1d58d1">'+@fld+'</font></a> as nvarchar) FROM <a href="mailto:'+@tbl+'"><font color="#1d58d1">'+@tbl+'</font></a> WHERE len(cast(<a href="mailto:'+@fld+'"><font color="#1d58d1">'+@fld+'</font></a> as nvarchar))='+convert(nvarchar(5),@maxlen)+')'</p>
		<p>              EXEC SP_EXECUTESQL @sql,N'@sample varchar(30) OUTPUT',@sample OUTPUT</p>
		<p>              --for quickly   </p>
		<p>              --SET @sql=N'SET @sample=convert(varchar(20),(SELECT TOP 1 <a href="mailto:'+@fld+'"><font color="#1d58d1">'+@fld+'</font></a> FROM '+</p>
		<p>                     <a href="mailto:--@tbl+'"><font color="#1d58d1">--@tbl+'</font></a> order by 1 desc ))'  </p>
		<p>              PRINT @sql</p>
		<p>              print @sample</p>
		<p>              print @tbl</p>
		<p>              EXEC SP_EXECUTESQL @sql,N'@sample nvarchar(30) OUTPUT',@sample OUTPUT</p>
		<p>              INSERT INTO #tc SELECT *,ltrim(ISNULL(@maxlen,0)) as MaxLenUsed,</p>
		<p>                     convert(nchar(20),ltrim(ISNULL(@sample,' '))) as Sample,' ' Comment FROM #t where <a href="mailto:TableName=@tbl"><font color="#1d58d1">TableName=@tbl</font></a> and <a href="mailto:FieldName=@fld"><font color="#1d58d1">FieldName=@fld</font></a></p>
		<p>       END</p>
		<p>       FETCH NEXT FROM read_cursor INTO @tbl,@fld</p>
		<p>END</p>
		<p> </p>
		<p>CLOSE read_cursor</p>
		<p>DEALLOCATE read_cursor</p>
		<p>GO</p>
		<p> </p>
		<p>SET ANSI_NULLS ON</p>
		<p>GO</p>
		<p>SET NOCOUNT OFF</p>
		<p>GO</p>
		<p>select count(*)  from #t</p>
		<p>DROP TABLE #t</p>
		<p>GO</p>
		<p> </p>
		<p>select count(*)-1  from #tc</p>
		<p> </p>
		<p>select * into ##tx from #tc order by tablename</p>
		<p>DROP TABLE #tc</p>
		<p> </p>
		<p>--select * from ##tx</p>
		<p> </p>
		<p>declare @db nvarchar(60),@sql nvarchar(3000)</p>
		<p>set @db=db_name()</p>
		<p>--请修改用户名和口令 导出到Excel 中</p>
		<p>set @sql='exec master.dbo.xp_cmdshell ''bcp ..dbo.##tx out c:\'+@db+'_exp.xls -w -C936 -Usa -Psa '''</p>
		<p>print @sql</p>
		<p>exec(@sql)</p>
		<p>GO</p>
		<p>DROP TABLE ##tx</p>
		<p>GO</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>-- ======================================================</p>
		<p>--根据表中数据生成insert语句的存储过程</p>
		<p>--建立存储过程，执行 spGenInsertSQL 表名</p>
		<p>--感谢playyuer</p>
		<p>-- ======================================================</p>
		<p>CREATE   proc spGenInsertSQL (@tablename varchar(256))</p>
		<p> </p>
		<p>as</p>
		<p>begin</p>
		<p>  declare @sql varchar(8000)</p>
		<p>  declare @sqlValues varchar(8000)</p>
		<p>  set @sql =' ('</p>
		<p>  set @sqlValues = 'values (''+'</p>
		<p>  select @sqlValues = @sqlValues + cols + ' + '','' + ' ,@sql = @sql + '[' + name + '],' </p>
		<p>    from </p>
		<p>        (select case </p>
		<p>                  when xtype in (48,52,56,59,60,62,104,106,108,122,127)                                </p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else ' + 'cast('+ name + ' as varchar)'+' end'</p>
		<p>                  when xtype in (58,61)</p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast('+ name +' as varchar)'+ '+'''''''''+' end'</p>
		<p>                 when xtype in (167)</p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>                  when xtype in (231)</p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>                  when xtype in (175)</p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'</p>
		<p>                  when xtype in (239)</p>
		<p>                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'</p>
		<p>                  else '''NULL'''</p>
		<p>                end as Cols,name</p>
		<p>           from syscolumns  </p>
		<p>          where id = object_id(@tablename) </p>
		<p>        ) T </p>
		<p>  set @sql ='select ''INSERT INTO ['+ @tablename + ']' + left(@sql,len(@sql)-1)+') ' + left(@sqlValues,len(@sqlValues)-4) + ')'' from <a href="mailto:'+@tablename"><font color="#1d58d1">'+@tablename</font></a></p>
		<p>  --print @sql</p>
		<p>  exec (@sql)</p>
		<p>end</p>
		<p> </p>
		<p>GO</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>-- ======================================================</p>
		<p>--根据表中数据生成insert语句的存储过程</p>
		<p>--建立存储过程，执行 proc_insert 表名</p>
		<p>--感谢Sky_blue</p>
		<p>-- ======================================================</p>
		<p> </p>
		<p>CREATE proc proc_insert (@tablename varchar(256))</p>
		<p>as</p>
		<p>begin</p>
		<p>       set nocount on</p>
		<p>       declare @sqlstr varchar(4000)</p>
		<p>       declare @sqlstr1 varchar(4000)</p>
		<p>       declare @sqlstr2 varchar(4000)</p>
		<p>       select @sqlstr='select ''insert <a href="mailto:'+@tablename"><font color="#1d58d1">'+@tablename</font></a></p>
		<p>       select @sqlstr1=''</p>
		<p>       select @sqlstr2=' ('</p>
		<p>       select @sqlstr1= ' values ( ''+'</p>
		<p>       select @sqlstr1=@sqlstr1+col+'+'',''+' ,@sqlstr2=@sqlstr2+name +',' from (select case </p>
		<p>--     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'</p>
		<p>       when a.xtype =104 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(1),'+a.name +')'+' end'</p>
		<p>       when a.xtype =175 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>       when a.xtype =61  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'</p>
		<p>       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'</p>
		<p>       when a.xtype =62  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'</p>
		<p>       when a.xtype =56  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(11),'+a.name +')'+' end'</p>
		<p>       when a.xtype =60  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'</p>
		<p>       when a.xtype =239 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>       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'</p>
		<p>       when a.xtype =231 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>       when a.xtype =59  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'</p>
		<p>       when a.xtype =58  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'</p>
		<p>       when a.xtype =52  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(12),'+a.name +')'+' end'</p>
		<p>       when a.xtype =122 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'</p>
		<p>       when a.xtype =48  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(6),'+a.name +')'+' end'</p>
		<p>--     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'</p>
		<p>       when a.xtype =167 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'</p>
		<p>       else '''NULL'''</p>
		<p>       end as col,a.colid,a.name</p>
		<p>       from syscolumns a where a.id = object_id(@tablename) and a.xtype &lt;&gt;189 and a.xtype &lt;&gt;34 and a.xtype &lt;&gt;35 and  a.xtype &lt;&gt;36</p>
		<p>       )t order by colid</p>
		<p>       </p>
		<p>       select @sqlstr=@sqlstr+left(@sqlstr2,len(@sqlstr2)-1)+') '+left(@sqlstr1,len(@sqlstr1)-3)+')'' from <a href="mailto:'+@tablename"><font color="#1d58d1">'+@tablename</font></a></p>
		<p>--  print @sqlstr</p>
		<p>       exec( @sqlstr)</p>
		<p>       set nocount off</p>
		<p>end</p>
		<p>GO</p>
<img src ="http://www.blogjava.net/nbtymm/aggbug/62143.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 12:05 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62143.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Transact SQL 常用语句以及函数简易说明(含示例) </title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62141.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 03:58:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62141.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62141.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62141.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62141.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62141.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Transact SQL  				语 句 功 能 				========================================================================																　　				--				数据操作 																																				　...&nbsp;&nbsp;<a href='http://www.blogjava.net/nbtymm/archive/2006/08/07/62141.html'>阅读全文</a><img src ="http://www.blogjava.net/nbtymm/aggbug/62141.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 11:58 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62141.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>整理了一些t-sql技巧(转自csdn)</title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62137.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 03:51:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62137.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62137.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62137.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62137.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62137.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一、 只复制一个表结构，不复制数据																										 																																						select										 										top										 										0										 										*										...&nbsp;&nbsp;<a href='http://www.blogjava.net/nbtymm/archive/2006/08/07/62137.html'>阅读全文</a><img src ="http://www.blogjava.net/nbtymm/aggbug/62137.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 11:51 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62137.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库设计方法、规范与技巧(转)</title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62136.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 03:46:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62136.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62136.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62136.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62136.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62136.html</trackback:ping><description><![CDATA[
		<font face="Verdana">一、数据库设计过程<br />　　数据库技术是信息资源管理最有效的手段。数据库设计是指对于一个给定的应用环境，构造最优的数据库模式，建立数据库及其应用系统，有效存储数据，满足用户信息要求和处理要求。<br />　　数据库设计中需求分析阶段综合各个用户的应用需求（现实世界的需求），在概念设计阶段形成独立于机器特点、独立于各个DBMS产品的概念模式（信息世界模型），用E-R图来描述。在逻辑设计阶段将E-R图转换成具体的数据库产品支持的数据模型如关系模型，形成数据库逻辑模式。然后根据用户处理的要求，安全性的考虑，在基本表的基础上再建立必要的视图（VIEW）形成数据的外模式。在物理设计阶段根据DBMS特点和处理的需要，进行物理存储安排，设计索引，形成数据库内模式。<br />　　1. 需求分析阶段 <br />　　需求收集和分析，结果得到数据字典描述的数据需求（和数据流图描述的处理需求）。 <br />　　需求分析的重点是调查、收集与分析用户在数据管理中的信息要求、处理要求、安全性与完整性要求。<br />　　需求分析的方法：调查组织机构情况、调查各部门的业务活动情况、协助用户明确对新系统的各种要求、确定新系统的边界。 <br />　　常用的调查方法有： 跟班作业、开调查会、请专人介绍、询问、设计调查表请用户填写、查阅记录。<br />　　分析和表达用户需求的方法主要包括自顶向下和自底向上两类方法。自顶向下的结构化分析方法（Structured Analysis，简称SA方法）从最上层的系统组织机构入手，采用逐层分解的方式分析系统，并把每一层用数据流图和数据字典描述。<br />　　数据流图表达了数据和处理过程的关系。系统中的数据则借助数据字典（Data Dictionary，简称DD）来描述。<br />　　数据字典是各类数据描述的集合，它是关于数据库中数据的描述，即元数据，而不是数据本身。数据字典通常包括数据项、数据结构、数据流、数据存储和处理过程五个部分(至少应该包含每个字段的数据类型和在每个表内的主外键)。<br />　　数据项描述＝｛数据项名，数据项含义说明，别名，数据类型，长度， <br />　　　　　　　　　取值范围，取值含义，与其他数据项的逻辑关系｝ <br />　　数据结构描述＝｛数据结构名，含义说明，组成:｛数据项或数据结构｝｝ <br />　　数据流描述＝｛数据流名，说明，数据流来源，数据流去向， <br />　　　　　　　　　组成:｛数据结构｝，平均流量，高峰期流量｝ <br />　　数据存储描述＝｛数据存储名，说明，编号，流入的数据流，流出的数据流， 　　<br />　　　　　　　　　　组成:｛数据结构｝，数据量，存取方式｝ <br />　　处理过程描述＝｛处理过程名，说明，输入:｛数据流｝，输出:｛数据流｝, <br />　　　　　　　　　　处理:｛简要说明｝｝ <br />　　2. 概念结构设计阶段 <br />　　通过对用户需求进行综合、归纳与抽象，形成一个独立于具体DBMS的概念模型，可以用E-R图表示。 <br />　　概念模型用于信息世界的建模。概念模型不依赖于某一个DBMS支持的数据模型。概念模型可以转换为计算机上某一DBMS支持的特定数据模型。 <br />　　概念模型特点：<br />　　(1) 具有较强的语义表达能力，能够方便、直接地表达应用中的各种语义知识。 <br />　　(2) 应该简单、清晰、易于用户理解，是用户与数据库设计人员之间进行交流的语言。<br />　　概念模型设计的一种常用方法为IDEF1X方法，它就是把实体-联系方法应用到语义数据模型中的一种语义模型化技术，用于建立系统信息模型。<br />　　使用IDEF1X方法创建E-R模型的步骤如下所示:<br />　　2.1 第零步——初始化工程<br />　　这个阶段的任务是从目的描述和范围描述开始，确定建模目标，开发建模计划，组织建模队伍，收集源材料，制定约束和规范。收集源材料是这阶段的重点。通过调查和观察结果，业务流程，原有系统的输入输出，各种报表，收集原始数据，形成了基本数据资料表。<br />　　2.2 第一步——定义实体<br />　　实体集成员都有一个共同的特征和属性集，可以从收集的源材料——基本数据资料表中直接或间接标识出大部分实体。根据源材料名字表中表示物的术语以及具有“代码”结尾的术语，如客户代码、代理商代码、产品代码等将其名词部分代表的实体标识出来，从而初步找出潜在的实体，形成初步实体表。<br />　　2.3 第二步——定义联系<br />　　IDEF1X模型中只允许二元联系，n元联系必须定义为n个二元联系。根据实际的业务需求和规则，使用实体联系矩阵来标识实体间的二元关系，然后根据实际情况确定出连接关系的势、关系名和说明，确定关系类型，是标识关系、非标识关系（强制的或可选的）还是非确定关系、分类关系。如果子实体的每个实例都需要通过和父实体的关系来标识，则为标识关系，否则为非标识关系。非标识关系中，如果每个子实体的实例都与而且只与一个父实体关联，则为强制的，否则为非强制的。如果父实体与子实体代表的是同一现实对象，那么它们为分类关系。<br />　　2.4 第三步——定义码<br />　　通过引入交叉实体除去上一阶段产生的非确定关系，然后从非交叉实体和独立实体开始标识侯选码属性，以便唯一识别每个实体的实例，再从侯选码中确定主码。为了确定主码和关系的有效性，通过非空规则和非多值规则来保证，即一个实体实例的一个属性不能是空值，也不能在同一个时刻有一个以上的值。找出误认的确定关系，将实体进一步分解，最后构造出IDEF1X模型的键基视图（KB图）。<br />　　2.5 第四步——定义属性<br />　　从源数据表中抽取说明性的名词开发出属性表，确定属性的所有者。定义非主码属性，检查属性的非空及非多值规则。此外，还要检查完全依赖函数规则和非传递依赖规则，保证一个非主码属性必须依赖于主码、整个主码、仅仅是主码。以此得到了至少符合关系理论第三范式的改进的IDEF1X模型的全属性视图。<br />　　2.6 第五步——定义其他对象和规则<br />　　定义属性的数据类型、长度、精度、非空、缺省值、约束规则等。定义触发器、存储过程、视图、角色、同义词、序列等对象信息。<br />　　3. 逻辑结构设计阶段 <br />　　将概念结构转换为某个DBMS所支持的数据模型（例如关系模型），并对其进行优化。设计逻辑结构应该选择最适于描述与表达相应概念结构的数据模型，然后选择最合适的DBMS。<br />　　将E-R图转换为关系模型实际上就是要将实体、实体的属性和实体之间的联系转化为关系模式,这种转换一般遵循如下原则： <br />　　1）一个实体型转换为一个关系模式。实体的属性就是关系的属性。实体的码就是关系的码。 <br />　　2）一个m:n联系转换为一个关系模式。与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性。而关系的码为各实体码的组合。 <br />　　3）一个1:n联系可以转换为一个独立的关系模式，也可以与n端对应的关系模式合并。如果转换为一个独立的关系模式，则与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性，而关系的码为n端实体的码。 <br />　　4）一个1:1联系可以转换为一个独立的关系模式，也可以与任意一端对应的关系模式合并。<br />　　5）三个或三个以上实体间的一个多元联系转换为一个关系模式。与该多元联系相连的各实体的码以及联系本身的属性均转换为关系的属性。而关系的码为各实体码的组合。 <br />　　6）同一实体集的实体间的联系，即自联系，也可按上述1:1、1:n和m:n三种情况分别处理。 <br />　　7）具有相同码的关系模式可合并。 <br />　　为了进一步提高数据库应用系统的性能，通常以规范化理论为指导，还应该适当地修改、调整数据模型的结构，这就是数据模型的优化。确定数据依赖。消除冗余的联系。确定各关系模式分别属于第几范式。确定是否要对它们进行合并或分解。一般来说将关系分解为3NF的标准，即：<br />　　表内的每一个值都只能被表达一次。<br />　　表内的每一行都应该被唯一的标识（有唯一键）。<br />　　表内不应该存储依赖于其他键的非键信息。 <br />　　4. 数据库物理设计阶段 <br />　　为逻辑数据模型选取一个最适合应用环境的物理结构（包括存储结构和存取方法）。根据DBMS特点和处理的需要，进行物理存储安排，设计索引，形成数据库内模式。<br />　　5. 数据库实施阶段 <br />　　运用DBMS提供的数据语言（例如SQL）及其宿主语言（例如C），根据逻辑设计和物理设计的结果建立数据库，编制与调试应用程序，组织数据入库，并进行试运行。 数据库实施主要包括以下工作：用DDL定义数据库结构、组织数据入库 、编制与调试应用程序、数据库试运行 <br />　　6. 数据库运行和维护阶段 <br />　　数据库应用系统经过试运行后即可投入正式运行。在数据库系统运行过程中必须不断地对其进行评价、调整与修改。包括：数据库的转储和恢复、数据库的安全性、完整性控制、数据库性能的监督、分析和改进、数据库的重组织和重构造。<br /><br />　　建模工具的使用<br />　　为加快数据库设计速度，目前有很多数据库辅助工具（CASE工具），如Rational公司的Rational Rose，CA公司的Erwin和Bpwin，Sybase公司的PowerDesigner以及Oracle公司的Oracle Designer等。<br />　　ERwin主要用来建立数据库的概念模型和物理模型。它能用图形化的方式，描述出实体、联系及实体的属性。ERwin支持IDEF1X方法。通过使用ERwin建模工具自动生成、更改和分析IDEF1X模型，不仅能得到优秀的业务功能和数据需求模型，而且可以实现从IDEF1X模型到数据库物理设计的转变。ERwin工具绘制的模型对应于逻辑模型和物理模型两种。在逻辑模型中，IDEF1X工具箱可以方便地用图形化的方式构建和绘制实体联系及实体的属性。在物理模型中，ERwin可以定义对应的表、列，并可针对各种数据库管理系统自动转换为适当的类型。<br />　　设计人员可根据需要选用相应的数据库设计建模工具。例如需求分析完成之后，设计人员可以使用Erwin画ER图，将ER图转换为关系数据模型，生成数据库结构；画数据流图，生成应用程序。<br />　　二、数据库设计技巧<br />　　1. 设计数据库之前（需求分析阶段）<br />　　1) 理解客户需求，询问用户如何看待未来需求变化。让客户解释其需求，而且随着开发的继续，还要经常询问客户保证其需求仍然在开发的目的之中。<br />　　2) 了解企业业务可以在以后的开发阶段节约大量的时间。<br />　　3) 重视输入输出。<br />　　在定义数据库表和字段需求（输入）时，首先应检查现有的或者已经设计出的报表、查询和视图（输出）以决定为了支持这些输出哪些是必要的表和字段。<br />　　举例：假如客户需要一个报表按照邮政编码排序、分段和求和，你要保证其中包括了单独的邮政编码字段而不要把邮政编码糅进地址字段里。<br />　　4) 创建数据字典和ER 图表<br />　　ER 图表和数据字典可以让任何了解数据库的人都明确如何从数据库中获得数据。ER图对表明表之间关系很有用，而数据字典则说明了每个字段的用途以及任何可能存在的别名。对SQL 表达式的文档化来说这是完全必要的。<br />　　5) 定义标准的对象命名规范<br />　　数据库各种对象的命名必须规范。<br />　　2. 表和字段的设计（数据库逻辑设计）<br />　　表设计原则<br />　　1) 标准化和规范化<br />　　数据的标准化有助于消除数据库中的数据冗余。标准化有好几种形式，但Third Normal Form（3NF）通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说，遵守3NF 标准的数据库的表设计原则是：“One Fact in One Place”即某个表只包括其本身基本的属性，当不是它们本身所具有的属性时需进行分解。表之间的关系通过外键相连接。它具有以下特点：有一组表专门存放通过键连接起来的关联数据。<br />　　举例：某个存放客户及其有关定单的3NF 数据库就可能有两个表：Customer 和Order。Order 表不包含定单关联客户的任何信息，但表内<br /><br />　　会存放一个键值，该键指向Customer 表里包含该客户信息的那一行。<br />　　事实上，为了效率的缘故，对表不进行标准化有时也是必要的。<br />　　2) 数据驱动<br />　　采用数据驱动而非硬编码的方式，许多策略变更和维护都会方便得多，大大增强系统的灵活性和扩展性。<br />　　举例，假如用户界面要访问外部数据源（文件、XML 文档、其他数据库等），不妨把相应的连接和路径信息存储在用户界面支持表里。还有，如果用户界面执行工作流之类的任务（发送邮件、打印信笺、修改记录状态等），那么产生工作流的数据也可以存放在数据库里。角色权限管理也可以通过数据驱动来完成。事实上，如果过程是数据驱动的，你就可以把相当大的责任推给用户，由用户来维护自己的工作流过程。<br />　　3) 考虑各种变化<br />　　在设计数据库的时候考虑到哪些数据字段将来可能会发生变更。<br />　　举例，姓氏就是如此（注意是西方人的姓氏，比如女性结婚后从夫姓等）。所以，在建立系统存储客户信息时，在单独的一个数据表里存储姓氏字段，而且还附加起始日和终止日等字段，这样就可以跟踪这一数据条目的变化。<br /><br />　　字段设计原则<br />　　4) 每个表中都应该添加的3 个有用的字段<br />　　•?dRecordCreationDate，在VB 下默认是Now()，而在SQL Server 下默认为GETDATE()<br />　　•?sRecordCreator，在SQL Server 下默认为NOT NULL DEFAULT USER<br />　　•?nRecordVersion，记录的版本标记；有助于准确说明记录中出现null 数据或者丢失数据的原因<br />　　5) 对地址和电话采用多个字段<br />　　描述街道地址就短短一行记录是不够的。Address_Line1、Address_Line2 和Address_Line3 可以提供更大的灵活性。还有，电话号码和邮件地址最好拥有自己的数据表，其间具有自身的类型和标记类别。<br />　　6) 使用角色实体定义属于某类别的列<br />　　在需要对属于特定类别或者具有特定角色的事物做定义时，可以用角色实体来创建特定的时间关联关系，从而可以实现自我文档化。<br />　　举例：用PERSON 实体和PERSON_TYPE 实体来描述人员。比方说，当John Smith, Engineer 提升为John Smith, Director 乃至最后爬到John Smith, CIO 的高位，而所有你要做的不过是改变两个表PERSON 和PERSON_TYPE 之间关系的键值，同时增加一个日期/时间字段来知道变化是何时发生的。这样，你的PERSON_TYPE 表就包含了所有PERSON 的可能类型，比如Associate、Engineer、Director、CIO 或者CEO 等。还有个替代办法就是改变PERSON 记录来反映新头衔的变化，不过这样一来在时间上无法跟踪个人所处位置的具体时间。<br />　　7) 选择数字类型和文本类型尽量充足<br />　　在SQL 中使用smallint 和tinyint 类型要特别小心。比如，假如想看看月销售总额，总额字段类型是smallint，那么，如果总额超过了$32,767 就不能进行计算操作了。<br />　　而ID 类型的文本字段，比如客户ID 或定单号等等都应该设置得比一般想象更大。假设客户ID 为10 位数长。那你应该把数据库表字段的长度设为12 或者13 个字符长。但这额外占据的空间却无需将来重构整个数据库就可以实现数据库规模的增长了。<br />　　8) 增加删除标记字段<br />　　在表中包含一个“删除标记”字段，这样就可以把行标记为删除。在关系数据库里不要单独删除某一行；最好采用清除数据程序而且要仔细维护索引整体性。 <br />　　3. 选择键和索引（数据库逻辑设计）<br />　　键选择原则：<br />　　1) 键设计4 原则<br />　　•?为关联字段创建外键。<br />　　•?所有的键都必须唯一。<br />　　•?避免使用复合键。<br />　　•?外键总是关联唯一的键字段。<br />　　2) 使用系统生成的主键<br />　　设计数据库的时候采用系统生成的键作为主键，那么实际控制了数据库的索引完整性。这样，数据库和非人工机制就有效地控制了对存储数据中每一行的访问。采用系统生成键作为主键还有一个优点：当拥有一致的键结构时，找到逻辑缺陷很容易。<br />　　3) 不要用用户的键(不让主键具有可更新性)<br />　　在确定采用什么字段作为表的键的时候，可一定要小心用户将要编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。<br />　　4) 可选键有时可做主键<br />　　把可选键进一步用做主键，可以拥有建立强大索引的能力。<br /><br />　　索引使用原则：<br />　　索引是从数据库中获取数据的最高效方式之一。95%的数据库性能问题都可以采用索引技术得到解决。<br />　　1) 逻辑主键使用唯一的成组索引，对系统键（作为存储过程）采用唯一的非成组索引，对任何外键列采用非成组索引。考虑数据库的空间有多大，表如何进行访问，还有这些访问是否主要用作读写。<br />　　2) 大多数数据库都索引自动创建的主键字段，但是可别忘了索引外键，它们也是经常使用的键，比如运行查询显示主表和所有关联表的某条记录就用得上。<br />　　3) 不要索引memo/note 字段，不要索引大型字段（有很多字符），这样作会让索引占用太多的存储空间。<br />　　4) 不要索引常用的小型表<br />　　不要为小型数据表设置任何键，假如它们经常有插入和删除操作就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。<br /><br />　　4. 数据完整性设计（数据库逻辑设计）<br />　　1) 完整性实现机制：<br />　　实体完整性：主键<br />　　参照完整性：<br />　　父表中删除数据：级联删除；受限删除；置空值<br />　　父表中插入数据：受限插入；递归插入<br />　　父表中更新数据：级联更新；受限更新；置空值<br />　　DBMS对参照完整性可以有两种方法实现：外键实现机制（约束规则）和触发器实现机制<br />　　用户定义完整性：<br />　　NOT NULL；CHECK；触发器<br />　　2) 用约束而非商务规则强制数据完整性<br />　　采用数据库系统实现数据的完整性。这不但包括通过标准化实现的完整性而且还包括数据的功能性。在写数据的时候还可以增加触发器来保证数据的正确性。不要依赖于商务层保证数据完整性；它不能保证表之间（外键）的完整性所以不能强加于其他完整性规则之上。<br />　　3) 强制指示完整性<br />　　在有害数据进入数据库之前将其剔除。激活数据库系统的指示完整性特性。这样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条件。<br />　　4) 使用查找控制数据完整性<br />　　控制数据完整性的最佳方式就是限制用户的选择。只要有可能都应该提供给用户一个清晰的价值列表供其选择。这样将减少键入代码的错误和误解同时提供数据的一致性。某些公共数据特别适合查找：国家代码、状态代码等。<br />　　5) 采用视图<br />　　为了在数据库和应用程序代码之间提供另一层抽象，可以为应用程序建立专门的视图而不必非要应用程序直接访问数据表。这样做还等于在处理数据库变更时给你提供了更多的自由。<br />　　5. 其他设计技巧<br />　　1) 避免使用触发器<br />　　触发器的功能通常可以用其他方式实现。在调试程序时触发器可能成为干扰。假如你确实需要采用触发器，你最好集中对它文档化。<br />　　2) 使用常用英语（或者其他任何语言）而不要使用编码<br />　　在创建下拉菜单、列表、报表时最好按照英语名排序。假如需要编码，可以在编码旁附上用户知道的英语。<br />　　3) 保存常用信息<br />　　让一个表专门存放一般数据库信息非常有用。在这个表里存放数据库当前版本、最近检查/修复（对Access）、关联设计文档的名称、客户等信息。这样可以实现一种简单机制跟踪数据库，当客户抱怨他们的数据库没有达到希望的要求而与你联系时，这样做对非客户机/服务器环境特别有用。<br />　　4) 包含版本机制<br />　　在数据库中引入版本控制机制来确定使用中的数据库的版本。时间一长，用户的需求总是会改变的。最终可能会要求修改数据库结构。把版本信息直接存放到数据库中更为方便。 <br />　　5) 编制文档<br />　　对所有的快捷方式、命名规范、限制和函数都要编制文档。<br />　　采用给表、列、触发器等加注释的数据库工具。对开发、支持和跟踪修改非常有用。<br />　　对数据库文档化，或者在数据库自身的内部或者单独建立文档。这样，当过了一年多时间后再回过头来做第2 个版本，犯错的机会将大大减少。<br />　　6) 测试、测试、反复测试<br />　　建立或者修订数据库之后，必须用用户新输入的数据测试数据字段。最重要的是，让用户进行测试并且同用户一道保证选择的数据类型满足商业要求。测试需要在把新数据库投入实际服务之前完成。<br />　　7) 检查设计<br />　　在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原型检查数据库。换句话说，针对每一种最终表达数据的原型应用，保证你检查了数据模型并且查看如何取出数据。<br />　　三、数据库命名规范<br />　　1. 实体（表）的命名<br />　　1) 表以名词或名词短语命名，确定表名是采用复数还是单数形式，此外给表的别名定义简单规则（比方说，如果表名是一个单词，别名就取单词的前4 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成4 个字母长的别名；如果表的名字由3 个单词组成，从头两个单词中各取一个然后从最后一个单词中再取出两个字母，结果还是组成4 字母长的别名，其余依次类推）<br />　　对工作用表来说，表名可以加上前缀WORK_ 后面附上采用该表的应用程序的名字。在命名过程当中，根据语义拼凑缩写即可。注意，由于ORCLE会将字段名称统一成大写或者小写中的一种，所以要求加上下划线。<br />　　举例：<br />　　定义的缩写 Sales: Sal 销售；<br />　　Order: Ord 订单；<br />　　Detail: Dtl 明细；<br />　　则销售订单明细表命名为：Sal_Ord_Dtl;<br />　　2) 如果表或者是字段的名称仅有一个单词，那么建议不使用缩写，而是用完整的单词。<br />　　举例：<br />　　定义的缩写 Material Ma 物品；<br />　　物品表名为：Material, 而不是 Ma.<br />　　但是字段物品编码则是：Ma_ID;而不是Material_ID<br />　　3) 所有的存储值列表的表前面加上前缀Z<br />　　目的是将这些值列表类排序在数据库最后。<br />　　4) 所有的冗余类的命名(主要是累计表)前面加上前缀X<br />　　冗余类是为了提高数据库效率，非规范化数据库的时候加入的字段或者表<br />　　5) 关联类通过用下划线连接两个基本类之后，再加前缀R的方式命名,后面按照字母顺序罗列两个表名或者表名的缩写。<br />　　关联表用于保存多对多关系。<br />　　如果被关联的表名大于10个字母，必须将原来的表名的进行缩写。如果没有其他原因，建议都使用缩写。<br />　　举例：表Object与自身存在多对多的关系,则保存多对多关系的表命名为：R_Object；<br />　　表 Depart和Employee;存在多对多的关系；则关联表命名为R_Dept_Emp<br />　　2. 属性（列）的命名<br />　　1) 采用有意义的列名，表内的列要针对键采用一整套设计规则。每一个表都将有一个自动ID作为主健,逻辑上的主健作为第一组候选主健来定义,如果是数据库自动生成的编码，统一命名为：ID;如果是自定义的逻辑上的编码则用缩写加“ID”的方法命名。如果键是数字类型，你可以用_NO 作为后缀；如果是字符类型则可以采用_CODE 后缀。对列名应该采用标准的前缀和后缀。<br />　　举例：销售订单的编号字段命名：Sal_Ord_ID；如果还存在一个数据库生成的自动编号，则命名为：ID。<br />　　2) 所有的属性加上有关类型的后缀，注意，如果还需要其它的后缀，都放在类型后缀之前。<br />　　注: 数据类型是文本的字段，类型后缀TX可以不写。有些类型比较明显的字段，可以不写类型后缀。<br />　　3) 采用前缀命名<br />　　给每个表的列名都采用统一的前缀，那么在编写SQL表达式的时候会得到大大的简化。这样做也确实有缺点，比如破坏了自动表连接工具的作用，后者把公共列名同某些数据库联系起来。<br />　　3. 视图的命名<br />　　1) 视图以V作为前缀，其他命名规则和表的命名类似；<br />　　2) 命名应尽量体现各视图的功能。<br />　　4. 触发器的命名<br />　　触发器以TR作为前缀，触发器名为相应的表名加上后缀，Insert触发器加"_I"，Delete触发器加"_D"，Update触发器加"_U"，如：<br /><br />　　TR_Customer_I，TR_Customer_D，TR_Customer_U。<br />　　5. 存储过程名<br />　　存储过程应以"UP_"开头，和系统的存储过程区分，后续部分主要以动宾形式构成，并用下划线分割各个组成部分。如增加代理商的帐户的存储过程为"UP_Ins_Agent_Account"。<br />　　6. 变量名<br />　　变量名采用小写，若属于词组形式，用下划线分隔每个单词，如@my_err_no。<br />　　7. 命名中其他注意事项<br />　　1) 以上命名都不得超过30个字符的系统限制。变量名的长度限制为29（不包括标识字符@）。<br />　　2) 数据对象、变量的命名都采用英文字符，禁止使用中文命名。绝对不要在对象名的字符之间留空格。<br />　　3) 小心保留词，要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突<br />　　5) 保持字段名和类型的一致性，在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据类型在一个表里是整数，那在另一个表里可就别变成字符型了。</font>
		<br />
<img src ="http://www.blogjava.net/nbtymm/aggbug/62136.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 11:46 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62136.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PowerDesigner设计数据库经验</title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62133.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 03:42:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62133.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62133.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62133.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62133.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62133.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: (转载自--http://www.cnblogs.com/qiubole/articles/116152.html)																				采用								PowerDesigner								设计数据库																																																												...&nbsp;&nbsp;<a href='http://www.blogjava.net/nbtymm/archive/2006/08/07/62133.html'>阅读全文</a><img src ="http://www.blogjava.net/nbtymm/aggbug/62133.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 11:42 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62133.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库设计经验 </title><link>http://www.blogjava.net/nbtymm/archive/2006/08/07/62128.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 07 Aug 2006 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/08/07/62128.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/62128.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/08/07/62128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/62128.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/62128.html</trackback:ping><description><![CDATA[
		<p>(转自--http://www.cnblogs.com/qiubole/articles/157312.html)<br />一个成功的管理系统，是由：[50% 的业务 + 50% 的软件] 所组成，而 50% 的成功软件又有 [25% 的数据库 + 25% 的程序] 所组成，数据库设计的好坏是一个关键。如果把企业的数据比做生命所必需的血液，那么数据库的设计就是应用中最重要的一部分。有关数据库设计的材料汗牛充栋，大学学位课程里也有专门的讲述。不过，就如我们反复强调的那样，再好的老师也比不过经验的教诲。所以我归纳历年来所走的弯路及体会，并在网上找了些对数据库设计颇有造诣的专业人士给大家传授一些设计数据库的技巧和经验。精选了其中的 60 个最佳技巧，并把这些技巧编写成了本文，为了方便索引其内容划分为 5 个部分：</p>
		<p>第 1 部分 - 设计数据库之前<br />这一部分罗列了 12 个基本技巧，包括命名规范和明确业务需求等。 <br />第 2 部分 - 设计数据库表<br />总共 24 个指南性技巧，涵盖表内字段设计以及应该避免的常见问题等。 <br />第 3 部分 - 选择键<br />怎么选择键呢？这里有 10 个技巧专门涉及系统生成的主键的正确用法，还有何 时以及如何索引字段以获得最佳性能等。 <br />第 4 部分 - 保证数据完整性<br />讨论如何保持数据库的清晰和健壮，如何把有害数据降低到最小程度。 <br />第 5 部分 - 各种小技巧<br />不包括在以上 4 个部分中的其他技巧，五花八门，有了它们希望你的数据库开发工作会更轻松一些。 <br />第 1 部分 - 设计数据库之前<br />考察现有环境<br />在设计一个新数据库时，你不但应该仔细研究业务需求而且还要考察现有的系统。大多数数据库项目都不是从头开始建立的；通常，机构内总会存在用来满足特定需求的现有系统（可能没有实现自动计算）。显然，现有系统并不完美，否则你就不必再建立新系统了。但是对旧系统的研究可以让你发现一些可能会忽略的细微问题。一般来说，考察现有系统对你绝对有好处。 <br />定义标准的对象命名规范<br />一定要定义数据库对象的命名规范。对数据库表来说，从项目一开始就要确定表名是采用复数还是单数形式。此外还要给表的别名定义简单规则（比方说，如果表名是一个单词，别名就取单词的前 4 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成 4 个字母长的别名；如果表的名字由 3 个单词组成，你不妨从头两个单词中各取一个然后从最后一个单词中再取出两个字母，结果还是组成 4 字母长的别名，其余依次类推）对工作用表来说，表名可以加上前缀 WORK_ 后面附上采用该表的应用程序的名字。表内的列[字段]要针对键采用一整套设计规则。比如，如果键是数字类型，你可以用 _N 作为后缀；如果是字符类型则可以采用 _C 后缀。对列[字段]名应该采用标准的前缀和后缀。再如，假如你的表里有好多“money”字段，你不妨给每个列[字段]增加一个 _M 后缀。还有，日期列[字段]最好以 D_ 作为名字打头。</p>
		<p>检查表名、报表名和查询名之间的命名规范。你可能会很快就被这些不同的数据库要素的名称搞糊涂了。假如你坚持统一地命名这些数据库的不同组成部分，至少你应该在这些对象名字的开头用 Table、Query 或者 Report 等前缀加以区别。</p>
		<p>如果采用了 Microsoft Access，你可以用 qry、rpt、tbl 和 mod 等符号来标识对象（比如 tbl_Employees）。我在和 SQL Server 打交道的时候还用过 tbl 来索引表，但我用 sp_company （现在用 sp_feft_）标识存储过程，因为在有的时候如果我发现了更好的处理办法往往会保存好几个拷贝。我在实现 SQL Server 2000 时用 udf_ （或者类似的标记）标识我编写的函数。 <br />工欲善其事, 必先利其器<br />采用理想的数据库设计工具，比如：SyBase 公司的 PowerDesign，她支持 PB、VB、Delphe 等语言，通过 ODBC 可以连接市面上流行的 30 多个数据库，包括 dBase、FoxPro、VFP、SQL Server 等，今后有机会我将着重介绍 PowerDesign 的使用。 <br />获取数据模式资源手册<br />正在寻求示例模式的人可以阅读《数据模式资源手册》一书，该书由 Len Silverston、W. H. Inmon 和 Kent Graziano 编写，是一本值得拥有的最佳数据建模图书。该书包括的章节涵盖多种数据领域，比如人员、机构和工作效能等。其他的你还可以参考：[1]萨师煊　王珊著　数据库系统概论(第二版)高等教育出版社 1991、[2][美] Steven M.Bobrowski 著 Oracle 7 与客户／服务器计算技术从入门到精通　刘建元等译　电子工业出版社，1996、[3]周中元　信息系统建模方法(下)　电子与信息化　1999年第3期，1999 <br />畅想未来，但不可忘了过去的教训<br />我发现询问用户如何看待未来需求变化非常有用。这样做可以达到两个目的：首先，你可以清楚地了解应用设计在哪个地方应该更具灵活性以及如何避免性能瓶颈；其次，你知道发生事先没有确定的需求变更时用户将和你一样感到吃惊。</p>
		<p>一定要记住过去的经验教训！我们开发人员还应该通过分享自己的体会和经验互相帮助。即使用户认为他们再也不需要什么支持了，我们也应该对他们进行这方面的教育，我们都曾经面临过这样的时刻“当初要是这么做了该多好..”。 <br />在物理实践之前进行逻辑设计<br />在深入物理设计之前要先进行逻辑设计。随着大量的 CASE 工具不断涌现出来，你的设计也可以达到相当高的逻辑水准，你通常可以从整体上更好地了解数据库设计所需要的方方面面。 <br />了解你的业务<br />在你百分百地确定系统从客户角度满足其需求之前不要在你的 ER（实体关系）模式中加入哪怕一个数据表（怎么，你还没有模式？那请你参看技巧 9）。了解你的企业业务可以在以后的开发阶段节约大量的时间。一旦你明确了业务需求，你就可以自己做出许多决策了。</p>
		<p>一旦你认为你已经明确了业务内容，你最好同客户进行一次系统的交流。采用客户的术语并且向他们解释你所想到的和你所听到的。同时还应该用可能、将会和必须等词汇表达出系统的关系基数。这样你就可以让你的客户纠正你自己的理解然后做好下一步的 ER 设计。 <br />创建数据字典和 ER 图表<br />一定要花点时间创建 ER 图表和数据字典。其中至少应该包含每个字段的数据类型和在每个表内的主外键。创建 ER 图表和数据字典确实有点费时但对其他开发人员要了解整个设计却是完全必要的。越早创建越能有助于避免今后面临的可能混乱，从而可以让任何了解数据库的人都明确如何从数据库中获得数据。</p>
		<p>有一份诸如 ER 图表等最新文档其重要性如何强调都不过分，这对表明表之间关系很有用，而数据字典则说明了每个字段的用途以及任何可能存在的别名。对 SQL 表达式的文档化来说这是完全必要的。 <br />创建模式<br />一张图表胜过千言万语：开发人员不仅要阅读和实现它，而且还要用它来帮助自己和用户对话。模式有助于提高协作效能，这样在先期的数据库设计中几乎不可能出现大的问题。模式不必弄的很复杂；甚至可以简单到手写在一张纸上就可以了。只是要保证其上的逻辑关系今后能产生效益。 <br />从输入输出下手<br />在定义数据库表和字段需求（输入）时，首先应检查现有的或者已经设计出的报表、查询和视图（输出）以决定为了支持这些输出哪些是必要的表和字段。举个简单的例子：假如客户需要一个报表按照邮政编码排序、分段和求和，你要保证其中包括了单独的邮政编码字段而不要把邮政编码糅进地址字段里。 <br />报表技巧<br />要了解用户通常是如何报告数据的：批处理还是在线提交报表？时间间隔是每天、每周、每月、每个季度还是每年？如果需要的话还可以考虑创建总结表。系统生成的主键在报表中很难管理。用户在具有系统生成主键的表内用副键进行检索往往会返回许多重复数据。这样的检索性能比较低而且容易引起混乱。 <br />理解客户需求<br />看起来这应该是显而易见的事，但需求就是来自客户（这里要从内部和外部客户的角度考虑）。不要依赖用户写下来的需求，真正的需求在客户的脑袋里。你要让客户解释其需求，而且随着开发的继续，还要经常询问客户保证其需求仍然在开发的目的之中。一个不变的真理是：“只有我看见了我才知道我想要的是什么”必然会导致大量的返工，因为数据库没有达到客户从来没有写下来的需求标准。而更糟的是你对他们需求的解释只属于你自己，而且可能是完全错误的。 <br />第 2 部分 - 设计表和字段<br />检查各种变化<br />我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变更。比方说，姓氏就是如此（注意是西方人的姓氏，比如女性结婚后从夫姓等）。所以，在建立系统存储客户信息时，我倾向于在单独的一个数据表里存储姓氏字段，而且还附加起始日和终止日等字段，这样就可以跟踪这一数据条目的变化。 <br />采用有意义的字段名<br />有一回我参加开发过一个项目，其中有从其他程序员那里继承的程序，那个程序员喜欢用屏幕上显示数据指示用语命名字段，这也不赖，但不幸的是，她还喜欢用一些奇怪的命名法，其命名采用了匈牙利命名和控制序号的组合形式，比如 cbo1、txt2、txt2_b 等等。<br />除非你在使用只面向你的缩写字段名的系统，否则请尽可能地把字段描述的清楚些。当然，也别做过头了，比如 Customer_Shipping_Address_Street_Line_1，虽然很富有说明性，但没人愿意键入这么长的名字，具体尺度就在你的把握中。 <br />采用前缀命名<br />如果多个表里有好多同一类型的字段（比如 FirstName），你不妨用特定表的前缀（比如 CusLastName）来帮助你标识字段。</p>
		<p>时效性数据应包括“最近更新日期/时间”字段。时间标记对查找数据问题的原因、按日期重新处理/重载数据和清除旧数据特别有用。 <br />标准化和数据驱动<br />数据的标准化不仅方便了自己而且也方便了其他人。比方说，假如你的用户界面要访问外部数据源（文件、XML 文档、其他数据库等），你不妨把相应的连接和路径信息存储在用户界面支持表里。还有，如果用户界面执行工作流之类的任务（发送邮件、打印信笺、修改记录状态等），那么产生工作流的数据也可以存放在数据库里。预先安排总需要付出努力，但如果这些过程采用数据驱动而非硬编码的方式，那么策略变更和维护都会方便得多。事实上，如果过程是数据驱动的，你就可以把相当大的责任推给用户，由用户来维护自己的工作流过程。 <br />标准化不能过头<br />对那些不熟悉标准化一词（normalization）的人而言，标准化可以保证表内的字段都是最基础的要素，而这一措施有助于消除数据库中的数据冗余。标准化有好几种形式，但 Third Normal Form（3NF）通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说，3NF 规定：<br />* 表内的每一个值都只能被表达一次。<br />* 表内的每一行都应该被唯一的标识（有唯一键）。<br />* 表内不应该存储依赖于其他键的非键信息。<br />遵守 3NF 标准的数据库具有以下特点：有一组表专门存放通过键连接起来的关联数据。比方说，某个存放客户及其有关定单的 3NF 数据库就可能有两个表：Customer 和 Order。Order 表不包含定单关联客户的任何信息，但表内会存放一个键值，该键指向 Customer 表里包含该客户信息的那一行。<br />更高层次的标准化也有，但更标准是否就一定更好呢？答案是不一定。事实上，对某些项目来说，甚至就连 3NF 都可能给数据库引入太高的复杂性。</p>
		<p>为了效率的缘故，对表不进行标准化有时也是必要的，这样的例子很多。曾经有个开发餐饮分析软件的活就是用非标准化表把查询时间从平均 40 秒降低到了两秒左右。虽然我不得不这么做，但我绝不把数据表的非标准化当作当然的设计理念。而具体的操作不过是一种派生。所以如果表出了问题重新产生非标准化的表是完全可能的。 <br />Microsoft Visual FoxPro 报表技巧<br />如果你正在使用 Microsoft Visual FoxPro，你可以用对用户友好的字段名来代替编号的名称：比如用 Customer Name 代替 txtCNaM。这样，当你用向导程序 [Wizards，台湾人称为‘精灵’] 创建表单和报表时，其名字会让那些不是程序员的人更容易阅读。 <br />不活跃或者不采用的指示符<br />增加一个字段表示所在记录是否在业务中不再活跃挺有用的。不管是客户、员工还是其他什么人，这样做都能有助于再运行查询的时候过滤活跃或者不活跃状态。同时还消除了新用户在采用数据时所面临的一些问题，比如，某些记录可能不再为他们所用，再删除的时候可以起到一定的防范作用。 <br />使用角色实体定义属于某类别的列[字段]<br />在需要对属于特定类别或者具有特定角色的事物做定义时，可以用角色实体来创建特定的时间关联关系，从而可以实现自我文档化。<br />这里的含义不是让 PERSON 实体带有 Title 字段，而是说，为什么不用 PERSON 实体和 PERSON_TYPE 实体来描述人员呢？比方说，当 John Smith, Engineer 提升为 John Smith, Director 乃至最后爬到 John Smith, CIO 的高位，而所有你要做的不过是改变两个表 PERSON 和 PERSON_TYPE 之间关系的键值，同时增加一个日期/时间字段来知道变化是何时发生的。这样，你的 PERSON_TYPE 表就包含了所有 PERSON 的可能类型，比如 Associate、Engineer、Director、CIO 或者 CEO 等。<br />还有个替代办法就是改变 PERSON 记录来反映新头衔的变化，不过这样一来在时间上无法跟踪个人所处位置的具体时间。 <br />采用常用实体命名机构数据<br />组织数据的最简单办法就是采用常用名字，比如：PERSON、ORGANIZATION、ADDRESS 和 PHONE 等等。当你把这些常用的一般名字组合起来或者创建特定的相应副实体时，你就得到了自己用的特殊版本。开始的时候采用一般术语的主要原因在于所有的具体用户都能对抽象事物具体化。<br />有了这些抽象表示，你就可以在第 2 级标识中采用自己的特殊名称，比如，PERSON 可能是 Employee、Spouse、Patient、Client、Customer、Vendor 或者 Teacher 等。同样的，ORGANIZATION 也可能是 MyCompany、MyDepartment、Competitor、Hospital、Warehouse、Government 等。最后 ADDRESS 可以具体为 Site、Location、Home、Work、Client、Vendor、Corporate 和 FieldOffice 等。<br />采用一般抽象术语来标识“事物”的类别可以让你在关联数据以满足业务要求方面获得巨大的灵活性，同时这样做还可以显著降低数据存储所需的冗余量。 <br />用户来自世界各地<br />在设计用到网络或者具有其他国际特性的数据库时，一定要记住大多数国家都有不同的字段格式，比如邮政编码等，有些国家，比如新西兰就没有邮政编码一说。 <br />数据重复需要采用分立的数据表<br />如果你发现自己在重复输入数据，请创建新表和新的关系。 <br />每个表中都应该添加的 3 个有用的字段<br />* dRecordCreationDate，在 VB 下默认是 Now()，而在 SQL Server 下默认为 GETDATE()<br />* sRecordCreator，在 SQL Server 下默认为 NOT NULL DEFAULT USER<br />* nRecordVersion，记录的版本标记；有助于准确说明记录中出现 null 数据或者丢失数据的原因 <br />对地址和电话采用多个字段<br />描述街道地址就短短一行记录是不够的。Address_Line1、Address_Line2 和 Address_Line3 可以提供更大的灵活性。还有，电话号码和邮件地址最好拥有自己的数据表，其间具有自身的类型和标记类别。</p>
		<p>过分标准化可要小心，这样做可能会导致性能上出现问题。虽然地址和电话表分离通常可以达到最佳状态，但是如果需要经常访问这类信息，或许在其父表中存放“首选”信息（比如 Customer 等）更为妥当些。非标准化和加速访问之间的妥协是有一定意义的。 <br />使用多个名称字段<br />我觉得很吃惊，许多人在数据库里就给 name 留一个字段。我觉得只有刚入门的开发人员才会这么做，但实际上网上这种做法非常普遍。我建议应该把姓氏和名字当作两个字段来处理，然后在查询的时候再把他们组合起来。</p>
		<p>我最常用的是在同一表中创建一个计算列[字段]，通过它可以自动地连接标准化后的字段，这样数据变动的时候它也跟着变。不过，这样做在采用建模软件时得很机灵才行。总之，采用连接字段的方式可以有效的隔离用户应用和开发人员界面。 <br />提防大小写混用的对象名和特殊字符<br />过去最令我恼火的事情之一就是数据库里有大小写混用的对象名，比如 CustomerData。这一问题从 Access 到 Oracle 数据库都存在。我不喜欢采用这种大小写混用的对象命名方法，结果还不得不手工修改名字。想想看，这种数据库/应用程序能混到采用更强大数据库的那一天吗？采用全部大写而且包含下划符的名字具有更好的可读性（CUSTOMER_DATA），绝对不要在对象名的字符之间留空格。 <br />小心保留词<br />要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突，比如，最近我编写的一个 ODBC 连接程序里有个表，其中就用了 DESC 作为说明字段名。后果可想而知！DESC 是 DESCENDING 缩写后的保留词。表里的一个 SELECT * 语句倒是能用，但我得到的却是一大堆毫无用处的信息。 <br />保持字段名和类型的一致性<br />在命名字段并为其指定数据类型的时候一定要保证一致性。假如字段在某个表中叫做“agreement_number”，你就别在另一个表里把名字改成“ref1”。假如数据类型在一个表里是整数，那在另一个表里可就别变成字符型了。记住，你干完自己的活了，其他人还要用你的数据库呢。 <br />仔细选择数字类型<br />在 SQL 中使用 smallint 和 tinyint 类型要特别小心，比如，假如你想看看月销售总额，你的总额字段类型是 smallint，那么，如果总额超过了 $32,767 你就不能进行计算操作了。 <br />删除标记<br />在表中包含一个“删除标记”字段，这样就可以把行标记为删除。在关系数据库里不要单独删除某一行；最好采用清除数据程序而且要仔细维护索引整体性。 <br />避免使用触发器<br />触发器的功能通常可以用其他方式实现。在调试程序时触发器可能成为干扰。假如你确实需要采用触发器，你最好集中对它文档化。 <br />包含版本机制<br />建议你在数据库中引入版本控制机制来确定使用中的数据库的版本。无论如何你都要实现这一要求。时间一长，用户的需求总是会改变的。最终可能会要求修改数据库结构。虽然你可以通过检查新字段或者索引来确定数据库结构的版本，但我发现把版本信息直接存放到数据库中不更为方便吗？。 <br />给文本字段留足余量<br />ID 类型的文本字段，比如客户 ID 或定单号等等都应该设置得比一般想象更大，因为时间不长你多半就会因为要添加额外的字符而难堪不已。比方说，假设你的客户 ID 为 10 位数长。那你应该把数据库表字段的长度设为 12 或者 13 个字符长。这算浪费空间吗？是有一点，但也没你想象的那么多：一个字段加长 3 个字符在有 1 百万条记录，再加上一点索引的情况下才不过让整个数据库多占据 3MB 的空间。但这额外占据的空间却无需将来重构整个数据库就可以实现数据库规模的增长了。身份证的号码从 15 位变成 18 位就是最好和最惨痛的例子。 <br />列[字段]命名技巧<br />我们发现，假如你给每个表的列[字段]名都采用统一的前缀，那么在编写 SQL 表达式的时候会得到大大的简化。这样做也确实有缺点，比如破坏了自动表连接工具的作用，后者把公共列[字段]名同某些数据库联系起来，不过就连这些工具有时不也连接错误嘛。举个简单的例子，假设有两个表：<br />Customer 和 Order。Customer 表的前缀是 cu_，所以该表内的子段名如下：cu_name_id、cu_surname、cu_initials 和cu_address 等。Order 表的前缀是 or_，所以子段名是：<br />or_order_id、or_cust_name_id、or_quantity 和 or_description 等。<br />这样从数据库中选出全部数据的 SQL 语句可以写成如下所示：<br />Select * From Customer, Order Where cu_surname = "MYNAME" ;<br />and cu_name_id = or_cust_name_id and or_quantity = 1<br />在没有这些前缀的情况下则写成这个样子（用别名来区分）：<br />Select * From Customer, Order Where Customer.surname = "MYNAME" ;<br />and Customer.name_id = Order.cust_name_id and Order.quantity = 1<br />第 1 个 SQL 语句没少键入多少字符。但如果查询涉及到 5 个表乃至更多的列[字段]你就知道这个技巧多有用了。 <br />第 3 部分 - 选择键和索引<br />数据采掘要预先计划<br />我所在的某一客户部门一度要处理 8 万多份联系方式，同时填写每个客户的必要数据（这绝对不是小活）。我从中还要确定出一组客户作为市场目标。当我从最开始设计表和字段的时候，我试图不在主索引里增加太多的字段以便加快数据库的运行速度。然后我意识到特定的组查询和信息采掘既不准确速度也不快。结果只好在主索引中重建而且合并了数据字段。我发现有一个指示计划相当关键——当我想创建系统类型查找时为什么要采用号码作为主索引字段呢？我可以用传真号码进行检索，但是它几乎就象系统类型一样对我来说并不重要。采用后者作为主字段，数据库更新后重新索引和检索就快多了。</p>
		<p>可操作数据仓库（ODS）和数据仓库（DW）这两种环境下的数据索引是有差别的。在 DW 环境下，你要考虑销售部门是如何组织销售活动的。他们并不是数据库管理员，但是他们确定表内的键信息。这里设计人员或者数据库工作人员应该分析数据库结构从而确定出性能和正确输出之间的最佳条件。 <br />使用系统生成的主键<br />这类同技巧 1，但我觉得有必要在这里重复提醒大家。假如你总是在设计数据库的时候采用系统生成的键作为主键，那么你实际控制了数据库的索引完整性。这样，数据库和非人工机制就有效地控制了对存储数据中每一行的访问。<br />采用系统生成键作为主键还有一个优点：当你拥有一致的键结构时，找到逻辑缺陷很容易。 <br />分解字段用于索引<br />为了分离命名字段和包含字段以支持用户定义的报表，请考虑分解其他字段（甚至主键）为其组成要素以便用户可以对其进行索引。索引将加快 SQL 和报表生成器脚本的执行速度。比方说，我通常在必须使用 SQL LIKE 表达式的情况下创建报表，因为 case number 字段无法分解为 year、serial number、case type 和 defendant code 等要素。性能也会变坏。假如年度和类型字段可以分解为索引字段那么这些报表运行起来就会快多了。 <br />键设计 4 原则<br />* 为关联字段创建外键。<br />* 所有的键都必须唯一。<br />* 避免使用复合键。<br />* 外键总是关联唯一的键字段。 <br />别忘了索引<br />索引是从数据库中获取数据的最高效方式之一。95% 的数据库性能问题都可以采用索引技术得到解决。作为一条规则，我通常对逻辑主键使用唯一的成组索引，对系统键（作为存储过程）采用唯一的非成组索引，对任何外键列[字段]采用非成组索引。不过，索引就象是盐，太多了菜就咸了。你得考虑数据库的空间有多大，表如何进行访问，还有这些访问是否主要用作读写。</p>
		<p>大多数数据库都索引自动创建的主键字段，但是可别忘了索引外键，它们也是经常使用的键，比如运行查询显示主表和所有关联表的某条记录就用得上。还有，不要索引 memo/note 字段，不要索引大型字段（有很多字符），这样作会让索引占用太多的存储空间。 <br />不要索引常用的小型表<br />不要为小型数据表设置任何键，假如它们经常有插入和删除操作就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。 <br />不要把社会保障号码（SSN）或身份证号码（ID）选作键<br />永远都不要使用 SSN 或 ID 作为数据库的键。除了隐私原因以外，须知政府越来越趋向于不准许把 SSN 或 ID 用作除收入相关以外的其他目的，SSN 或 ID 需要手工输入。永远不要使用手工输入的键作为主键，因为一旦你输入错误，你唯一能做的就是删除整个记录然后从头开始。</p>
		<p>我在破解他人的程序时候，我看到很多人把 SSN 或 ID 还曾被用做系列号，当然尽管这么做是非法的。而且人们也都知道这是非法的，但他们已经习惯了。后来，随着盗取身份犯罪案件的增加，我现在的同行正痛苦地从一大摊子数据中把 SSN 或 ID 删除。 <br />不要用用户的键<br />在确定采用什么字段作为表的键的时候，可一定要小心用户将要编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。这样做会迫使你采取以下两个措施：<br />* 在创建记录之后对用户编辑字段的行为施加限制。假如你这么做了，你可能会发现你的应用程序在商务需求突然发生变化，而用户需要编辑那些不可编辑的字段时缺乏足够的灵活性。当用户在输入数据之后直到保存记录才发现系统出了问题他们该怎么想？删除重建？假如记录不可重建是否让用户走开？<br />* 提出一些检测和纠正键冲突的方法。通常，费点精力也就搞定了，但是从性能上来看这样做的代价就比较大了。还有，键的纠正可能会迫使你突破你的数据和商业/用户界面层之间的隔离。<br />所以还是重提一句老话：你的设计要适应用户而不是让用户来适应你的设计。</p>
		<p>不让主键具有可更新性的原因是在关系模式下，主键实现了不同表之间的关联。比如，Customer 表有一个主键 CustomerID，而客户的定单则存放在另一个表里。Order 表的主键可能是 OrderNo 或者 OrderNo、CustomerID 和日期的组合。不管你选择哪种键设置，你都需要在 Order 表中存放 CustomerID 来保证你可以给下定单的用户找到其定单记录。<br />假如你在 Customer 表里修改了 CustomerID，那么你必须找出 Order 表中的所有相关记录对其进行修改。否则，有些定单就会不属于任何客户——数据库的完整性就算完蛋了。<br />如果索引完整性规则施加到表一级，那么在不编写大量代码和附加删除记录的情况下几乎不可能改变某一条记录的键和数据库内所有关联的记录。而这一过程往往错误丛生所以应该尽量避免。 <br />可选键(候选键)有时可做主键<br />记住，查询数据的不是机器而是人。<br />假如你有可选键，你可能进一步把它用做主键。那样的话，你就拥有了建立强大索引的能力。这样可以阻止使用数据库的人不得不连接数据库从而恰当的过滤数据。在严格控制域表的数据库上，这种负载是比较醒目的。如果可选键真正有用，那就是达到了主键的水准。<br />我的看法是，假如你有可选键，比如国家表内的 state_code，你不要在现有不能变动的唯一键上创建后续的键。你要做的无非是创建毫无价值的数据。如你因为过度使用表的后续键[别名]建立这种表的关联，操作负载真得需要考虑一下了。 <br />别忘了外键<br />大多数数据库索引自动创建的主键字段。但别忘了索引外键字段，它们在你想查询主表中的记录及其关联记录时每次都会用到。还有，不要索引 memo/notes 字段而且不要索引大型文本字段（许多字符），这样做会让你的索引占据大量的数据库空间。 <br />第 4 部分 - 保证数据的完整性<br />用约束而非商务规则强制数据完整性<br />如果你按照商务规则来处理需求，那么你应当检查商务层次/用户界面：如果商务规则以后发生变化，那么只需要进行更新即可。假如需求源于维护数据完整性的需要，那么在数据库层面上需要施加限制条件。如果你在数据层确实采用了约束，你要保证有办法把更新不能通过约束检查的原因采用用户理解的语言通知用户界面。除非你的字段命名很冗长，否则字段名本身还不够。</p>
		<p>只要有可能，请采用数据库系统实现数据的完整性。这不但包括通过标准化实现的完整性而且还包括数据的功能性。在写数据的时候还可以增加触发器来保证数据的正确性。不要依赖于商务层保证数据完整性；它不能保证表之间（外键）的完整性所以不能强加于其他完整性规则之上。 <br />分布式数据系统<br />对分布式系统而言，在你决定是否在各个站点复制所有数据还是把数据保存在一个地方之前应该估计一下未来 5 年或者 10 年的数据量。当你把数据传送到其他站点的时候，最好在数据库字段中设置一些标记。在目的站点收到你的数据之后更新你的标记。为了进行这种数据传输，请写下你自己的批处理或者调度程序以特定时间间隔运行而不要让用户在每天的工作后传输数据。本地拷贝你的维护数据，比如计算常数和利息率等，设置版本号保证数据在每个站点都完全一致。 <br />强制指示完整性(参照完整性?)<br />没有好办法能在有害数据进入数据库之后消除它，所以你应该在它进入数据库之前将其剔除。激活数据库系统的指示完整性特性。这样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条件。 <br />关系<br />如果两个实体之间存在多对一关系，而且还有可能转化为多对多关系，那么你最好一开始就设置成多对多关系。从现有的多对一关系转变为多对多关系比一开始就是多对多关系要难得多。 <br />采用视图<br />为了在你的数据库和你的应用程序代码之间提供另一层抽象，你可以为你的应用程序建立专门的视图而不必非要应用程序直接访问数据表。这样做还等于在处理数据库变更时给你提供了更多的自由。 <br />给数据保有和恢复制定计划<br />考虑数据保有策略并包含在设计过程中，预先设计你的数据恢复过程。采用可以发布给用户/开发人员的数据字典实现方便的数据识别同时保证对数据源文档化。编写在线更新来“更新查询”供以后万一数据丢失可以重新处理更新。 <br />用存储过程让系统做重活<br />解决了许多麻烦来产生一个具有高度完整性的数据库解决方案之后，我决定封装一些关联表的功能组，提供一整套常规的存储过程来访问各组以便加快速度和简化客户程序代码的开发。数据库不只是一个存放数据的地方，它也是简化编码之地。 <br />使用查找<br />控制数据完整性的最佳方式就是限制用户的选择。只要有可能都应该提供给用户一个清晰的价值列表供其选择。这样将减少键入代码的错误和误解同时提供数据的一致性。某些公共数据特别适合查找：国家代码、状态代码等。 <br />第 5 部分 - 各种小技巧<br />文档、文档、文档<br />对所有的快捷方式、命名规范、限制和函数都要编制文档。</p>
		<p>采用给表、列[字段]、触发器等加注释的数据库工具。是的，这有点费事，但从长远来看，这样做对开发、支持和跟踪修改非常有用。</p>
		<p>取决于你使用的数据库系统，可能有一些软件会给你一些供你很快上手的文档。你可能希望先开始在说，然后获得越来越多的细节。或者你可能希望周期性的预排，在输入新数据同时随着你的进展对每一部分细节化。不管你选择哪种方式，总要对你的数据库文档化，或者在数据库自身的内部或者单独建立文档。这样，当你过了一年多时间后再回过头来做第 2 个版本，你犯错的机会将大大减少。 <br />使用常用英语（或者其他任何语言）而不要使用编码<br />为什么我们经常采用编码（比如 9935A 可能是‘青岛啤酒’的供应代码，4XF788-Q 可能是帐目编码）？理由很多。但是用户通常都用英语进行思考而不是编码。工作 5 年的会计或许知道 4XF788-Q 是什么东西，但新来的可就不一定了。在创建下拉菜单、列表、报表时最好按照英语名排序。假如你需要编码，那你可以在编码旁附上用户知道的英语。 <br />保存常用信息<br />让一个表专门存放一般数据库信息非常有用。我常在这个表里存放数据库当前版本、最近检查/修复（对 FoxPro）、关联设计文档的名称、客户等信息。这样可以实现一种简单机制跟踪数据库，当客户抱怨他们的数据库没有达到希望的要求而与你联系时，这样做对非客户机/服务器环境特别有用。 <br />测试、测试、反复测试<br />建立或者修订数据库之后，必须用用户新输入的数据测试数据字段。最重要的是，让用户进行测试并且同用户一道保证你选择的数据类型满足商业要求。测试需要在把新数据库投入实际服务之前完成。 <br />检查设计<br />在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原型检查数据库。换句话说，针对每一种最终表达数据的原型应用，保证你检查了数据模型并且查看如何取出数据。 <br />Microsoft Visual FoxPro 设计技巧<br />对复杂的 Microsoft Visual FoxPro 数据库应用程序而言，可以把所有的主表放在一个数据库容器文件里，然后增加其他数据库表文件和装载同原有数据库有关的特殊文件。根据需要用这些文件连接到主文件中的主表。比如数据输入、数据索引、统计分析、向管理层或者政府部门提供报表以及各类只读查询等。这一措施简化了用户和组权限的分配，而且有利于应用程序函数（存储过程）的分组和划分，从而在程序必须修改的时候易于管理。 </p>
		<p>
				<br />
		</p>
<img src ="http://www.blogjava.net/nbtymm/aggbug/62128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-08-07 11:14 <a href="http://www.blogjava.net/nbtymm/archive/2006/08/07/62128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库设计三大范式应用实例剖析(转载)</title><link>http://www.blogjava.net/nbtymm/archive/2006/06/15/52958.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Thu, 15 Jun 2006 03:40:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/06/15/52958.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/52958.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/06/15/52958.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/52958.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/52958.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<strong>引言<br /><br /></strong>　　<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>的设计范式是<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>设计所需要满足的规范，满足这些规范的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>是简洁的、结构明晰的，同时，不会发生插入（insert）、删除（delete）和更新（update）操作异常。反之则是乱七八糟，不仅给<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>的编程人员制造麻烦，而且面目可憎，可能存储了大量不需要的冗余信息。<br /><br />　　设计范式是不是很难懂呢？非也，大学教材上给我们一堆数学公式我们当然看不懂，也记不住。所以我们很多人就根本不按照范式来设计<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>。<br /><br />　　实质上，设计范式用很形象、很简洁的话语就能说清楚，道明白。本文将对范式进行通俗地说明，并以笔者曾经设计的一个简单论坛的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>为例来讲解怎样将这些范式应用于实际工程。<br /><br />　　<strong>范式说明</strong><br /><br />　　第一范式（1NF）：<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中的字段都是单一属性的，不可再分。这个单一属性由基本类型构成，包括整型、实数、字符型、逻辑型、日期型等。<br /><br />　　例如，如下的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表是符合第一范式的：<br /><br /><table cellspacing="0" cellpadding="2" width="90%" align="center" border="1"><tbody><tr><td>字段1 </td><td>字段2 </td><td>字段3 </td><td>字段4</td></tr><tr><td> </td><td> </td><td> </td><td> </td></tr></tbody></table><br />　　而这样的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表是不符合第一范式的：<br /><br /><table cellspacing="0" cellpadding="2" width="90%" align="center" border="1"><tbody><tr><td>字段1 </td><td>字段2 </td><td colspan="2"><div align="center">字段3 </div></td><td>字段4</td></tr><tr><td> </td><td> </td><td>字段3.1</td><td>字段3.2 </td><td> </td></tr></tbody></table><p><br />　　很显然，在当前的任何关系<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>管理系统（DBMS）中，傻瓜也不可能做出不符合第一范式的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>，因为这些DBMS不允许你把<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表的一列再分成二列或多列。因此，你想在现有的DBMS中设计出不符合第一范式的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>都是不可能的。<br /><br />　　第二范式（2NF）：<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中不存在非关键字段对任一候选关键字段的部分函数依赖（部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况），也即所有非关键字段都完全依赖于任意一组候选关键字。假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分)，关键字为组合关键字(学号, 课程名称)，因为存在如下决定关系：<br /><br />　　(学号, 课程名称) → (姓名, 年龄, 成绩, 学分)<br /><br />　　这个<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表不满足第二范式，因为存在如下决定关系：<br /><br />　　(课程名称) → (学分)<br /><br />　　(学号) → (姓名, 年龄)<br /><br />　　即存在组合关键字中的字段决定非关键字的情况。<br /><br />　　由于不符合2NF，这个选课关系表会存在如下问题：<br /><br />　　(1) 数据冗余：<br /><br />　　同一门课程由n个学生选修，"学分"就重复n-1次；同一个学生选修了m门课程，姓名和年龄就重复了m-1次。<br /><br />　　(2) 更新异常：<br /><br />　　若调整了某门课程的学分，数据表中所有行的"学分"值都要更新，否则会出现同一门课程学分不同的情况。<br /><br />　　(3) 插入异常：<br /><br />　　假设要开设一门新的课程，暂时还没有人选修。这样，由于还没有"学号"关键字，课程名称和学分也无法记录入<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>。<br /><br />　　(4) 删除异常：<br /><br />　　假设一批学生已经完成课程的选修，这些选修记录就应该从<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中删除。但是，与此同时，课程名称和学分信息也被删除了。很显然，这也会导致插入异常。 <br /><br />　　把选课关系表SelectCourse改为如下三个表：<br /><br />　　学生：Student(学号, 姓名, 年龄)；<br /><br />　　课程：Course(课程名称, 学分)；<br /><br />　　选课关系：SelectCourse(学号, 课程名称, 成绩)。<br /><br />　　这样的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表是符合第二范式的，消除了数据冗余、更新异常、插入异常和删除异常。<br /><br />　　另外，所有单关键字的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表都符合第二范式，因为不可能存在组合关键字。<br /><br />　　第三范式（3NF）：在第二范式的基础上，数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖，指的是如果存在"A → B → C"的决定关系，则C传递函数依赖于A。因此，满足第三范式的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表应该不存在如下依赖关系：<br /><br />　　关键字段 → 非关键字段x → 非关键字段y<br /><br />　　假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话)，关键字为单一关键字"学号"，因为存在如下决定关系：<br /><br />　　(学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)<br /><br />　　这个<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>是符合2NF的，但是不符合3NF，因为存在如下决定关系：<br /><br />　　(学号) → (所在学院) → (学院地点, 学院电话)<br /><br />　　即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖。<br /><br />　　它也会存在数据冗余、更新异常、插入异常和删除异常的情况，读者可自行分析得知。<br /><br />　　把学生关系表分为如下两个表：<br /><br />　　学生：(学号, 姓名, 年龄, 所在学院)；<br /><br />　　学院：(学院, 地点, 电话)。<br /><br />　　这样的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表是符合第三范式的，消除了数据冗余、更新异常、插入异常和删除异常。<br /><br />　　鲍依斯-科得范式（BCNF）：在第三范式的基础上，<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合第三范式。<br /><br />　　假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量)，且有一个管理员只在一个仓库工作；一个仓库可以存储多种物品。这个<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中存在如下决定关系：<br /><br />　　(仓库ID, 存储物品ID) →(管理员ID, 数量)<br /><br />　　(管理员ID, 存储物品ID) → (仓库ID, 数量)<br /><br />　　所以，(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字，表中的唯一非关键字段为数量，它是符合第三范式的。但是，由于存在如下决定关系：<br /><br />　　(仓库ID) → (管理员ID)<br /><br />　　(管理员ID) → (仓库ID)<br /><br />　　即存在关键字段决定关键字段的情况，所以其不符合BCNF范式。它会出现如下异常情况：<br /><br />　　(1) 删除异常：<br /><br />　　当仓库被清空后，所有"存储物品ID"和"数量"信息被删除的同时，"仓库ID"和"管理员ID"信息也被删除了。<br /><br />　　(2) 插入异常：<br /><br />　　当仓库没有存储任何物品时，无法给仓库分配管理员。<br /><br />　　(3) 更新异常：<br /><br />　　如果仓库换了管理员，则表中所有行的管理员ID都要修改。<br /><br />　　把仓库管理关系表分解为二个关系表：<br /><br />　　仓库管理：StorehouseManage(仓库ID, 管理员ID)；<br /><br />　　仓库：Storehouse(仓库ID, 存储物品ID, 数量)。<br /><br />　　这样的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表是符合BCNF范式的，消除了删除异常、插入异常和更新异常。</p><p><strong>范式应用<br /><br /></strong>　　我们来逐步搞定一个论坛的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>，有如下信息：<br /><br />　　（1） 用户：用户名，email，主页，电话，联系地址<br /><br />　　（2） 帖子：发帖标题，发帖内容，回复标题，回复内容 <br /><br />　　第一次我们将<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>设计为仅仅存在表：<br />　　 </p><p></p><table cellspacing="0" cellpadding="2" width="90%" align="center" border="1"><tbody><tr><td>用户名 </td><td>email </td><td>主页</td><td>电话</td><td>联系地址</td><td>发帖标题</td><td>发帖内容</td><td>回复标题</td><td>回复内容</td></tr></tbody></table><p><br />　　这个<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表符合第一范式，但是没有任何一组候选关键字能决定<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表的整行，唯一的关键字段用户名也不能完全决定整个元组。我们需要增加"发帖ID"、"回复ID"字段，即将表修改为：<br /><br /></p><p></p><table cellspacing="0" cellpadding="2" width="90%" align="center" border="1"><tbody><tr><td>用户名</td><td>email</td><td>主页</td><td>电话</td><td>联系地址</td><td>发帖ID</td><td>发帖标题</td><td>发帖内容</td><td>回复ID</td><td>回复标题</td><td>回复内容</td></tr></tbody></table><p><br />　　这样数据表中的关键字(用户名，发帖ID，回复ID)能决定整行：<br /><br />　　(用户名,发帖ID,回复ID) → (email,主页,电话,联系地址,发帖标题,发帖内容,回复标题,回复内容)<br /><br />　　但是，这样的设计不符合第二范式，因为存在如下决定关系：<br /><br />　　(用户名) → (email,主页,电话,联系地址)<br /><br />　　(发帖ID) → (发帖标题,发帖内容)<br /><br />　　(回复ID) → (回复标题,回复内容)<br /><br />　　即非关键字段部分函数依赖于候选关键字段，很明显，这个设计会导致大量的数据冗余和操作异常。 <br /><br />　　我们将<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表分解为（带下划线的为关键字）：<br /><br />　　（1） 用户信息：用户名，email，主页，电话，联系地址<br /><br />　　（2） 帖子信息：发帖ID，标题，内容<br /><br />　　（3） 回复信息：回复ID，标题，内容<br /><br />　　（4） 发贴：用户名，发帖ID<br /><br />　　（5） 回复：发帖ID，回复ID<br /><br />　　这样的设计是满足第1、2、3范式和BCNF范式要求的，但是这样的设计是不是最好的呢？<br /><br />　　不一定。<br /><br />　　观察可知，第4项"发帖"中的"用户名"和"发帖ID"之间是1：N的关系，因此我们可以把"发帖"合并到第2项的"帖子信息"中；第5项"回复"中的 "发帖ID"和"回复ID"之间也是1：N的关系，因此我们可以把"回复"合并到第3项的"回复信息"中。这样可以一定量地减少数据冗余，新的设计为：<br /><br />　　（1） 用户信息：用户名，email，主页，电话，联系地址<br /><br />　　（2） 帖子信息：用户名，发帖ID，标题，内容<br /><br />　　（3） 回复信息：发帖ID，回复ID，标题，内容<br /><br />　　<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表1显然满足所有范式的要求；<br /><br />　　<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表2中存在非关键字段"标题"、"内容"对关键字段"发帖ID"的部分函数依赖，即不满足第二范式的要求，但是这一设计并不会导致数据冗余和操作异常；<br /><br />　　<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表3中也存在非关键字段"标题"、"内容"对关键字段"回复ID"的部分函数依赖，也不满足第二范式的要求，但是与<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表2相似，这一设计也不会导致数据冗余和操作异常。<br /><br />　　由此可以看出，并不一定要强行满足范式的要求，对于1：N关系，当1的一边合并到N的那边后，N的那边就不再满足第二范式了，但是这种设计反而比较好！<br /><br />　　对于M：N的关系，不能将M一边或N一边合并到另一边去，这样会导致不符合范式要求，同时导致操作异常和数据冗余。 <br />对于1：1的关系，我们可以将左边的1或者右边的1合并到另一边去，设计导致不符合范式要求，但是并不会导致操作异常和数据冗余。<br /><br />　　<strong>结论</strong><br /><br />　　满足范式要求的<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>设计是结构清晰的，同时可避免数据冗余和操作异常。这并意味着不符合范式要求的设计一定是错误的，在<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>表中存在1：1或1：N关系这种较特殊的情况下，合并导致的不符合范式要求反而是合理的。<br /><br />　　在我们设计<a href="http://www.chinahtml.com/databases/" target="_blank"><font color="#4371a6">数据库</font></a>的时候，一定要时刻考虑范式的要求。</p></div>
<img src ="http://www.blogjava.net/nbtymm/aggbug/52958.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-06-15 11:40 <a href="http://www.blogjava.net/nbtymm/archive/2006/06/15/52958.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库设计方法、规范与技巧（转载）</title><link>http://www.blogjava.net/nbtymm/archive/2006/06/15/52951.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Thu, 15 Jun 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/06/15/52951.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/52951.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/06/15/52951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/52951.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/52951.html</trackback:ping><description><![CDATA[
		<div>
				<font style="BACKGROUND-COLOR: #ffffff" color="#000000">一、数据库设计过程<br />数据库技术是信息资源管理最有效的手段。数据库设计是指对于一个给定的应用环境，构造最优的数据库模式，建立数据库及其应用系统，有效存储数据，满足用户信息要求和处理要求。<br />数据库设计中需求分析阶段综合各个用户的应用需求（现实世界的需求），在概念设计阶段形成独立于机器特点、独立于各个DBMS产品的概念模式（信息世界模型），用E-R图来描述。在逻辑设计阶段将E-R图转换成具体的数据库产品支持的数据模型如关系模型，形成数据库逻辑模式。然后根据用户处理的要求，安全性的考虑，在基本表的基础上再建立必要的视图（VIEW）形成数据的外模式。在物理设计阶段根据DBMS特点和处理的需要，进行物理存储安排，设计索引，形成数据库内模式。<br />1. 需求分析阶段 <br />需求收集和分析，结果得到数据字典描述的数据需求（和数据流图描述的处理需求）。 <br />需求分析的重点是调查、收集与分析用户在数据管理中的信息要求、处理要求、安全性与完整性要求。<br />需求分析的方法：调查组织机构情况、调查各部门的业务活动情况、协助用户明确对新系统的各种要求、确定新系统的边界。 <br />常用的调查方法有： 跟班作业、开调查会、请专人介绍、询问、设计调查表请用户填写、查阅记录。<br />分析和表达用户需求的方法主要包括自顶向下和自底向上两类方法。自顶向下的结构化分析方法（Structured Analysis，简称SA方法）从最上层的系统组织机构入手，采用逐层分解的方式分析系统，并把每一层用数据流图和数据字典描述。<br />数据流图表达了数据和处理过程的关系。系统中的数据则借助数据字典（Data Dictionary，简称DD）来描述。<br />数据字典是各类数据描述的集合，它是关于数据库中数据的描述，即元数据，而不是数据本身。数据字典通常包括数据项、数据结构、数据流、数据存储和处理过程五个部分(至少应该包含每个字段的数据类型和在每个表内的主外键)。<br />数据项描述＝｛数据项名，数据项含义说明，别名，数据类型，长度， <br />　　　　　　　　　取值范围，取值含义，与其他数据项的逻辑关系｝ <br />数据结构描述＝｛数据结构名，含义说明，组成:｛数据项或数据结构｝｝ <br />数据流描述＝｛数据流名，说明，数据流来源，数据流去向， <br />　　　　　　　　　组成:｛数据结构｝，平均流量，高峰期流量｝ <br />数据存储描述＝｛数据存储名，说明，编号，流入的数据流，流出的数据流， 　　<br />　　　　　　　　组成:｛数据结构｝，数据量，存取方式｝ <br />处理过程描述＝｛处理过程名，说明，输入:｛数据流｝，输出:｛数据流｝, <br />　　　　　　　　　　处理:｛简要说明｝｝ <br />2. 概念结构设计阶段 <br />通过对用户需求进行综合、归纳与抽象，形成一个独立于具体DBMS的概念模型，可以用E-R图表示。 <br />概念模型用于信息世界的建模。概念模型不依赖于某一个DBMS支持的数据模型。概念模型可以转换为计算机上某一DBMS支持的特定数据模型。 <br />概念模型特点：<br />(1) 具有较强的语义表达能力，能够方便、直接地表达应用中的各种语义知识。 <br />(2) 应该简单、清晰、易于用户理解，是用户与数据库设计人员之间进行交流的语言。<br />概念模型设计的一种常用方法为IDEF1X方法，它就是把实体-联系方法应用到语义数据模型中的一种语义模型化技术，用于建立系统信息模型。<br />    使用IDEF1X方法创建E-R模型的步骤如下所示:<br />2.1 第零步——初始化工程<br />这个阶段的任务是从目的描述和范围描述开始，确定建模目标，开发建模计划，组织建模队伍，收集源材料，制定约束和规范。收集源材料是这阶段的重点。通过调查和观察结果，业务流程，原有系统的输入输出，各种报表，收集原始数据，形成了基本数据资料表。<br />2.2 第一步——定义实体<br />实体集成员都有一个共同的特征和属性集，可以从收集的源材料——基本数据资料表中直接或间接标识出大部分实体。根据源材料名字表中表示物的术语以及具有“代码”结尾的术语，如客户代码、代理商代码、产品代码等将其名词部分代表的实体标识出来，从而初步找出潜在的实体，形成初步实体表。<br />2.3 第二步——定义联系<br />IDEF1X模型中只允许二元联系，n元联系必须定义为n个二元联系。根据实际的业务需求和规则，使用实体联系矩阵来标识实体间的二元关系，然后根据实际情况确定出连接关系的势、关系名和说明，确定关系类型，是标识关系、非标识关系（强制的或可选的）还是非确定关系、分类关系。如果子实体的每个实例都需要通过和父实体的关系来标识，则为标识关系，否则为非标识关系。非标识关系中，如果每个子实体的实例都与而且只与一个父实体关联，则为强制的，否则为非强制的。如果父实体与子实体代表的是同一现实对象，那么它们为分类关系。<br />2.4 第三步——定义码<br />通过引入交叉实体除去上一阶段产生的非确定关系，然后从非交叉实体和独立实体开始标识侯选码属性，以便唯一识别每个实体的实例，再从侯选码中确定主码。为了确定主码和关系的有效性，通过非空规则和非多值规则来保证，即一个实体实例的一个属性不能是空值，也不能在同一个时刻有一个以上的值。找出误认的确定关系，将实体进一步分解，最后构造出IDEF1X模型的键基视图（KB图）。<br />2.5 第四步——定义属性<br />从源数据表中抽取说明性的名词开发出属性表，确定属性的所有者。定义非主码属性，检查属性的非空及非多值规则。此外，还要检查完全依赖函数规则和非传递依赖规则，保证一个非主码属性必须依赖于主码、整个主码、仅仅是主码。以此得到了至少符合关系理论第三范式的改进的IDEF1X模型的全属性视图。<br />2.6 第五步——定义其他对象和规则<br />    定义属性的数据类型、长度、精度、非空、缺省值、约束规则等。定义触发器、存储过程、视图、角色、同义词、序列等对象信息。<br />3. 逻辑结构设计阶段 <br />    将概念结构转换为某个DBMS所支持的数据模型（例如关系模型），并对其进行优化。设计逻辑结构应该选择最适于描述与表达相应概念结构的数据模型，然后选择最合适的DBMS。<br />将E-R图转换为关系模型实际上就是要将实体、实体的属性和实体之间的联系转化为关系模式,这种转换一般遵循如下原则： <br />1）一个实体型转换为一个关系模式。实体的属性就是关系的属性。实体的码就是关系的码。 <br />2）一个m:n联系转换为一个关系模式。与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性。而关系的码为各实体码的组合。 <br />3）一个1:n联系可以转换为一个独立的关系模式，也可以与n端对应的关系模式合并。如果转换为一个独立的关系模式，则与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性，而关系的码为n端实体的码。 <br />4）一个1:1联系可以转换为一个独立的关系模式，也可以与任意一端对应的关系模式合并。<br />5）三个或三个以上实体间的一个多元联系转换为一个关系模式。与该多元联系相连的各实体的码以及联系本身的属性均转换为关系的属性。而关系的码为各实体码的组合。  <br />6）同一实体集的实体间的联系，即自联系，也可按上述1:1、1:n和m:n三种情况分别处理。 <br />7）具有相同码的关系模式可合并。 <br />为了进一步提高数据库应用系统的性能，通常以规范化理论为指导，还应该适当地修改、调整数据模型的结构，这就是数据模型的优化。确定数据依赖。消除冗余的联系。确定各关系模式分别属于第几范式。确定是否要对它们进行合并或分解。一般来说将关系分解为3NF的标准，即：<br />表内的每一个值都只能被表达一次。<br />•?表内的每一行都应该被唯一的标识（有唯一键）。<br />表内不应该存储依赖于其他键的非键信息。   <br />4. 数据库物理设计阶段 <br />为逻辑数据模型选取一个最适合应用环境的物理结构（包括存储结构和存取方法）。根据DBMS特点和处理的需要，进行物理存储安排，设计索引，形成数据库内模式。<br />5. 数据库实施阶段 <br />运用DBMS提供的数据语言（例如SQL）及其宿主语言（例如C），根据逻辑设计和物理设计的结果建立数据库，编制与调试应用程序，组织数据入库，并进行试运行。 数据库实施主要包括以下工作：用DDL定义数据库结构、组织数据入库 、编制与调试应用程序、数据库试运行  <br />6. 数据库运行和维护阶段 <br />数据库应用系统经过试运行后即可投入正式运行。在数据库系统运行过程中必须不断地对其进行评价、调整与修改。包括：数据库的转储和恢复、数据库的安全性、完整性控制、数据库性能的监督、分析和改进、数据库的重组织和重构造。<br /><br />建模工具的使用<br />为加快数据库设计速度，目前有很多数据库辅助工具（CASE工具），如Rational公司的Rational Rose，CA公司的Erwin和Bpwin，Sybase公司的PowerDesigner以及Oracle公司的Oracle Designer等。<br />ERwin主要用来建立数据库的概念模型和物理模型。它能用图形化的方式，描述出实体、联系及实体的属性。ERwin支持IDEF1X方法。通过使用ERwin建模工具自动生成、更改和分析IDEF1X模型，不仅能得到优秀的业务功能和数据需求模型，而且可以实现从IDEF1X模型到数据库物理设计的转变。ERwin工具绘制的模型对应于逻辑模型和物理模型两种。在逻辑模型中，IDEF1X工具箱可以方便地用图形化的方式构建和绘制实体联系及实体的属性。在物理模型中，ERwin可以定义对应的表、列，并可针对各种数据库管理系统自动转换为适当的类型。<br />设计人员可根据需要选用相应的数据库设计建模工具。例如需求分析完成之后，设计人员可以使用Erwin画ER图，将ER图转换为关系数据模型，生成数据库结构；画数据流图，生成应用程序。<br />二、数据库设计技巧<br />1. 设计数据库之前（需求分析阶段）<br />1) 理解客户需求，询问用户如何看待未来需求变化。让客户解释其需求，而且随着开发的继续，还要经常询问客户保证其需求仍然在开发的目的之中。<br />2) 了解企业业务可以在以后的开发阶段节约大量的时间。<br />3) 重视输入输出。<br />在定义数据库表和字段需求（输入）时，首先应检查现有的或者已经设计出的报表、查询和视图（输出）以决定为了支持这些输出哪些是必要的表和字段。<br />举例：假如客户需要一个报表按照邮政编码排序、分段和求和，你要保证其中包括了单独的邮政编码字段而不要把邮政编码糅进地址字段里。<br />4) 创建数据字典和ER 图表<br />ER 图表和数据字典可以让任何了解数据库的人都明确如何从数据库中获得数据。ER图对表明表之间关系很有用，而数据字典则说明了每个字段的用途以及任何可能存在的别名。对SQL 表达式的文档化来说这是完全必要的。<br />5) 定义标准的对象命名规范<br />数据库各种对象的命名必须规范。<br />2. 表和字段的设计（数据库逻辑设计）<br />表设计原则<br />1) 标准化和规范化<br />数据的标准化有助于消除数据库中的数据冗余。标准化有好几种形式，但Third Normal Form（3NF）通常被认为在性能、扩展性和数据完整性方面达到了最好平衡。简单来说，遵守3NF 标准的数据库的表设计原则是：“One Fact in One Place”即某个表只包括其本身基本的属性，当不是它们本身所具有的属性时需进行分解。表之间的关系通过外键相连接。它具有以下特点：有一组表专门存放通过键连接起来的关联数据。<br />举例：某个存放客户及其有关定单的3NF 数据库就可能有两个表：Customer 和Order。Order 表不包含定单关联客户的任何信息，但表内会存放一个键值，该键指向Customer 表里包含该客户信息的那一行。<br />事实上，为了效率的缘故，对表不进行标准化有时也是必要的。<br />2) 数据驱动<br />采用数据驱动而非硬编码的方式，许多策略变更和维护都会方便得多，大大增强系统的灵活性和扩展性。<br />举例，假如用户界面要访问外部数据源（文件、XML 文档、其他数据库等），不妨把相应的连接和路径信息存储在用户界面支持表里。还有，如果用户界面执行工作流之类的任务（发送邮件、打印信笺、修改记录状态等），那么产生工作流的数据也可以存放在数据库里。角色权限管理也可以通过数据驱动来完成。事实上，如果过程是数据驱动的，你就可以把相当大的责任推给用户，由用户来维护自己的工作流过程。<br />3) 考虑各种变化<br />在设计数据库的时候考虑到哪些数据字段将来可能会发生变更。<br />举例，姓氏就是如此（注意是西方人的姓氏，比如女性结婚后从夫姓等）。所以，在建立系统存储客户信息时，在单独的一个数据表里存储姓氏字段，而且还附加起始日和终止日等字段，这样就可以跟踪这一数据条目的变化。<br /><br />字段设计原则<br />4) 每个表中都应该添加的3 个有用的字段<br />•?dRecordCreationDate，在VB 下默认是Now()，而在SQL Server 下默认为GETDATE()<br />•?sRecordCreator，在SQL Server 下默认为NOT NULL DEFAULT USER<br />•?nRecordVersion，记录的版本标记；有助于准确说明记录中出现null 数据或者丢失数据的原因<br />5) 对地址和电话采用多个字段<br />描述街道地址就短短一行记录是不够的。Address_Line1、Address_Line2 和Address_Line3 可以提供更大的灵活性。还有，电话号码和邮件地址最好拥有自己的数据表，其间具有自身的类型和标记类别。<br />6) 使用角色实体定义属于某类别的列<br />在需要对属于特定类别或者具有特定角色的事物做定义时，可以用角色实体来创建特定的时间关联关系，从而可以实现自我文档化。<br />举例：用PERSON 实体和PERSON_TYPE 实体来描述人员。比方说，当John Smith, Engineer 提升为John Smith, Director 乃至最后爬到John Smith, CIO 的高位，而所有你要做的不过是改变两个表PERSON 和PERSON_TYPE 之间关系的键值，同时增加一个日期/时间字段来知道变化是何时发生的。这样，你的PERSON_TYPE 表就包含了所有PERSON 的可能类型，比如Associate、Engineer、Director、CIO 或者CEO 等。还有个替代办法就是改变PERSON 记录来反映新头衔的变化，不过这样一来在时间上无法跟踪个人所处位置的具体时间。<br />7) 选择数字类型和文本类型尽量充足<br />在SQL 中使用smallint 和tinyint 类型要特别小心。比如，假如想看看月销售总额，总额字段类型是smallint，那么，如果总额超过了$32,767 就不能进行计算操作了。<br />而ID 类型的文本字段，比如客户ID 或定单号等等都应该设置得比一般想象更大。假设客户ID 为10 位数长。那你应该把数据库表字段的长度设为12 或者13 个字符长。但这额外占据的空间却无需将来重构整个数据库就可以实现数据库规模的增长了。<br />8) 增加删除标记字段<br />在表中包含一个“删除标记”字段，这样就可以把行标记为删除。在关系数据库里不要单独删除某一行；最好采用清除数据程序而且要仔细维护索引整体性。 <br />3. 选择键和索引（数据库逻辑设计）<br />键选择原则：<br />1) 键设计4 原则<br />•?为关联字段创建外键。<br />•?所有的键都必须唯一。<br />•?避免使用复合键。<br />•?外键总是关联唯一的键字段。<br />2) 使用系统生成的主键<br />设计数据库的时候采用系统生成的键作为主键，那么实际控制了数据库的索引完整性。这样，数据库和非人工机制就有效地控制了对存储数据中每一行的访问。采用系统生成键作为主键还有一个优点：当拥有一致的键结构时，找到逻辑缺陷很容易。<br />3) 不要用用户的键(不让主键具有可更新性)<br />在确定采用什么字段作为表的键的时候，可一定要小心用户将要编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。<br />4) 可选键有时可做主键<br />把可选键进一步用做主键，可以拥有建立强大索引的能力。<br /><br />索引使用原则：<br />索引是从数据库中获取数据的最高效方式之一。95%的数据库性能问题都可以采用索引技术得到解决。<br />1) 逻辑主键使用唯一的成组索引，对系统键（作为存储过程）采用唯一的非成组索引，对任何外键列采用非成组索引。考虑数据库的空间有多大，表如何进行访问，还有这些访问是否主要用作读写。<br />2) 大多数数据库都索引自动创建的主键字段，但是可别忘了索引外键，它们也是经常使用的键，比如运行查询显示主表和所有关联表的某条记录就用得上。<br />3) 不要索引memo/note 字段，不要索引大型字段（有很多字符），这样作会让索引占用太多的存储空间。<br />4) 不要索引常用的小型表<br />不要为小型数据表设置任何键，假如它们经常有插入和删除操作就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。<br /><br />4. 数据完整性设计（数据库逻辑设计）<br />1) 完整性实现机制：<br />实体完整性：主键<br />参照完整性：<br />父表中删除数据：级联删除；受限删除；置空值<br />父表中插入数据：受限插入；递归插入<br />父表中更新数据：级联更新；受限更新；置空值<br />DBMS对参照完整性可以有两种方法实现：外键实现机制（约束规则）和触发器实现机制<br />用户定义完整性：<br />    NOT NULL；CHECK；触发器<br />2) 用约束而非商务规则强制数据完整性<br />采用数据库系统实现数据的完整性。这不但包括通过标准化实现的完整性而且还包括数据的功能性。在写数据的时候还可以增加触发器来保证数据的正确性。不要依赖于商务层保证数据完整性；它不能保证表之间（外键）的完整性所以不能强加于其他完整性规则之上。<br />3) 强制指示完整性<br />在有害数据进入数据库之前将其剔除。激活数据库系统的指示完整性特性。这样可以保持数据的清洁而能迫使开发人员投入更多的时间处理错误条件。<br />4) 使用查找控制数据完整性<br />控制数据完整性的最佳方式就是限制用户的选择。只要有可能都应该提供给用户一个清晰的价值列表供其选择。这样将减少键入代码的错误和误解同时提供数据的一致性。某些公共数据特别适合查找：国家代码、状态代码等。<br />5) 采用视图<br />为了在数据库和应用程序代码之间提供另一层抽象，可以为应用程序建立专门的视图而不必非要应用程序直接访问数据表。这样做还等于在处理数据库变更时给你提供了更多的自由。<br />5. 其他设计技巧<br />1) 避免使用触发器<br />触发器的功能通常可以用其他方式实现。在调试程序时触发器可能成为干扰。假如你确实需要采用触发器，你最好集中对它文档化。<br />2) 使用常用英语（或者其他任何语言）而不要使用编码<br />在创建下拉菜单、列表、报表时最好按照英语名排序。假如需要编码，可以在编码旁附上用户知道的英语。<br />3) 保存常用信息<br />让一个表专门存放一般数据库信息非常有用。在这个表里存放数据库当前版本、最近检查/修复（对Access）、关联设计文档的名称、客户等信息。这样可以实现一种简单机制跟踪数据库，当客户抱怨他们的数据库没有达到希望的要求而与你联系时，这样做对非客户机/服务器环境特别有用。<br />4) 包含版本机制<br />在数据库中引入版本控制机制来确定使用中的数据库的版本。时间一长，用户的需求总是会改变的。最终可能会要求修改数据库结构。把版本信息直接存放到数据库中更为方便。 <br />5) 编制文档<br />对所有的快捷方式、命名规范、限制和函数都要编制文档。<br />采用给表、列、触发器等加注释的数据库工具。对开发、支持和跟踪修改非常有用。<br />对数据库文档化，或者在数据库自身的内部或者单独建立文档。这样，当过了一年多时间后再回过头来做第2 个版本，犯错的机会将大大减少。<br />6) 测试、测试、反复测试<br />建立或者修订数据库之后，必须用用户新输入的数据测试数据字段。最重要的是，让用户进行测试并且同用户一道保证选择的数据类型满足商业要求。测试需要在把新数据库投入实际服务之前完成。<br />7) 检查设计<br />在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原型检查数据库。换句话说，针对每一种最终表达数据的原型应用，保证你检查了数据模型并且查看如何取出数据。<br />三、数据库命名规范<br />1. 实体（表）的命名<br />1) 表以名词或名词短语命名，确定表名是采用复数还是单数形式，此外给表的别名定义简单规则（比方说，如果表名是一个单词，别名就取单词的前4 个字母；如果表名是两个单词，就各取两个单词的前两个字母组成4 个字母长的别名；如果表的名字由3 个单词组成，从头两个单词中各取一个然后从最后一个单词中再取出两个字母，结果还是组成4 字母长的别名，其余依次类推）<br />对工作用表来说，表名可以加上前缀WORK_ 后面附上采用该表的应用程序的名字。在命名过程当中，根据语义拼凑缩写即可。注意，由于ORCLE会将字段名称统一成大写或者小写中的一种，所以要求加上下划线。<br />举例：<br />定义的缩写 Sales: Sal 销售；<br />Order: Ord 订单；<br />Detail: Dtl 明细；<br />则销售订单明细表命名为：Sal_Ord_Dtl;<br />2) 如果表或者是字段的名称仅有一个单词，那么建议不使用缩写，而是用完整的单词。<br />举例：<br />定义的缩写 Material Ma 物品；<br />物品表名为：Material, 而不是 Ma.<br />但是字段物品编码则是：Ma_ID;而不是Material_ID<br />3) 所有的存储值列表的表前面加上前缀Z<br />目的是将这些值列表类排序在数据库最后。<br />4) 所有的冗余类的命名(主要是累计表)前面加上前缀X<br />冗余类是为了提高数据库效率，非规范化数据库的时候加入的字段或者表<br />5) 关联类通过用下划线连接两个基本类之后，再加前缀R的方式命名,后面按照字母顺序罗列两个表名或者表名的缩写。<br />关联表用于保存多对多关系。<br />如果被关联的表名大于10个字母，必须将原来的表名的进行缩写。如果没有其他原因，建议都使用缩写。<br />举例：表Object与自身存在多对多的关系,则保存多对多关系的表命名为：R_Object；<br />表 Depart和Employee;存在多对多的关系；则关联表命名为R_Dept_Emp<br />2. 属性（列）的命名<br />1) 采用有意义的列名，表内的列要针对键采用一整套设计规则。每一个表都将有一个自动ID作为主健,逻辑上的主健作为第一组候选主健来定义,如果是数据库自动生成的编码，统一命名为：ID;如果是自定义的逻辑上的编码则用缩写加“ID”的方法命名。如果键是数字类型，你可以用_NO 作为后缀；如果是字符类型则可以采用_CODE 后缀。对列名应该采用标准的前缀和后缀。<br />举例：销售订单的编号字段命名：Sal_Ord_ID；如果还存在一个数据库生成的自动编号，则命名为：ID。<br />2) 所有的属性加上有关类型的后缀，注意，如果还需要其它的后缀，都放在类型后缀之前。<br />注: 数据类型是文本的字段，类型后缀TX可以不写。有些类型比较明显的字段，可以不写类型后缀。<br />3) 采用前缀命名<br />给每个表的列名都采用统一的前缀，那么在编写SQL表达式的时候会得到大大的简化。这样做也确实有缺点，比如破坏了自动表连接工具的作用，后者把公共列名同某些数据库联系起来。<br />3. 视图的命名<br />1) 视图以V作为前缀，其他命名规则和表的命名类似；<br />2) 命名应尽量体现各视图的功能。<br />4. 触发器的命名<br />触发器以TR作为前缀，触发器名为相应的表名加上后缀，Insert触发器加'_I'，Delete触发器加'_D'，Update触发器加'_U'，如：TR_Customer_I，TR_Customer_D，TR_Customer_U。<br />5. 存储过程名<br />存储过程应以'UP_'开头，和系统的存储过程区分，后续部分主要以动宾形式构成，并用下划线分割各个组成部分。如增加代理商的帐户的存储过程为'UP_Ins_Agent_Account'。<br />6. 变量名<br />变量名采用小写，若属于词组形式，用下划线分隔每个单词，如@my_err_no。<br />7. 命名中其他注意事项<br />1)  以上命名都不得超过30个字符的系统限制。变量名的长度限制为29（不包括标识字符@）。<br />2)  数据对象、变量的命名都采用英文字符，禁止使用中文命名。绝对不要在对象名的字符之间留空格。<br />3) 小心保留词，要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突<br />5) 保持字段名和类型的一致性，在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据类型在一个表里是整数，那在另一个表里可就别变成字符型了。 </font>
		</div>
<img src ="http://www.blogjava.net/nbtymm/aggbug/52951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-06-15 11:23 <a href="http://www.blogjava.net/nbtymm/archive/2006/06/15/52951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>精美SQL语句(转载)</title><link>http://www.blogjava.net/nbtymm/archive/2006/06/05/50447.html</link><dc:creator>nbt</dc:creator><author>nbt</author><pubDate>Mon, 05 Jun 2006 04:08:00 GMT</pubDate><guid>http://www.blogjava.net/nbtymm/archive/2006/06/05/50447.html</guid><wfw:comment>http://www.blogjava.net/nbtymm/comments/50447.html</wfw:comment><comments>http://www.blogjava.net/nbtymm/archive/2006/06/05/50447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/nbtymm/comments/commentRss/50447.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/nbtymm/services/trackbacks/50447.html</trackback:ping><description><![CDATA[asc 按升序排列<br />desc 按降序排列<br /><p>下列语句部分是Mssql语句，不可以在access中使用。</p><p>SQL分类： <br />DDL—数据定义语言(CREATE，ALTER，DROP，DECLARE) <br />DML—数据操纵语言(SELECT，DELETE，UPDATE，INSERT) <br />DCL—数据控制语言(GRANT，REVOKE，COMMIT，ROLLBACK)</p><p>首先,简要介绍基础语句：<br />1、说明：创建数据库<br />CREATE DATABASE database-name <br />2、说明：删除数据库<br />drop database dbname<br />3、说明：备份sql server<br />--- 创建 备份数据的 device<br />USE master<br />EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat'<br />--- 开始 备份<br />BACKUP DATABASE pubs TO testBack <br />4、说明：创建新表<br />create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)<br />根据已有的表创建新表： <br />A：create table tab_new like tab_old (使用旧表创建新表)<br />B：create table tab_new as select col1,col2… from tab_old definition only<br />5、说明：删除新表drop table tabname <br />6、说明：增加一个列<br />Alter table tabname add column col type<br />注：列增加后将不能删除。DB2中列加上后数据类型也不能改变，唯一能改变的是增加varchar类型的长度。<br />7、说明：添加主键： Alter table tabname add primary key(col) <br />说明：删除主键： Alter table tabname drop primary key(col) <br />8、说明：创建索引：create [unique] index idxname on tabname(col….) <br />删除索引：drop index idxname<br />注：索引是不可更改的，想更改必须删除重新建。<br />9、说明：创建视图：create view viewname as select statement <br />删除视图：drop view viewname<br />10、说明：几个简单的基本的sql语句<br />选择：select * from table1 where 范围<br />插入：insert into table1(field1,field2) values(value1,value2)<br />删除：delete from table1 where 范围<br />更新：update table1 set field1=value1 where 范围<br />查找：select * from table1 where field1 like ’%value1%’ ---like的语法很精妙，查资料!<br />排序：select * from table1 order by field1,field2 [desc]<br />总数：select count as totalcount from table1<br />求和：select sum(field1) as sumvalue from table1<br />平均：select avg(field1) as avgvalue from table1<br />最大：select max(field1) as maxvalue from table1<br />最小：select min(field1) as minvalue from table1<br />11、说明：几个高级查询运算词<br />A： UNION 运算符 <br />UNION 运算符通过组合其他两个结果表（例如 TABLE1 和 TABLE2）并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时（即 UNION ALL），不消除重复行。两种情况下，派生表的每一行不是来自 TABLE1 就是来自 TABLE2。 <br />B： EXCEPT 运算符 <br />EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL)，不消除重复行。 <br />C： INTERSECT 运算符<br />INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL)，不消除重复行。 <br />注：使用运算词的几个查询结果行必须是一致的。 <br />12、说明：使用外连接 <br />A、left outer join： <br />左外连接（左连接）：结果集几包括连接表的匹配行，也包括左连接表的所有行。 <br />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<br />B：right outer join: <br />右外连接(右连接)：结果集既包括连接表的匹配连接行，也包括右连接表的所有行。 <br />C：full outer join： <br />全外连接：不仅包括符号连接表的匹配行，还包括两个连接表中的所有记录。</p><p>其次，大家来看一些不错的sql语句<br />1、说明：复制表(只复制结构,源表名：a 新表名：b) (Access可用)<br />法一：select * into b from a where 1&lt;&gt;1<br />法二：select top 0 * into b from a</p><p>2、说明：拷贝表(拷贝数据,源表名：a 目标表名：b) (Access可用)<br />insert into b(a, b, c) select d,e,f from b;</p><p>3、说明：跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)<br />insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件<br />例子：..from b in '"&amp;Server.MapPath(".")&amp;"\data.mdb" &amp;"' where..</p><p>4、说明：子查询(表名1：a 表名2：b)<br />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)</p><p>5、说明：显示文章、提交人和最后回复时间<br />select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b</p><p>6、说明：外连接查询(表名1：a 表名2：b)<br />select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c</p><p>7、说明：在线视图查询(表名1：a )<br />select * from (SELECT a,b,c FROM a) T where t.a &gt; 1;</p><p>8、说明：between的用法,between限制查询数据范围时包括了边界值,not between不包括<br />select * from table1 where time between time1 and time2<br />select a,b,c, from table1 where a not between 数值1 and 数值2</p><p>9、说明：in 的使用方法<br />select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’)</p><p>10、说明：两张关联表，删除主表中已经在副表中没有的信息 <br />delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 )</p><p>11、说明：四表联查问题：<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 .....</p><p>12、说明：日程安排提前五分钟提醒 <br />SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())&gt;5</p><p>13、说明：一条sql 语句搞定数据库分页<br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段</p><p>14、说明：前10条记录<br />select top 10 * form table1 where 范围</p><p>15、说明：选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)<br />select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)</p><p>16、说明：包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表<br />(select a from tableA ) except (select a from tableB) except (select a from tableC)</p><p>17、说明：随机取出10条数据<br />select top 10 * from tablename order by newid()</p><p>18、说明：随机选择记录<br />select newid()</p><p>19、说明：删除重复记录<br />Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)</p><p>20、说明：列出数据库里所有的表名<br />select name from sysobjects where type='U' </p><p>21、说明：列出表里的所有的<br />select name from syscolumns where id=object_id('TableName')</p><p>22、说明：列示type、vender、pcs字段，以type字段排列，case可以方便地实现多重选择，类似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<br />显示结果：<br />type vender pcs<br />电脑 A 1<br />电脑 A 1<br />光盘 B 2<br />光盘 A 2<br />手机 B 3<br />手机 C 3</p><p>23、说明：初始化表table1<br /><br />TRUNCATE TABLE table1</p><p>24、说明：选择从10到15的记录<br />select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc<br />　　<br />随机选择数据库记录的方法（使用Randomize函数，通过SQL语句实现）<br />　　对存储在数据库中的数据来说，随机数特性能给出上面的效果，但它们可能太慢了些。你不能要求ASP“找个随机数”然后打印出来。实际上常见的解决方案是建立如下所示的循环： <br />Randomize <br />RNumber = Int(Rnd*499) +1 <br />　<br />While Not objRec.EOF <br />If objRec("ID") = RNumber THEN <br />... 这里是执行脚本 ... <br />end if <br />objRec.MoveNext <br />Wend <br />　<br />　　这很容易理解。首先，你取出1到500范围之内的一个随机数（假设500就是数据库内记录的总数）。然后，你遍历每一记录来测试ID 的值、检查其是否匹配RNumber。满足条件的话就执行由THEN 关键字开始的那一块代码。假如你的RNumber 等于495，那么要循环一遍数据库花的时间可就长了。虽然500这个数字看起来大了些，但相比更为稳固的企业解决方案这还是个小型数据库了，后者通常在一个数据库内就包含了成千上万条记录。这时候不就死定了？ <br />　　采用SQL，你就可以很快地找出准确的记录并且打开一个只包含该记录的recordset，如下所示： <br />Randomize <br />RNumber = Int(Rnd*499) + 1 <br />　<br />SQL = "SELECT * FROM Customers WHERE ID = " &amp; RNumber <br />　<br />set objRec = ObjConn.Execute(SQL) <br />Response.WriteRNumber &amp; " = " &amp; objRec("ID") &amp; " " &amp; objRec("c_email") <br />　<br />　　不必写出RNumber 和ID，你只需要检查匹配情况即可。只要你对以上代码的工作满意，你自可按需操作“随机”记录。Recordset没有包含其他内容，因此你很快就能找到你需要的记录这样就大大降低了处理时间。 <br />再谈随机数 <br />　　现在你下定决心要榨干Random 函数的最后一滴油，那么你可能会一次取出多条随机记录或者想采用一定随机范围内的记录。把上面的标准Random 示例扩展一下就可以用SQL应对上面两种情况了。 <br />　　为了取出几条随机选择的记录并存放在同一recordset内，你可以存储三个随机数，然后查询数据库获得匹配这些数字的记录： <br />SQL = "SELECT * FROM Customers WHERE ID = " &amp; RNumber &amp; " OR ID = " &amp; RNumber2 &amp; " OR ID = " &amp; RNumber3 <br />　<br />　　假如你想选出10条记录（也许是每次页面装载时的10条链接的列表），你可以用BETWEEN 或者数学等式选出第一条记录和适当数量的递增记录。这一操作可以通过好几种方式来完成，但是 SELECT 语句只显示一种可能（这里的ID 是自动生成的号码）： <br />SQL = "SELECT * FROM Customers WHERE ID BETWEEN " &amp; RNumber &amp; " AND " &amp; RNumber &amp; "+ 9" </p><p>　　注意：以上代码的执行目的不是检查数据库内是否有9条并发记录。</p><p>　<br />随机读取若干条记录，测试过<br />Access语法：SELECT top 10 * From 表名 ORDER BY Rnd(id)<br />Sql server:select top n * from 表名 order by newid()<br />mysqlelect * From 表名 Order By rand() Limit n<br />Access左连接语法(最近开发要用左连接,Access帮助什么都没有,网上没有Access的SQL说明,只有自己测试, 现在记下以备后查)<br />语法elect table1.fd1,table1,fd2,table2.fd2 From table1 left join table2 on table1.fd1,table2.fd1 where ...<br />使用SQL语句 用...代替过长的字符串显示<br />语法：<br />SQL数据库：select case when len(field)&gt;10 then left(field,10)+'...' else field end as news_name,news_id from tablename<br />Access数据库：SELECT iif(len(field)&gt;2,left(field,2)+'...',field) FROM tablename; <br />　<br />Conn.Execute说明<br />Execute方法<br />　　该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集，该方法的使用格式分为以下两种：<br /><br />　1．执行SQL查询语句时，将返回查询得到的记录集。用法为：<br />　　　　Set 对象变量名=连接对象.Execute("SQL 查询语言")<br />　　　Execute方法调用后，会自动创建记录集对象，并将查询结果存储在该记录对象中，通过Set方法，将记录集赋给指定的对象保存，以后对象变量就代表了该记录集对象。</p><p>　　　　2．执行SQL的操作性语言时，没有记录集的返回。此时用法为：<br />　　　　连接对象.Execute "SQL 操作性语句" [, RecordAffected][, Option]<br />　　　　　　·RecordAffected 为可选项，此出可放置一个变量，SQL语句执行后，所生效的记录数会自动保存到该变量中。通过访问该变量，就可知道SQL语句队多少条记录进行了操作。<br />　　　　　　·Option 可选项，该参数的取值通常为adCMDText，它用于告诉ADO，应该将Execute方法之后的第一个字符解释为命令文本。通过指定该参数，可使执行更高效。</p><p>·BeginTrans、RollbackTrans、CommitTrans方法<br />　　这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开始一个事物；RollbackTrans用于回滚事务；CommitTrans用于提交所有的事务处理结果，即确认事务的处理。<br />　　事务处理可以将一组操作视为一个整体，只有全部语句都成功执行后，事务处理才算成功；若其中有一个语句执行失败，则整个处理就算失败，并恢复到处里前的状态。<br />　　BeginTrans和CommitTrans用于标记事务的开始和结束，在这两个之间的语句，就是作为事务处理的语句。判断事务处理是否成功，可通过连接对象的Error集合来实现，若Error集合的成员个数不为0，则说明有错误发生，事务处理失败。Error集合中的每一个Error对象，代表一个错误信息。 </p><img src ="http://www.blogjava.net/nbtymm/aggbug/50447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/nbtymm/" target="_blank">nbt</a> 2006-06-05 12:08 <a href="http://www.blogjava.net/nbtymm/archive/2006/06/05/50447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>