数据库设计(5/9):存储过程名称

简介:

存储过程名称(Procedure Headers)

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语句,并使用简单的一系列控制结构。这是结构化编程的进步。

内聚度(Cohesion)

内聚度是一个模块做且只做一件事会很好:那是逻辑上的内聚性。模块应该高内聚。模块的命名格式应该是“<动作><对象>”,这里“<对象>”是数据模型里特定的逻辑单元,“<动作>”是单一明确的行动。有很多内聚类型。我们从最差到最好对它们排名。

  1. 偶然内聚(coincidental cohesion)是模块部分任意分组。一个偶然模块无关行为的火车残骸。它是“Lady GaGa,鱿鱼和汽车”模块,它们的描述会是复合且复杂的句子。这个在SQL里的最佳例子是OTLT(查询表(One True Lookup Table))设计缺陷。这是它里面有对整个实体架构的查询表。
  2. 逻辑性内聚(logical cohesion)是模块部分按它们逻辑上做同样的事进行分组,即使本质上它们是不同的。在SQL里最常见的例子是在任何表上进行更新、插入或删除的通用模块——在“Lady GaGa,鱿鱼和汽车”上。用动态SQL,XML和CLR进行实现。
  3. 时间性内聚(temporal cohesion)是模块部分按它们处理时分组。例如,对于整个系统,进行所有初始化工作的模块。
  4. 过程性内聚(procedural cohesion)是模块部分是因为它们总是紧跟特定操作顺序。例如,当一个用户登录到数据库,我们会检查用特权可并记录登录。不把这些事放在各个模块里,在上层的控制下,我们写集成的一块来完成这一切。
  5. 通信性内聚(communicational cohension)是模块部分因它们在同样的数据库元上操作进行分组。例如在一个存储过程里,UPDATE的一系列语句影响同样列,基于先前模块做的事。
  6. 顺序性内聚(sequential cohesion)是模块部分因为从一个部分的输出是另一个部分的输入,像流水线一样。在SQL里,可以看下临时表的使用,在磁带文件系统里用来替换。
  7. 功能性内聚(functional cohesion)是模块只做一件定义明确的工作,像一个数学函数。这才是我们模块里想要的,它是功能性编程语言的基础。没有副作用或外部数据。

如果在你的软件工程课程里错过这些,你可以网上找下它们的具体定义

耦合度(Coupling)

耦合度是模块之间的相互独立性。如果你的模块需要特定的顺序执行,它们是强耦合度。如果它们之间可以独立运行,可以像乐高一样堆积,它们是松散或弱耦合的。耦合有好几类,从低到高排序是:

  1. 内容耦合(Content coupling)是一个模块的修改依赖于另一个模块的内部操作。控制模块的跳入与调出。在SQL里,用户自定义函数(UDF)的使用和CLR是最好的例子。直到运行的时候你才知道会发生什么。
  2. 公共耦合(Common coupling)是两个模块共用同样的全局数据。这听起来像SQL里的表,但它指的是在公共数据上有2个做同样工作的方法。做同样的工作很快变成几乎一样的工作。
  3. 外部耦合(External coupling)是两个模块共享一个外部强加的数据格式,通信协议,或设备接口。理想的,我们不需要担心外部环境。这是在数据库里,分层架构不做任何形式或包装。
  4. 控制耦合(Control coupling)是一个模块通过传递标记控制执行。直到它的主模块告诉它,你不知道这个模块会做什么。这也是在SQL编程里,使用BIT标记不好的一个原因。
  5. 标记耦合(Stamp coupling)是模块共享复合数据结构,且只使用它的一部分。在SQL里可以使用视图实现。
  6. 数据耦合(Data coupling)是模块共享简单数据元。想下传递参数;这是只共享的数据元。
  7. 非直接耦合(Message Coupling)是最宽松的耦合类型。模块不相互依赖,它们使用公共接口来交换少参数的信息。这更多是面向对象的方法,但你会在触发器、异常处理和其他SQL特性里看到它。

这个在我的《SQL 编程风格》书里关于存储过程编写的章节里会有简单的介绍。同时,你也可以阅读下DeMarco, Yourdon, Constantine, Myers或其它软件工程先驱。这已经不是简单的SQL编程了。在你写任何语言的代码前,这些都是你应该知道的。

好的SQL存储过程

一般而言,好的存储过程是高内聚,低耦合,它不使用控制结构的缺陷,除非是必须的。对于过程化开发人员,这个是意外。理由是尽可能多的把“编程的元素”放入单纯的SQL,这样优化器可以更好的处理代码。



本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/5604708.html,如需转载请自行联系原作者

相关文章
|
1月前
|
存储 SQL 关系型数据库
【MySQL 数据库】9、存储过程
【MySQL 数据库】9、存储过程
200 0
|
2月前
|
存储 数据库
Navicate 如何导出数据库中的存储过程、事件、视图等?
Navicate 如何导出数据库中的存储过程、事件、视图等?
|
6月前
|
存储 SQL Go
数据库:存储过程实验
一、实验目的及要求 目的 掌握存储过程的编写与调用 要求 掌握存储过程的编写;掌握存储过程的调用
38 0
|
7月前
|
存储 SQL Oracle
Oracle数据库批量删除表、视图、序列、存储过程、函数脚本
Oracle数据库批量删除表、视图、序列、存储过程、函数脚本
68 0
|
3月前
|
存储 Java 数据库
JAVAEE框架数据库技术之13_oracle 之PLSQL技术及存储过程和函数(二)
JAVAEE框架数据库技术之13_oracle 之PLSQL技术及存储过程和函数
34 0
|
26天前
|
存储 SQL 数据挖掘
视图、触发器和存储过程:提升数据库功能
视图、触发器和存储过程:提升数据库功能
19 1
|
2月前
|
存储 数据库
【数据库】分支与循环&函数&存储过程
【数据库】分支与循环&函数&存储过程
21 1
|
2月前
|
存储 Linux 数据库
期末速成数据库极简版【存储过程】(5)
期末速成数据库极简版【存储过程】(5)
17 0
|
2月前
|
Java 数据库连接 数据库
Windows7 64位 连接Access数据库“未发现数据源名称并且未指定默认驱动程序“的解决办法
Windows7 64位 连接Access数据库“未发现数据源名称并且未指定默认驱动程序“的解决办法
|
7月前
|
存储 JSON BI
如何使用事物码 SAT 查找某个 SAPGUI 屏幕字段对应的后台存储数据库表的名称试读版
如何使用事物码 SAT 查找某个 SAPGUI 屏幕字段对应的后台存储数据库表的名称试读版
52 0