对于设计和创建数据库完全是个新手?没关系,Joe Celko, 世界上读者数量最多的SQL作者之一,会告诉你这些基础。和往常一样,即使是最专业的数据库老手,也会给他们带来惊喜。Joe是DMBS杂志是多年来最受 读者喜爱的作者。他在美国、英国,北欧,南美及非洲传授SQL知识。他在ANSI / ISO SQL标准委员会工作了10年,为SQL-89和SQL-92标准做出了杰出贡献。
Joe Celko会谈下数据库设计里存储过程和它的位置。他所写的是令人深思的东西,即使是有经验的数据库开发人员。
在第一篇,我们因它们是什么并区分它们命名数据元。在第二篇,我们用SQL里给我们的数据类型和简单的行或列约束来模型化数据元。在第三篇,我们把这些行放入表成为实体,关系和辅助数据。在第四篇,我们有了基础表,把它们连接在一起,增加从索引,并创建了视图。
这应该会给你万事俱备,却是错误的感觉。在一个非常简单的数据库里,这是对的。但在一个真正的数据库有更多的架构来考虑。在这些架构外的其他东西事:游标(cursors)、触发器(triggers)和存储过程(stored procedures)。有更多像整理、翻译,特权和诸如此类的东西。我将只处理这3个东西——游标(cursors)、触发器(triggers)和存储过程(stored procedures)——我只用通常的方式命名。尽管ANS/ISO标准里,T-SQL和其他产品可以获得更高的占有欲。理由很简单:这些东西建立在早期SQL产品使用的现有文件系统上。这些程序结构是用来弥补在早期产品里缺少申明式代码。这些供应商有锁在今天“代码博物馆”不能地址他们客户群的用户。
SQL允许存储过程代码模块在架构里保存。同时在标准SQL里有SQL/PSM语言,你会使用像T-SQL的专门语言。这些语言通常是Algol家族的成员;那就是说他它们有IF-THEN-ELSE,WHILE循环和有BEGIN-END作用域的代码块。
这些专用语言的大多数从未想用做程序开发。对于T-SQL的首要规则(The rules of thumb)是不写超过50行的的过程,且不使用PRINT。但事实上,你可以避免所有的面向过程,每个表像文件和代码一样对待,好像数据库是个过程化的文件系统。如果你喜欢疼痛,大可敲个钉子到你身体,所以不用纠结。
存储过程的目的更像个视图。它们授予所有用户在所有时间会用同样的方式做同样的工作。视图封装了一个查询并给它一个名称,因此创建了一个虚拟表。存储过程用同样的方式封装了UPDATE,INSERT,DELETE和SELECT,但增加了参数。
在存储过程名称里一个参数(parameter )就是个“持有人(place holder)”,参数值(argument )是传给存储过程的实际值。T-SQL参数过去只限制于简单的标量值。现在,它们可以是表值和XML字符。让我们从简单的标量参数开始。
编译器读取参数值,并检查数据类型,范围和确保它是有效的其他事项。编译器会做比你想象还多的事。看下这个T-SQL:
1 SELECT ISDATE ('2010-01-01'); -- TRUE 2 SELECT CAST ('2010-01-01' AS DATE); -- no problem 3 SELECT ISDATE ('010-01-01'; --TRUE 4 SELECT CAST ('010-01-01' AS DATE); -- error
同事,浮点表示法有点意思。如果你以DECIMAL或FLOAT转化指数计数法,肯定没问题:
1 SELECT CAST (62.3E8 AS DECIMAL(18,5)); -- returns 6230000000.00000 2 SELECT CAST (62.3E8 AS FLOAT); -- returns 6230000000
但现在把字符串的指数计数法,尝试转化它为DECIMAL或FLOAT,你肯定会出现问题:
1 SELECT CAST ('62.3E8' AS DECIMAL(18,5)); -- error 2 SELECT CAST ('62.3E8' AS FLOAT); -- returns 6230000000
如果转化字符,你要用这样的指数代码:
1 SELECT CAST(CAST ('62.3E8' AS FLOAT) AS DECIMAL(18,5)); -- 6230000000.00000
现在尝试传这些测试值作为参数,看看它们的结果:
1 CREATE PROCEDURE Test 2 (@in_test_date DATE, 3 @in_test_decimal DECIMAL(18,5)) 4 AS 5 BEGIN 6 SELECT @in_test_date, @in_test_decimal; 7 END;
EXEC Test '2010-01-01', 62.3E8;
返回值(2010-01-01, 6230000000.00000)和预期的一样。
你不能传表达式作为参数值,但你可以传本地变量,它是表达式值预先设置的。这就是说这个会报错:
EXEC Test '2010-01-01', 62.3 * POWER(10, 8) ;
但这个会成功执行:
1 BEGIN 2 DECLARE @local_decimal DECIMAL (18,5); 3 SET @local_decimal = 62.3 * POWER(10, 8); 4 EXEC Test '2010-01-01', @local_decimal ; 5 END;
你会看到新的SQL编程会尝试传递XML或CSV(逗号分割值(Comma Separated Values))列表字符作为参数值。它们增加了解析器的负担(XML解析器或任何写出来用来分割CSV字符的任何代码)并让它们的数据完整性受到危险。SQL Server可以处理超过2000的参数值,对于现实中的情况已经足够能应付。
在参数列表里另一个未使用的功能是默认值。这个语法非常简单。参数声明后一个“=”和一个合适的定值。
如果参数值没有提供,就会使用默认值。
CREATE PROCEDURE Test (@in_test_date DATE = '2010-01-01', @in_test_decimal DECIMAL(18,5) = 0.00000 ) AS BEGIN SELECT @in_test_date, @in_test_decimal; END;
除非你特别分配参数值到参数,它们是从左到右的顺序分配。
EXEC Test; -- returns (2010-01-01, 0.00000) EXEC Test '2010-12-25' -- returns (2010-12-25, 0.00000) EXEC Test @in_test_date = 789; -- returns (2010-01-01, 789.00000)
最后,参数可以用作输出。这就是说它需要在调用的模块里有个本地变量,这样的话,返回值才有地方可去。这是展示这个语法的例子:
1 CREATE PROCEDURE Test 2 (@in_test_date DATE OUTPUT, 3 @in_test_decimal DECIMAL(18,5) OUTPUT) 4 AS 5 BEGIN 6 SET @in_test_date = '2010-12-25'; 7 SET @in_test_decimal = 789; 8 END; 9 10 BEGIN 11 DECLARE @local_date DATE; 12 DECLARE @local_decimal DECIMAL(18,5); 13 SELECT @local_date, @local_decimal; --returns (NULL, NULL) 14 EXEC Test @local_date OUTPUT, @local_decimal OUTPUT 15 SELECT @local_date, @local_decimal; -- returns (2010-12-25, 789.00000) 16 END;
关于存储过程标题就讲这些;那存储过程的具体内容呢?嗯,我们现在暂时不讲。我们先讲下原则让,然后再看看特定的工具。我们需要一个高度来看如何编写代码——软件工程(Software Engineering)。
软件工程的基础不在SQL里修改。但现实完全不一样。我们大多数(从学LISP,APL,FP,Haskell或其它外来语言学起,对这些程序员例外)学过从Algol-60进化而来的结构化编程语言。适用于过程化语言的原则同样适用于SQL存储过程。
在近1970年,我们发现我们可以在程序里写出更好(更快,正确,更易维护的)的代码,在代码里有本地代码块规则和代码模块,都是一个入口一个出口。我们避免GO TO语句,并使用简单的一系列控制结构。这是结构化编程的进步。
内聚度是一个模块做且只做一件事会很好:那是逻辑上的内聚性。模块应该高内聚。模块的命名格式应该是“<动作><对象>”,这里“<对象>”是数据模型里特定的逻辑单元,“<动作>”是单一明确的行动。有很多内聚类型。我们从最差到最好对它们排名。
如果在你的软件工程课程里错过这些,你可以网上找下它们的具体定义。
耦合度是模块之间的相互独立性。如果你的模块需要特定的顺序执行,它们是强耦合度。如果它们之间可以独立运行,可以像乐高一样堆积,它们是松散或弱耦合的。耦合有好几类,从低到高排序是:
这个在我的《SQL 编程风格》书里关于存储过程编写的章节里会有简单的介绍。同时,你也可以阅读下DeMarco, Yourdon, Constantine, Myers或其它软件工程先驱。这已经不是简单的SQL编程了。在你写任何语言的代码前,这些都是你应该知道的。
一般而言,好的存储过程是高内聚,低耦合,它不使用控制结构的缺陷,除非是必须的。对于过程化开发人员,这个是意外。理由是尽可能多的把“编程的元素”放入单纯的SQL,这样优化器可以更好的处理代码。
如何实现?下篇会告诉你。
http://www.sqlservercentral.com/articles/Stairway+Series/Procedures+in+Database+design/70891/