SQL开发规范

SQL开发规范
作者:Michael.Wang 2006-07-16
Email:Wang_fa_yi@126.com
1. 目录
2. 书写标准 
   
2.1 书写格式 
   
2.2 缩进 
   
2.3 换行 
   
2.4 空格 
   
2.5 注释 
3. SQL优化性能建议 
   
3.1 书写SQL语句优化细则 
   
3.2 选用不同的T-SQL系统自带保留字和函数进行优化 
   
3.3 排序注意事项 
   
3.4 选用索引注意事项 
   
3.5 其他经验性规则 
4. T-SQL例子代码 
   
4.1 新建表的例子代码 
   
4.2 新建存储过程例子代码 
   
4.3 新建函数例子代码 
   
4.4 其他微软相关例子代码(主要是注意代码的缩进、换行、大小写、空格等格式)
 

1. 引言
   进行SQL和T
-SQL编程时常常会忽略编码标准,但这些标准却是开发小组顺利开展工作的关键工具,SQL和T-SQL 代码的格式似乎并不重要,但一致的格式可以使您的同事(不论是同一小组的成员还是更大范围的SQL和T-SQL 开发团队的成员)更轻松地浏览和理解您的代码。T-SQL 语句有一个结构,遵循一目了然的结构使您可以更轻松地查找和确认语句的不同部分,可以加快开发效率。
2. 书写标准
  
2.1 书写格式
    
1) SQL语句的SQL保留字大写。
    
2) 变量名称使用混合大小写,数据类型使用小写。
    
3) 新建的表以TB_作前缀比如TB_User,视图VW_,存储过程PROC_,函数FUN_,表名称不加s;如果表比较多,一般分模块来区分,比如TB_SM_User,表示系统模块的用户表。
    
4) 当命名存储过程名或者视图和函数名时,如果动词加名称来描述该功能时,使用符合我们习惯的用法,即动词在前名词在后,如:GetUserInfo, AddUserInfo, EditUserInfo, DeleteUserInfo等。
    
5) 使用别名:当一个T-SQL语句中涉及到多个表时,始终使用表名别名(一般取表名的头两个字母)来限定字段名,避免使用table1、table2、t1、t2等类似命名。这使其他人阅读起来更清楚,避免了含义模糊的引用。
  
2.2 缩进 
    
1) 一般缩进为4个空格。
    
2) 不要用Tab制表符来作缩进。
  
2.3 换行
    
1Select/From/Where/Order by/Group by等子句必须另起一行写。
    
2) Select子句内容如果只有一项,与Select同行写。
    
3) Select子句内容如果多于一项,每一项单独占一行,在对应Select的基础上向右缩进4空格。
    
4) From子句内容如果只有一项,与From同行写。
    
5) From子句内容如果多于一项,每一项单独占一行,在对应From的基础上向右缩进4个空格。
    
6) Where子句的条件如果有多项,每一个条件占一行,以AND或者OR开头,在对应Where的基础上向右缩进4个空格。
    
7) (Update)Set子句内容每一项单独占一行,无缩进。
    
8) Insert子句内容每个表字段单独占一行,无缩进;values每一项单独占一行,无缩进。
    
9) SQL文中间不允许出现空行。
    
10) 存储过程或函数输入参数命名以in开头,如@in_Year;输出参数以out开头,如@out_money;局部变量首字母小写,第二个单词起的每个单词的首字母大写,不使用“_”,如@customerCount
  
2.4 空格
    
1) 连接符or、inand、以及=、<=>=等前后加上一个空格。
    
2) 逗号之后必须接一个空格。
    
3) 关键字、保留字和左括号之间必须有一个空格。
  
2.5 注释
    
1) 对较为复杂的SQL语句加上注释,说明算法、功能。注释风格:注释单独成行、放在语句前面。
    
2) 应对不易理解的分支条件表达式加注释。
    
3) 对重要的计算应说明其功能。
    
4) 过长的函数实现,应将其语句按实现的功能分段加以概括性说明。
    
5) 常量及变量注释时,应注释被保存值的含义(必须),合法取值的范围(可选)。
    
6) 可采用单行/多行注释。(-- 或 /* */ 方式)。
3. SQL优化性能建议
  
3.1 书写SQL语句优化细则
    
1) 尽量避免相同语句由于书写格式的不同,而导致多次语法分析。
    
2) 多表连接时,使用表的别名来引用列。
    
3) 不要在任何代码中使用 SELECT *
    
4) 避免使用 GOTO,错误处理程序中除外。
    
5) where条件中尽量减少使用常量比较,改用主机变量。
  
3.2 选用不同的T-SQL系统自带保留字和函数进行优化
    
1) 优先使用 SELECTINTO(创建新表),然后使用 INSERT INTOSELECT,以避免大量死锁。
    
2) 尽量少用嵌套查询。如必须,请用not exist代替not in子句。
    
3) 用多表连接代替EXISTS子句。
    
4) 少用DISTINCT,用EXISTS代替。
    
5) 使用UNION ALL提高性能 。
    
6in、or子句常会使用工作表,使索引失效;如果不产生大量重复值,可以考虑把子句拆开;拆开的子句中应该包含索引。
  
3.3 排序注意事项
    
1) 大量的排序操作影响系统性能,所以尽量减少order by和group by排序操作。如必须使用排序操作,请遵循如下规则:
      a. 排序尽量建立在有索引的列上。
      b. 如结果集不需唯一,使用union all代替union。
  
3.4 选用索引注意事项
    
1) 尽量避免对索引列进行计算。如对索引列计算较多,请提请系统管理员建立函数索引。
    
2) 尽量注意比较值与索引列数据类型的一致性。
    
3) 对于复合索引,SQL语句必须使用主索引列。
    
4) 索引中,尽量避免使用NULL。
    
5) 对于索引的比较,尽量避免使用NOT=!=)。
    
6) 查询列和排序列与索引列次序保持一致。
  
3.5 其他经验性规则
    
1) 避免嵌套连接。例如:A = B and B = C and C = D
    
2) 为了提高性能,应优先使用连接,然后使用子查询或嵌套查询。
    
3) 系统可能选择基于规则的优化器,所以将结果集返回数据量小的表作为驱动表(from后边最后一个表)。
    
4) 供别的文件或函数调用的函数,绝不应使用全局变量交换数据。
    
5) 查询的WHERE过滤原则,应使过滤记录数最多的条件放在最前面。
    
6) 任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
4. T-SQL例子代码
  
4.1 新建表的例子代码
--=================================================================
--
Spec: 创建订单信息表TB_Order
--
CreateBy: Michael.Wang
--
CreateDate: 2006-07-06
--
Version: 0.1
--
Remark: 注意关联外键表: TB_Employee,TB_Customer,TB_Shipper;以及
--
        子表(明细表)TB_Order_Detail
--
ModifyBy1:
--
ModifyDate1:
--
ModifyRemark1:
--
ModifyBy2:
--
ModifyDate2:
--
ModifyRemark2:
--
==================================================================
USE Northwind
GO

CREATE TABLE dbo.TB_Order
(
    OrderID         
int IDENTITY(1,1NOT NULL,--主键
    CustomerID      nchar(5)          NULL,    --外键对应客户表TB_Customer
    EmployeeID      int               NULL,    --外键对应员工表TB_Employee
    OrderDate       datetime          NULL,
    RequiredDate    
datetime          NULL,
    ShippedDate     
datetime          NULL,
    ShipVia         
int               NULL,    --外键对应供应商表TB_Shipper
    Freight         money             NULL DEFAULT (0),
    ShipName        
nvarchar(40)      NULL,
    ShipAddress     
nvarchar(60)      NULL,
    ShipCity        
nvarchar(15)      NULL,
    ShipRegion      
nvarchar(15)      NULL,
    ShipPostalCode  
nvarchar(10)      NULL,
    ShipCountry     
nvarchar(15)      NULL,
    
CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED
    (
        OrderID 
ASC
    )
    
ON PRIMARY
 )

GO
-----可以在设计窗口中直接设计图表产生下面代码也可以手工加上下面的代码--------
USE Northwind
GO
ALTER TABLE dbo.Orders  WITH NOCHECK ADD  CONSTRAINT FK_Orders_Customers FOREIGN KEY(CustomerID)
REFERENCES dbo.Customers (CustomerID)
GO
ALTER TABLE dbo.Orders CHECK CONSTRAINT FK_Orders_Customers
GO
ALTER TABLE dbo.Orders  WITH NOCHECK ADD  CONSTRAINT FK_Orders_Employees FOREIGN KEY(EmployeeID)
REFERENCES dbo.Employees (EmployeeID)
GO
ALTER TABLE dbo.Orders CHECK CONSTRAINT FK_Orders_Employees
GO
ALTER TABLE dbo.Orders  WITH NOCHECK ADD  CONSTRAINT FK_Orders_Shippers FOREIGN KEY(ShipVia)
REFERENCES dbo.Shippers (ShipperID)
GO
ALTER TABLE dbo.Orders CHECK CONSTRAINT FK_Orders_Shippers
4.2 新建存储过程例子代码
--====================================================================
--
Spec: 统计某个客户在某一时段的定单总金额、折扣金额以及净金额
--
Parameter: CompanyName --客户名称;in_Year  --某年份;
--
           StartMonth  --起始月份;EndMonth --结束月份
--
CreateBy: Michael.Wang
--
CreateDate: 2006-07-06
--
Version: 0.1
--
Remark: 统计某个客户1997年3月到8月的定单总金额、折扣金额以及净金额;
--
        统计结果为:CompanyName,TotalMoney,NetMoney ,DiscountMoney
--
        总金额TotalMoney=UnitPrice*Quantity;
--
        折扣金额DiscountMoney=总金额*Discount;
--
        净金额NetMoney=总金额-折扣金额
--
ModifyBy1:
--
ModifyDate1:
--
ModifyRemark1:
--
ModifyBy2:
--
ModifyDate2:
--
ModifyRemark2:
--
==================================================================
USE Northwind
GO

CREATE PROC PROC_StatCustomerMoney
(
    
@in_CompanyName nvarchar(40), --客户名称
    @in_Year         int,         --某年份
    @in_StartMonth   int,         --某起始月份
    @in_EndMonth     int          --某结束月份
)
AS
    
SELECT Ct.CompanyName,
        
SUM(De.UnitPrice * De.Quantity) AS TotalMoney,  --定单总金额
        SUM(De.UnitPrice * De.Quantity * (1 - De.Discount)) AS NetMoney , --折扣金额
        SUM(De.UnitPrice * De.Quantity * De.Discount) AS DiscountMoney  --净金额
    FROM Customers Ct, Orders Od, [Order Details] De
    
WHERE Ct.CustomerID = Od.CustomerID
        
AND Od.OrderID = De.OrderID
        
AND DATEPART(yyyy, Od.OrderDate) = @in_Year
        
AND DATEPART (mm, Od.OrderDate) >= @in_StartMonth
        
AND DATEPART (mm, Od.OrderDate) <= @in_EndMonth
        
AND Ct.CompanyName = @in_CompanyName
    
GROUP BY Ct.CompanyName
4.3 新建函数例子代码
--=================================================================
--
Spec: 获取某年的某个客户的月平均定单金额
--
Parameter: CompanyName --客户名称;in_Year  --某年份;
--
Return: AVGMoney --平均定单金额
--
CreateBy: Michael.Wang
--
CreateDate: 2006-07-06
--
ModifyBy:
--
ModifyDate:
--
Version: 0.1
--
Remark: 该年份里有发生业务数据的月定单总金额的平均值
--
=================================================================
USE Northwind
GO

CREATE FUNCTION FUN_GetCustomerYearAvgMoney
(
    
@in_CompanyName nvarchar(40), --客户名称
    @in_Year        int           --某年份
)
RETURNS money
AS
    
DECLARE @avgMoney money
    
SELECT @avgMoney = AVG(TotalMoney)
    
FROM
    ( 
SELECT Ct.CompanyName,
            
DATEPART(mm, Od.OrderDate) As "month",
            
SUM(De.UnitPrice * De.Quantity) AS TotalMoney
     
FROM Customers Ct, Orders Od, [Order Details] De
     
WHERE Ct.CustomerID = Od.CustomerID
         
AND Od.OrderID = De.OrderID
         
AND DATEPART(yyyy, Od.OrderDate) = @in_Year
         
AND Ct.CompanyName = @in_CompanyName
     
GROUP BY Ct.CompanyName, DATEPART(mm, Od.OrderDate)
    ) 
AS CompanyTotalMoney
    
RETURN @avgMoney
4.4 其他微软相关例子代码(主要是注意代码的缩进、换行、大小写、空格等格式)
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      
WHERE TABLE_NAME = 'author_sales')
   
DROP TABLE author_sales
GO
IF EXISTS(SELECT name FROM sysobjects
      
WHERE name = 'get_author_sales' AND type = 'P')
   
DROP PROCEDURE get_author_sales
GO
USE pubs
CREATE TABLE author_sales
( data_source   
varchar(20),
  au_id         
varchar(11),
  au_lname      
varchar(40),
  sales_dollars 
smallmoney
)
GO
CREATE PROCEDURE get_author_sales
AS
   
SELECT 'PROCEDURE', authors.au_id, authors.au_lname,
      
SUM(titles.price * sales.qty)
   
FROM authors INNER JOIN titleauthor
      
ON authors.au_id = titleauthor.au_id INNER JOIN titles
      
ON titleauthor.title_id = titles.title_id INNER JOIN sales
      
ON titles.title_id = sales.title_id
   
WHERE authors.au_id like '8%'
   
GROUP BY authors.au_id, authors.au_lname
GO
--INSERTSELECT example
USE pubs
INSERT author_sales
   
SELECT 'SELECT', authors.au_id, authors.au_lname,
      
SUM(titles.price * sales.qty)
   
FROM authors INNER JOIN titleauthor
      
ON authors.au_id = titleauthor.au_id INNER JOIN titles
      
ON titleauthor.title_id = titles.title_id INNER JOIN sales
      
ON titles.title_id = sales.title_id
   
WHERE authors.au_id LIKE '8%'
   
GROUP BY authors.au_id, authors.au_lname

--INSERTEXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales

--INSERTEXECUTE('string') example
INSERT author_sales
EXECUTE
(
'
SELECT 
''EXEC STRING'', authors.au_id, authors.au_lname,
   SUM(titles.price * sales.qty)
   FROM authors INNER JOIN titleauthor
      ON authors.au_id = titleauthor.au_id INNER JOIN titles
      ON titleauthor.title_id = titles.title_id INNER JOIN sales
      ON titles.title_id = sales.title_id
   WHERE authors.au_id like 
''8%''
   GROUP BY authors.au_id, authors.au_lname
')

--Show results.
SELECT * FROM author_sales
posted on 2006-11-21 23:09  IMustDo  阅读(1081)  评论(1编辑  收藏  举报