服务器 频道

SQL Server 数据库中存储过程的自动化生成

  解析表列
  
  Syscolumns 表提供了许多必须的元数据信息,例如列名、ID、长度和是否允许空值。它还被用来连接 sysindexes 表来确定表的主键。同样可以通过 INFORMATION_SCHEMA.COLUMNS 视图获取列的默认值。
  
  既然所有的存储过程都使用相同的元数据信息,那么出于模块化和可维护性考虑,将其封装在独立的代码块中是件非常好的事情。SQL Server 的早期版本没有UDF(用户定义函数),使得模块化看起来很困难。但是 SQL Server 2000 具备了 UDF 特性,我们决定进一步采用该代码并将四个设计时存储过程中的公共特性进行模块化。创建五个新的 UDFs 来处理系统表和信息大纲视图,封装所有取得的元数据。
  
  毫无疑问,为了创建新的运行时存储过程,我们需要知道下面的关于表的元数据列信息:
  
  列名
  列的ID号
  列的数据类型
  列最大长度(包括字符和二进制数据)
  列的精度,或者值的位数(decimal和numeric数据)
  列的数值范围,或者小数点后的位数(decimal和numeric数据)
  列是否允许为null
  列是否是主键的一部分
  列是否是 Identity 列
  列的默认值
  
  这些信息的大多数来自 syscolumns 表,只有两个除外。默认值实际上来自INFORMATION_SCHEMA.COLUMNS 视图。数据类型名(datatype name)从 systypes 表中吸取,并且通过一个更复杂的 syscolumns,sysindexes 和 sysindexkeys 表联合来确定一个列是否是主键的一部分。它是如此的复杂,以至于我们将该功能封装到其自己的 UDF 中。
  
  让我们看一下 Figure 3 中的主要功能,它揭示了更多的元数据信息。这个 UDF 不是太复杂。正如你看到的,大多数元数据信息——除了一些简单的列重命名——被返回时未做任何修改,包括列名、列 ID、长度、精度、范围、是否允许为空和数据类型名字。接下来需要对这些信息做一点额外的工作。对于主键元数据,我们已创建另外一个 UDF 来确定表中一列是否是某个表主键的一部分。我们将马上检查这些额外的 UDFs 函数。
  
  让我们看一下 alternate 类型和 identity 状态。Syscolumns 状态字段的第8位(128)指示该列是否是一个 identity 列。(这对于了解何时创建 Insert 和 Update 脚本非常重要)。我们的简单公式对这个值实施一个逻辑与 (&),并将该结果包装在 Sign 函数中。如果该位被设置,则意味该列是 identity 列。c.status & 128 将返回值 128 。否则,将返回值 0 。Sign 函数当为正值时返回1,负数时返回 -1,0 值时返回 0。因此,如果列被评估为 identity 列,将返回值 1,否则返回0。
  
  alternate 类型被用来表示该数据类型在定义时是否要求额外的信息(长度、精度或范围)。我们将 character 和 binary 数据类型作为值是 1 的 alternate 类型,decimals 和 numerics 作为2,其它数据类型作为 0。这个值在存储过程用来确定长度、精度和范围是否需要加入到参数定义中。
  
  查找主键列
  
  正如你看到的,查找列的信息并不是很难。查找一个字段是否是主键的一部分稍微要费点力。有一个字段列表是能获取的。但要找到这些字段得在 syscolumns、sysindexes 和 sysindexkeys 表联合中并与我们请求的列进行比较(在 @sColumnName 参数中被传递到 UDF)。因此,查找主键的任务在单独的用户定义函数中较容易完成,因为我们可以将这个工作封装到某个单一的函数调用中。
  
  让我们考察一下这个函数,看看所发生的真相:
  
  CREATE FUNCTION dbo.fnIsColumnPrimaryKey
  (@sTableName varchar(128), @sColumnName varchar(128))
  RETURNS bit
  AS
  BEGIN
  DECLARE  @nTableID int,
  @nIndexID int,
  @i int
  
  SET @nTableID = OBJECT_ID(@sTableName)
  
  这个函数包含两个参数,表名和列名,如果指定列是表中的主键的一部分,函数将返回一个 bit 标志。我们接着声明一些存储过程中要用到的变量,并且赋予初始值。现在来到有趣的部分:查找主键信息。我们开始为表的主键索引找到索引ID,如下面的代码所示:
  
  SELECT @nIndexID = indid
  FROM   sysindexes
  WHERE id = @nTableID
  AND   indid BETWEEN 1 And 254
  AND   (status & 2048) = 2048
  ORDER BY indid
  
  IF (@nIndexID Is Null)
  RETURN 0
  
  现在将这个表的主键索引的索引 ID 赋予变量 @nIndexID。状态列的第12位(2048)指示是否是主键索引。如果不是主键,则没有记录被返回,并将 @nIndexID 设置为空值。如果退出函数时@nIndexID 包含一个空值,返回0值。换句话说,如果没有主键索引,那么列就不是主键的一部分。现在我们再次在主键索引列的列表中检查要求的列 (@sColumnName)。  IF @ColumnName IN
  (SELECT sc.[name]
  FROM    sysindexkeys sik
  INNER JOIN syscolumns sc ON sik.id = sc.id AND sik.colid =
  sc.colid
  WHERE   sik.id = @nTableID
  AND    sik.indid = @nIndexID    )
  BEGIN
  RETURN 1
  END
  
  RETURN 0
  END
  
  用前面获得的 IndexID,我们从 syscolumns 和 sysindexkeys 的联合中获得列名。这些表通过列 ID 和对象 ID 进行联合。通过 WHERE 从句设置检索条件,因此我们可以只选择要求的表的索引列(sik.id = @nTableID),并且仅仅选择主键索引(sik.indid = @nIndexID)。如果 @sColumnName 在返回的列的列表中,返回值1,否则,返回值0,标识匹配没有找到。
  
0
相关文章