代码改变世界

数据库之创建存储过程

2017-11-01 15:36  糯米粥  阅读(11238)  评论(0编辑  收藏  举报

sql 版本

 

 

 

 

sql servre 帮助文档中对存储过程的解释

  创建存储过程。存储过程是已保存的 Transact-SQL 语句集合,或对 Microsoft .NET Framework 公共语言运行时 (CLR) 方法的引用,

  可接收并返回用户提供的参数。可以创建过程供永久使用,或在一个会话(局部临时过程)中临时使用,或在所有会话(全局临时过程)中临时使用

创建存储过程可以用 proc或者procedure关键字 proc是简写

 

 

目前有个简单的库(TestInfo),库里面有个简单的表(Show) 表里面有些简单的数据

 

编写个简单的存储过程,没有任何参数和返回值的存储过程

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL --存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
AS
SELECT * FROM Show

执行sql语句成功后,在sql中可以看到存储过程已经创建

 

 

OBJECT_ID(object_name,'object_type);函数用于判断对象是否存在
object_type对应的类型如下
  AF = Aggregate function (CLR)
  C = CHECK constraint --检查约束
  D = DEFAULT (constraint or stand-alone)
  F = FOREIGN KEY constraint --外键约束
  FN = SQL scalar function --函数
  FS = Assembly (CLR) scalar-function
  FT = Assembly (CLR) table-valued function
  IF = SQL inline table-valued function
  IT = Internal table
  P = SQL Stored Procedure --存储过程
  PC = Assembly (CLR) stored-procedure --CLR存储过程
  PG = Plan guide
  PK = PRIMARY KEY constraint --主键约束
  R = Rule (old-style, stand-alone) --规则
  RF = Replication-filter-procedure
  S = System base table --数据库
  SN = Synonym
  SQ = Service queue
  TA = Assembly (CLR) DML trigger --CLR触发器
  TF = SQL table-valued-function
  TR = SQL DML trigger --DML触发器
  U = Table (user-defined) --数据表
  UQ = UNIQUE constraint --唯一约束
  V = View --视图
  X = Extended stored procedure

比如上面的判断存储过程是否存在 OBJECT_ID('proc_show01','P') 获取存储过程名为:proc_show01
判断表是否存在 OBJECT_ID('info','U') 获取表名为:info
或者:
select * from sysobjects where name='info' and type='u'

 SQL Server支持五种类型的完整性约束
  NOT NULL (非空)--防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值.
  CHECK (检查)--检查在约束中指定的条件是否得到了满足.
  UNIQUE (唯一)--保证在指定的列中没有重复值.在该表中每一个值或者每一组值都将是唯一的.
  PRIMARY KEY (主键)--用来唯一的标识出表的每一行,并且防止出现NULL值,一个表只能有一个主键约束.
  POREIGN KEY (外部键)--通过使用公共列在表之间建立一种父子(parent-child)关系,在表上定义的外部键可以指向主键或者其他表的唯一键.

 

在.Net转编写测试代码。调用刚创建的存储过程

 /// 执行存储过程
        /// </summary>
        /// <param name="cmdText">存储过程的名称</param>
        /// <param name="param">存储过程参数</param>
        /// <returns></returns>

        public static DataTable GetPro2(string cmdText, params SqlParameter[] param)
        {
            DataTable dt = new DataTable();
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                SqlCommand cmd = new SqlCommand(cmdText, conn);
                // 指定执行语句为存储过程
                cmd.CommandType = CommandType.StoredProcedure;
                if (param != null && param.Length != 0)
                {
                    cmd.Parameters.AddRange(param);
                }
                SqlDataAdapter dp = new SqlDataAdapter(cmd);
                dp.Fill(dt);
            }
            return dt;
        }

 

  成功后的界面
 

 

 

编写一个有输入参数的存储过程

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @name nvarchar(20)
)
AS
SELECT * FROM Show where @name=Name

 

测试看结果,传入 name="张三"

 

 

编写存储过程的可空参数,当存储过程参数有默认值的时候,

那么.net在调用存储过程的时候。可以不用传参数。否则如果不传这会报错

所以:

除非定义了参数的默认值或者将参数设置为等于另一个参数,否则用户必须在调用过程时为每个声明的参数提供值

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL --存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @name nvarchar(20) = null --默认值为空
)
AS
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
end
select * from Show where @name = name

 

测试。当不传值的时候,默认查询的是 “刘德华”

 

 

 

创建有输入参数和输出参数的存储过程

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @id int output, --输出参数,输出id
    @name nvarchar(20) = null --默认值为空
)
AS
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    set @id=0
    select * from Show where name = @name
    --return
end
else
    begin
        set @id=(select ID from Show where name = @name)
        select * from Show where name = @name
    end

 

既然有输出参数。那么得修改上面的.Net代码。如下

/// <summary>
        /// 调用存储过程
        /// </summary>
        /// <param name="cmdText">存储过程名称</param>
        /// <param name="v1">输出参数(这里是ID)</param>
        /// <param name="param">参数</param>
        /// <returns></returns>
        public static DataTable GetPro1(string cmdText, out int v1, params SqlParameter[] param)
        {
            DataTable dt = new DataTable();
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                SqlCommand cmd = new SqlCommand(cmdText, conn);
                // 指定执行语句为存储过程
                cmd.CommandType = CommandType.StoredProcedure;
                if (param != null && param.Length != 0)
                {
                    cmd.Parameters.AddRange(param);
                }
                SqlDataAdapter dp = new SqlDataAdapter(cmd);
                //dp.SelectCommand.Parameters.AddRange(param);
                dp.Fill(dt);

                /*
                  获取输出参数。
                 * 这里在组装参数的时候。指定了数组的第一个数是输出参数
                 */
                v1 = Convert.ToInt32(param[0].Value); 
          //也可以通过SqlCommand根据参数名称获取存储过程的返回值,跟param[0].Value是同样的效果
                object id = cmd.Parameters["@id"].Value; }
return dt; }

 

在组装参数的时候。要指定哪些参数是输入参数。哪些是输出参数,默认是输入参数(Input)。

如果是输入(或者输入输出)参数。则必须要赋值。在存储过程中没有给默认值的情况下

在下图可以看出。有输入参数有两个 Input 和 InputOutput

.Net 有个ParameterDirection 枚举类

 

sql 中有out 输出参数,output 输入输出参数

CREATE PROC proc_show01
(
    @id int output, --输入输出参数,输出id
    @ck int out,
    @name nvarchar(20) = null --默认值为空 ,默认是输入参数
)

但在.Net都是output。因为output就是输入输入参数的总称

 param[3].Direction = ParameterDirection.Output

 

测试代码

 static void Main(string[] args)
        {
            //SqlParameter p = new SqlParameter("@name", "张三");

            //拼装参数
            SqlParameter[] param = { 
                                        new SqlParameter("@id",SqlDbType.Int),
                                        new SqlParameter("@name",SqlDbType.NVarChar)
                                   };

            //设置参数是输出参数
            param[0].Direction = ParameterDirection.Output;
            param[1].Value = "王五";
            int id;
            DataTable dt = SQLHelper.GetPro1("proc_show01", out id, param); //没有参数
        }

运行看结果

 

 

 

上面用 param[0].Direction = ParameterDirection.Output; 指定第一个参数是输出参数

然后通过 v1 = Convert.ToInt32(param[0].Value); 获取存储过程返回的值

 

从ParameterDirection枚举可以看出输入输出参数都测试过了。还有一个操作的返回值没有测试

 

那么来编写一个有操作类返回值的存储过程

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @id int output, --输出参数,输出id
    @name nvarchar(20) = null --默认值为空
)
AS
/*
定义一个变量,返回一个值 
也可以不定义变量,直接用return 返回
*/
declare @returnValue int 
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    set @id=0
    select * from Show where name = @name
    set @returnValue=10
    --return 0 --这里同样可以
end
else
    begin
        set @id=(select ID from Show where name = @name)
        select * from Show where name = @name
        set @returnValue=11
    end
return @returnValue --返回值

 

用 ParameterDirection.ReturnValue 指定是返回值

 

GetPro1则要加一个out 参数

 public static DataTable GetPro1(string cmdText, out int v1,out int v2, params SqlParameter[] param)

则: v2 = Convert.ToInt32(param[1].Value);

 

测试看效果

 

存储过程里面也可以执行一个新增操作。然后返回刚新增的ID,比如:

begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    --set @id=0
    select * from Show where name = @name
    set @returnValue=10
    --return 0 --这里同样可以
    insert Show(Name,Remark)VALUES('新增','地球');
    set @id = @@IDENTITY --返回新增的ID
end

 

上面都是通过.net代码访问存储过程,那么通过Transact-SQL语句怎么执行呢?

Transact-SQL 语句用exec(简写)关键字 ,全称是execute关键字

因为存储过程的参数顺序是

 

所以可以这样直接传参数,但顺序必须根据存储过程定义参数顺序一样

 

 

 

上面只是获取结果集。那么怎获取输出参数(outPut)和返回值呢(ReturnValue)

因为返回值是方法的返回值。所以可以变量名=存储过程名称 是不是跟.Net中很相似?

declare @m int --定义变量接收值
declare @result int 
exec @result=[proc_show01] @name='赵六',@id=@m output --一定要注名是output
select @m as '输出参数',@result as '返回值'

 

执行结果

 

其实就是参数名= 值 @name='赵六',@id=@m  的方式。因为

一旦使用了 '@name = value' 形式之后,所有后续的参数就必须以 '@name = value' 的形式传递。

 

用这种方式。顺序可以不用跟存储过程中定义参数的顺序相同

 

如果不用@name = value'的方式同样可以,

但这样直接传参数,但顺序必须根据存储过程定义参数顺序一样,返回值放在最前面

 

DECLARE @name varchar
--set @name='张三'
DECLARE @id int
DECLARE @result int
EXEC @result = proc_show01 @id output,'张三' --这里传参数的顺序一定要对应
--EXEC @result = proc_show01 @id output,@name 
select @id as '输出参数',@result as '返回值'

 

 

结果一样,其实不一样。因为传的参数不一样,哈哈哈

 

 

 

存储过程与SQL语句对比 (知识来源于网络)

优势:

1、提高性能
  SQL语句在创建过程时进行分析和编译。 存储过程是预编译的,在首次运行一个存储过程时,查询优化器对其进行分析、

  优化,并给出最终被存在系统表中的存储计划,这样,在执行过程时便可节省此开销。


2、降低网络开销
  存储过程调用时只需用提供存储过程名和必要的参数信息,从而可降低网络的流量。


3、便于进行代码移植
  数据库专业人员可以随时对存储过程进行修改,但对应用程序源代码却毫无影响,从而极大的提高了程序的可移植性。


4、更强的安全性
  1)系统管理员可以对执行的某一个存储过程进行权限限制,避免非授权用户对数据的访问
  2)在通过网络调用过程时,只有对执行过程的调用是可见的。 因此,恶意用户无法看到表和数据库对象名称、嵌入自己的 Transact-SQL 语句或搜索关键数据。
  3)使用过程参数有助于避免 SQL 注入攻击。 因为参数输入被视作文字值而非可执行代码,所以,攻击者将命令插入过程内的 Transact-SQL 语句并损害安全性将更为困难。
  4)可以对过程进行加密,这有助于对源代码进行模糊处理。 

 

劣势:

1、存储过程需要专门的数据库开发人员进行维护,但实际情况是,往往由程序开发员人员兼职

2、设计逻辑变更,修改存储过程没有SQL灵活

为什么在实际应用中,存储过程用到相对较少呢?

在通常的项目研发中,用存储过程却相对较少,这是为什么呢?
分析原因如下:
  1)没有特定的数据库开发人员,普通程序员兼职进行数据库操作
  2)程序员往往只需操作程序,即可完成数据访问,无需再在数据库上进行开发
  3)项目需求变动比较频繁,修改SQL语句比较方便,特别是涉及逻辑变更 

存储过程与SQL语句如何抉择?

基于实际应用的经验,给予如下建议:

  1、在一些高效率或者规范性要求比较高的项目,建议采用存储过程
  2、对于一般项目建议采用参数化命令方式,是存储过程与SQL语句一种折中的方式
  3、对于一些算法要求比较高,涉及多条数据逻辑,建议采用存储过程

 

 

//新增字段前判断字段是否存在
 
IF NOT EXISTS (select name from syscolumns where id=object_id(N'tb'AND NAME='name')
 BEGIN
 ALTER TABLE tb
 ADD name VARCHAR(100) NULL
 END
GO

--新增字段 ALTER TABLE dbo.info ADD test bit NULL --添加描述 EXECUTE sp_addextendedproperty N'MS_Description', '这里是描述', N'user', N'dbo', N'table', N'info', N'column', N'test' --更新说明 EXEC sp_updateextendedproperty 'MS_Description','字段1','user',dbo,'table','info','column',test --删除属性 EXEC sp_dropextendedproperty 'MS_Description','user',dbo,'table','info','column',test


1、添加字段

alter table 表名 add 字段名 type not null default 0

2、修改字段名

alter table 表名 rename column A to B

3、修改字段类型

alter table 表名 modify column UnitPrice decimal(18, 4) not null 

4、修改字段默认值

alter table 表名 drop constraint 约束名字   ------说明:删除表的字段的原有约束

alter table 表名 add constraint 约束名字 DEFAULT 默认值 for 字段名称 -------说明:添加一个表的字段的约束并指定默认值

5、删除字段

alter table 表名 drop column 字段名;

 

收集网络删的一个查询表的信息

 1 SET QUOTED_IDENTIFIER ON
 2 GO
 3 
 4 
 5 CREATE PROCEDURE [dbo].[sp_SelectTable]
 6     @tableName nvarchar(500)
 7 AS
 8 BEGIN
 9     SET NOCOUNT ON;
10         SELECT 
11         表名       = case when a.colorder=1 then d.name else '' end,
12         表说明     = case when a.colorder=1 then isnull(f.value,'') else '' end,
13         字段序号   = a.colorder,
14         字段名     = a.name,
15         标识       = case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then ''else '' end,
16         主键       = case when exists(SELECT 1 FROM sysobjects where xtype='PK' and parent_obj=a.id and name in (
17                          SELECT name FROM sysindexes WHERE indid in( SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid))) then '' else '' end,
18         类型       = b.name,
19         占用字节数 = a.length,
20         长度       = COLUMNPROPERTY(a.id,a.name,'PRECISION'),
21         小数位数   = isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0),
22         允许空     = case when a.isnullable=1 then ''else '' end,
23         默认值     = isnull(e.text,''),
24         字段说明   = isnull(g.[value],'')
25     FROM 
26         syscolumns a
27     left join 
28         systypes b 
29     on 
30         a.xusertype=b.xusertype
31     inner join 
32         sysobjects d 
33     on 
34         a.id=d.id  and d.xtype='U' and  d.name<>'dtproperties'
35     left join 
36         syscomments e 
37     on 
38         a.cdefault=e.id
39     left join 
40     sys.extended_properties   g 
41     on 
42         a.id=G.major_id and a.colid=g.minor_id  
43     left join
44     sys.extended_properties f
45     on 
46         d.id=f.major_id and f.minor_id=0
47     where 
48         d.name in(@tableName)    --如果只查询指定表,加上此where条件,tablename是要查询的表名;去除where条件查询所有的表信息
49     order by 
50         a.id,a.colorder
51 END
52 GO
View Code

调用:exec sp_SelectTable @tableName='表名'

 

 源码

参考资料

   http://www.cnblogs.com/cwyblog/archive/2013/04/21/3034281.html

   http://www.cnblogs.com/yank/p/4235609.html

 http://www.cnblogs.com/SkySoot/archive/2012/02/10/2345185.html

http://www.cnblogs.com/sosoft/p/3535696.html