SQL Server 数据库开发规范

数据库版本的选择

SQL Server 目前常见版本有 : 2005、2008、2012、2014、2016、2017……

后面谈及 SQL Server 的版本前面不再加 “SQL Server” 字样,仅用 4 位数字代替。

根据服务器上的操作系统及公司常用的数据库版本,结合项目的需要,选择较新的版本为佳。

注:

  • SQLServer版本与操作系统密切相关, 如:Win2003 无法安装 2012 及 2014, Win2008 无法安装 2016, 2016 版本起开始支持Linux……
  • SQL Server2012 bug较多,一般不要选用;
  • 选择新版数据库, 当然会有性能方面的考虑(比如 2012起提供了新的分页语法, 2014提供内存表,2016 内置系统的分割函数), 但不仅仅是出于性能的考虑, 还有
  1. 常规维护(比如:2008的新功能压缩备份,只有常规备份的几分之一,可以节约服务器的空间)
  2. DBA维护(比如:2005 就没有影响较小查慢SQL、死锁的方法, 2014 就比较方便而且对系统性能影响较小; 2014上的作业用Power Shell 脚本可安全地实现更多系统功能)、
  3. 审计(2008新加功能,如果数据库数据经常被篡改或怀疑有黑客侵入,就用得着了)
  4. 高可用及读写分离(对于访问量大,稳定性要求高而且有报表需求的项目,2014 的 alwayson 是非常实用的)
  5. 其它还有内置的加密(2016开始支持, 普通用户即使能查也无法看到实际数据)、对 Linux 的支持等, 也是非常实用的。具体的可以查看 msdn 上相关介绍。

同一版本还有开发版、标准版、企业版等区别,一律选用企业版, 不再赘述。

数据库的创建

1
2
3
4
5
6
7
8
9
10
--文件的增长量设置为 64MB
CREATE DATABASE [test]
  ON  PRIMARY
( NAME = N 'test' , FILENAME = N 'D:\database\test.mdf' , SIZE = 5MB , FILEGROWTH = 64MB )
  LOG ON
( NAME = N 'test_log' , FILENAME = N 'D:\database\test_log.ldf' , SIZE = 1MB , FILEGROWTH = 64MB )
GO
--恢复模式改为简单,如果希望为完整,不要下面这句即可
ALTER DATABASE [test] SET RECOVERY SIMPLE WITH NO_WAIT
GO

主要是以下几个方面( 3、4两点可以DBA来完成 ):

  1. 增加量要设置为固定值而不是百分比, 避免数据库变大后一次性增涨过大导致空间浪费及性能问题;
  2. 一般不太重要(不涉及到钱和重要业务)的库建议设置为简单,这样性能高而且免维护,缺点:如果要找回以前更改的数据比较麻烦。如果生产环境设置为完整模式,必须请DBA帮忙维护( 主要是做日志备份 );
  3. 对于要做表分区的,可以增加文件组和文件, 但一般情况下只保留一个文件组和文件,因为多文件只有在多磁盘下才有性能提升的可能;
  4. 对于某些不太重要的大表(如日志), 也可以单独增加一个专门的文件组和文件,方便日后单独清理并释放空间. 

命名规范

几种常见的命名方法:

Pascal :

    将标识符的首字母和后面连接的每个单词的首字母都大写。可以对三字符或更多字符的标识符使用 Pascal 大小写。例如:BackColor

 

Camel :

    标识符的首字母小写,而每个后面连接的单词的首字母都大写。例如: backColor

 

大写 :

    标识符中的所有字母都大写。仅对于由两个或者更少字母组成的标识符使用该约定。例如:

    System.IO

    System.Web.UI


以下是常见的数据库对象的命名规范:

序号对象名命名规范示例备注
1数据库项目名,全小写csp归档库为原数据库+'_年份', 如:'csp_2018'
2模块名_表名_后缀

sys_user_data

sys_user_role_mid

 

全小写。模块名为可选, 但尽量加上

后缀:

data 为基本数据表

mid 为中间表

3字段Camel 命名法insertTime 
4存储过程Proc_模块名_表对应类名_操作Proc_BaseData_Area_GetPacal命名法。 开发类的存储过程绝对不要用 'sp_xxx' , 因为这样会先查找系统存储过程,没有再去查找用户存储过程,影响性能。
5函数Fun_功能名Fun_SplitPacal命名法。
6触发器trig_表名_操作类型

trig_user_data_I (插入)

trig_user_data_I_D_U ( 增、删、改 )

 
7主键PK_表名PK_user_data 
8外键FK_表名_字段名FK_user_data_departmentId

外键约束会导致性能下降、系统迁移麻烦。

尽量不要建立外键,外键在心里。

9默认约束DF_表名_字段名DF_user_data_insertTime 
10检查约束CK_表名_字段名CK_user_data_birthday 
11视图view_表名_功能view_user类似表名
12函数Fun_操作Fun_Split 
13索引IX_表名_字段名
  1. IX_user_data_name_addTime

2. IX_DBA_user_data_account

DBA优化时创建的索引以 “IX_DBA_” 开头
14其它对象(序列、自定义类型等)seq_表名或功能名seq_order_data命名规则类似表名
15其它可编程对象  命名规则类似存储过程

另外: 所有数据库对象名称不允许用中文,不允许包含空格,不要使用数据库保留字


建表及相关对象规范

  1. SQL Server 的未来版本中将删除 ntext、text 和 image 数据类型。 请避免在新开发工作中使用这些数据类型,并考虑修改当前使用这些数据类型的应用程序。 请改用 nvarchar(max)、 varchar(max)和 varbinary(max) 。

  2. 在满足需要的同时尽量选用占用空间较小的数据类型,比如时间类型可选类型主要有 date, smalldatetime, datetime, datetime2 , 如果只需要记载年份, 则 date 就够用了, 可以节省硬盘和内存空间, 而且效率更高;
  3. 字符串统一采用 nvarchar 。对于较大的表( 百万级以上 ), 只用到了纯字母和数字的字段, 而且性能要求较高的情况下可以用 varchar ;
  4. 尽量减少不必要的约束, 这些虽然带来方便, 但会影响增、删、改的速度;
  5. 尽量少用触发器, 触发器的逻辑应尽可能简单,且不允许出现错误(必须有判断机制),不允许远程操作(远程操作可能有网络错误导致原有的增删改都无法完成);
  6. 对于可以用其它信息得到的列(如根据生日列可以得到年龄; 根据当前时间判断当前记录是否已过期的), 请使用计算列,而不是用定时任务反复去更新导致表频繁更新而影响数据库性能。
  7. 对于增、删、改非常频繁的表,不应有太多的索引, 而且记录数不宜过多。
  8. 每个表都必须建立主键。无主键的表会带来阻塞,数据重复等不必要的问题。bigint 比 guid 建立的主键效率更高, 如果一定要用 guid 作为主键, 看是否能生成有序的 guid , 如果不能, 主键不要使用聚集索引,可以用其它常用列(如 insertTime)作为聚集索引。
  9. 作为与其它表连接的字段,必须建立索引。

https://docs.microsoft.com/zh-cn/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-2017

数据库设计规范

 

  1. 三范式

数据库设计中应尽可能遵守三范式。所谓三范式即:

  • 没有重复的组或多值的列,这是数据库设计的最低要求。
  • 非关键字段必须依赖于主关键字,不能依赖于一个组合式主关键字的某些组成部分。消除部分依赖,大部分情况下,数据库设计都应该达到第二范式。
  • 一个非关键字段不能依赖于另一个非关键字段。消除传递依赖,达到第三范式应该是系统中大部分表的要求,除非一些特殊作用的表。

      2.适当的冗余

但是完全按照规范化设计的系统几乎是不可能的,除非系统特别的小,在规范化设计后,有计划地加入冗余是必要的。冗余可以是冗余数据库、冗余表或者冗余字段,不同粒度的冗余可以起到不同的作用。冗余可以是为了编程方便而增加,也可以是为了性能的提高而增加。从性能角度来说,冗余数据库可以分散数据库压力,冗余表可以分散数据量大的表的并发压力,也可以加快特殊查询的速度,冗余字段可以有效减少数据库表的连接,提高效率。
比如一些日志表的历史统计信息,我们可以通过作业定期在数据库负载较小的凌晨8点对数据日志数据进行统计,并建立冗余的统计表记录下来。
      3.主键
主键是必要的,SQL SERVER的主键同时是一个唯一索引,而且在实际应用中,我们往往选择最小的键组合作为主键,所以主键往往适合作为表的聚集索引。聚集索引对查询的影响是比较大的,这个在下面索引的叙述。在有多个键的表,主键的选择也比较重要,一般选择总的长度小的键,小的键的比较速度快,同时小的键可以使主键的B树结构的层次更少。主键的选择还要注意组合主键的字段次序,对于组合主键来说,不同的字段次序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。
      4.索引
索引分为聚集索引和非聚集索引。
每个数据表只能建立一个聚集索引,聚集索引决定了数据在表中的物理顺序,同时非聚集索引依赖聚集索引存在。每一个非聚集索引B树的页节点都存有对应的聚集索引键。因此聚集索引和非聚集索引的选择应该遵守如下规范:
1)应尽量选择符合唯一约束的字段建立聚集索引
2)尽量选择占用空间较小的字段建立聚集索引,一般要求聚集索引小于900字节
3)根据数据量决定哪些表需要增加索引,数据量小的可以只有主键。同时对数据量比较大的表(>1000行)应结合数据表的使用情况建立非聚集索引以提高数据库查询的反应效率。但是过多的非聚集索引也会影响数据表记录的插入及更新速度,一般要求非聚集索引的个数不超过两位数。因此应该针对各数据表的实际情况设计索引。
4)若某列的值大部分是a,少数是别的值(如b,c,d…),且经常以该列的其它值(如b,c,d…)为查询条件,可以考虑对(如b,c,d…)建立筛选索引。
5)把经常一起出现的字段组合在一起,组成组合索引,组合索引的字段顺序与主键一样,也需要把最常用的字段放在前面,把重复率低的字段放在前面,同一索引中的组成列最好不要超过3列。
6)根据使用频率决定哪些字段需要建立索引,选择经常作为连接条件、筛选条件、聚合查询、排序的字段作为索引的候选字段。
7)若表主要用来查询,则可按需要建立索引,若对表操作主要是UPDATE,则尽可能少建索引。
      5.主键与聚集索引的关系
在数据库设计中,我们经常容易混淆主键和聚集索引的关系。因为如果我们建立主键的时候没有特别说明,SQL SERVER会默认在主键上建立聚集索引。同时由于聚集索引同时也是唯一索引,而且主键一般为较小的键。所以我们经常将主键作为聚集索引。但是这并不表示主键和聚集索引等同。

存储过程(SQL)编写规范

 

一、注释

在 SSMS 界面下右键 “存储过程” -> “新建存储过程” 生成的脚本即可。 在后期修改后加上修改的注释, 便于后期跟进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- =============================================
-- Author:      yenange
-- Create date: 2018-04-01
-- Description: 测试
-- Modify date: 2018-04-29
-- Modify desc: 增加参数
-- =============================================
CREATE PROCEDURE dbo.Proc_Test
     @p1 BIGINT
AS
BEGIN
     SET NOCOUNT ON ;
     SELECT @p1 AS r;
END
GO

二、前后有 BEGIN  ... END GO , 便于与其它脚本区分开。

三、保留字一律大写, 表名和函数或等对象一律加上架构名 dbo. ( 或其它 );

四、不要使用SELECT * 需要哪些字段,查询哪些字段, 尽可能少的返回结果集行的数量。

五、尽量避免 GOTO , 这会使可读性降低。

六、对于多表连接及查询, 可以使用别名用于简化。

七、性能相关:

 

1) Where子句尽量避免使用函数;
2) 避免在ORDER BY子句中使用表达式;
3) 限制在GROUP BY子句中使用表达式;
4) 慎用游标;
5) 避免隐式类型转换,例如字符型一定要用’’,数字型一定不要使用’’;
6) 查询语句一定要有范围的限定,避免全表扫描操作;
7) 慎用DISTINCT关键字;
8) 慎用OR关键字,可以用UNION ALL替代;
9) 除非必要,尽量用UNION ALL而非UNION
10) 使用EXISTS(SELECT 1)替count(*)来判断是否存在记录;
11) SET NOCOUNT ON 语句
把 SET NOCOUNT ON 语句放到存储过程和触发器中,作为第一句执行语句。

八、尽量使用索引

  1. SARG (Searchable Arguments)操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。

如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。

SARG包含以下操作符=、>、<、>=、<=、BETWEEN及部分情况下的LIKE。LIKE是否符合SARG,要看通配符%所在的位置。

属于 SARG 的情况:

属于 SARG不属于 SARG
LIKE 'abc%'LIKE '%abc%', LIKE '%abc'
type = 1, type>1, type<5type <> 1

      2. 尽可能不要包装字段:

      正确:日期 >= '2018-04-28' AND 日期< '2018-04-29'

      错误:DATEDIFF( DAY, 日期, '2018-04-28') =0

      3. 不要在SQL中判断参数

1
2
3
4
5
6
7
8
9
10
11
12
--前提:参数情况未知
DECLARE @sql NVARCHAR( MAX ),@account NVARCHAR(50)
SET @account = 'test'
--正确: 拼接SQL
SET @sql= 'SELECT [name] FROM user_data WHERE 1=1 '
IF @account IS NOT NULL
BEGIN
     SET @sql=@sql+ ' AND account=@account'
END
EXEC sp_executesql @sql,N '@account NVARCHAR(50)' ,@account
--错误:不拼接SQL,在 SQL 中判断
SELECT [ name ] FROM user_data WHERE @account IS NULL OR account=@account

      

九. 事务和锁
事务是数据库应用中和重要的工具,它有原子性、一致性、隔离性、持久性这四个属性,很多操作我们都需要利用事务来保证数据的正确性。在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞。具体以下方面需要特别注意:
1) 凡是不怕脏读的地方,使用NOLOCK提示查询优化器
在繁忙的系统中,对改善并发问题,是个不错的选择;
2) 在存储过程,触发器,以及SQL 簇中,尽可能按照相同的循序来访问相关的表。这样可以减少死锁的机会; 
3) 事务尽可能短
4) 在事务中涉及到数据修改量,尽可能小,提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。
5) 事务操作过程不应该有交互,因为交互等待的时候,事务并未结束,可能锁定了很多资源。
6) 尽可能低的设置锁,以及隔离的级别。
7) 尽量不要指定锁类型和索引,SQL SERVER允许我们自己指定语句使用的锁类型和索引,但是一般情况下,SQL SERVER优化器选择的锁类型和索引是在当前数据量和查询条件下是最优的,我们指定的可能只是在目前情况下更有,但是数据量和数据分布在将来是会变化的。

十、注意临时表和表变量的用法

在复杂系统中,临时表和表变量很难避免,关于临时表和表变量的用法,需要注意:

1) 如果语句很复杂,连接太多,可以考虑用临时表和表变量分步完成。
2) 如果需要多次用到一个大表的同一部分数据,考虑用临时表和表变量暂存这部分数据。
3) 如果需要综合多个表的数据,形成一个结果,可以考虑用临时表和表变量分步汇总这多个表的数据。
4) 表变量尽量加上主键,以提高查询和连接性能;
5) 一般五千条数据或以下可以用表变量,数据较多或需要复杂连接(需要其它索引)的情况下,应该用临时表。

 

十一、子查询的优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
--1) NOT IN、NOT EXISTS的相关子查询可以改用LEFT JOIN代替写法。
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   WHERE BusinessEntityID NOT IN ( SELECT BusinessEntityID                    FROM [AdventureWorks2012].[Person].[BusinessEntity] WITH (NOLOCK))
--可以改写成
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   LEFT JOIN [AdventureWorks2012].[Person].[BusinessEntity] BE WITH (NOLOCK)
         ON BEA.BusinessEntityID = BE.BusinessEntityID
   WHERE BE.BusinessEntityID IS NULL
--2)如果保证子查询没有重复 ,IN、EXISTS的相关子查询可以用INNER JOIN 代替。
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   WHERE BusinessEntityID IN ( SELECT BusinessEntityID  FROM [AdventureWorks2012].[Person].[BusinessEntity] WITH (NOLOCK))
--可以改写成:
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   INNER JOIN [AdventureWorks2012].[Person].[BusinessEntity] BE WITH (NOLOCK)
         ON BEA.BusinessEntityID = BE.BusinessEntityID
--3)不要用COUNT(*)的子查询判断是否存在记录,最好用LEFT JOIN或者EXISTS
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   WHERE ( SELECT COUNT (*) FROM [AdventureWorks2012].[Person].[BusinessEntity] WITH (NOLOCK))=0
--可以改写成:
SELECT BEA.[AddressID]
       ,BEA.[AddressTypeID]
   FROM [AdventureWorks2012].[Person].[BusinessEntityAddress] BEA WITH (NOLOCK)
   LEFT JOIN [AdventureWorks2012].[Person].[BusinessEntity] BE WITH (NOLOCK)

当然, 对于数据库优化来说, 没有一定之规, 数据量的大小、统计信息、服务器硬件等都会影响最终的效果, 最好的方式还是实际看执行计划。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值