现在的位置: 首页 > 综合 > 正文

如何通过存储过程实现记录集的循环

2013年09月10日 ⁄ 综合 ⁄ 共 2970字 ⁄ 字号 评论关闭

在应用程序开发的时候,我们经常可能会遇到下面的应用,我们会通过查询数据表的记录集,循环每一条记录,通过每一条的记录集对另一张表进行数据进行操作,如插入与更新,我们现在假设有一个这样的业务:老师为所在班级的学生选课,选的课程如有哲学、马克思主义政治经济学、毛泽东思想概论、邓小平理论这些课,现在操作主要如下:
  1) 先要查询这些还没有毕业的这些学生的名单,毕业过后的无法进行选课;
  2) 在批量的选取学生的同时,还需要添加对应的某一门课程;
  3) 点添加后选课结束。
  我们如果有SQL语句
  我们先建立三张表:
  createtableStudent--学生表 ( StudentIdintnotnull,--学生ID StudentNameVARCHAR2(30),--学生姓名 StuentSNVARCHAR2(30),--学生学号 StudentGraduint--学生是否毕业0代表毕业,1代表未毕业 ) createtableSubject--课程表 ( SubjectIdintnotnull,--课程ID SubjectNameVARCHAR2(20)--课程名称 ) createtableSelSubject--选好课程表 ( SubjectIdintnotnull,--课程ID StudentIDintnotnull--学生ID )
  在实现上面的业务功能的时候,我们可能很多人一开始就在程序里面直接实现了,代码如下:
  --省略数据库连接
  DataTable dt = GetDs().Tables[0]; int subjectid = Convert.ToInt32(this.DrpList.SelectedValue); if(dt.Rows.Count0) { Add(subjectid,Convert.ToInt32(dt.Rows[i][0].ToString())); }
  public DataSet GetDs() { SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString); SqlCommand cmd = new SqlCommand("select studentid from Student where StudentGradu = 1", con); SqlDataAdapter dat = new SqlDataAdapter(cmd); DataSet ds = new DataSet(); dat.Fill(ds); return ds; }
  public void Add(int subjectid,int studentinfoid) { StringBuilder strSql = new StringBuilder(); strSql.Append("insert into SelSubject("); strSql.Append("SubjectId,StudentId)"); strSql.Append(" values ("); strSql.Append("@SubjectId,@StudentId)"); SqlParameter[] parameters = {new SqlParameter("@SubjectId", SqlDbType.Int,4),new SqlParameter("@StudentId", SqlDbType.Int,4)}; parameters[0].Value = subjectid; parameters.Value = studentinfoid; DbHelperSQL.ExecuteSql(strSql.ToString(), parameters); }
  我们从上述的代码中可以看到,在我们取出来表的记录集的时候,然后通过满足条件的记录集在程序中循环的去传条件进行数据的插入的操作。
  如果数据量少可能看不出用上面这种办法实现的弱点,因为它每次在操作数据库的时候,都存在着频繁的和数据库的I/O直接交互,这点性能的牺牲实属不应该,那我们就看下面的方法,通过存储过程的游标方法来实现:
  建立存储过程:
  Create PROCEDURE P_InsertSubject@SubjectId intAS DECLARE rs CURSOR LOCAL SCROLL FOR select studentid from student where StudentGradu = 1
  OPEN rs FETCH NEXT FROM rs INTO @tempStudentID WHILE @@FETCH_STATUS = 0BEGINInsert SelSubject values (@SubjectId,@tempStudentID) FETCH NEXT FROM rs INTO @tempStudentIDENDCLOSE rs GO
  使用游标对记录集循环进行处理的时候一般操作如以下几个步骤:
  1、把记录集传给游标;
  2、打开游标
  3、开始循环
  4、从游标中取值
  5、检查那一行被返回
  6、处理
  7、关闭循环
  8、关闭游标
  上面这种方法在性能上面无疑已经是提高很多了,但我们也想到,在存储过程编写的时候,有时候我们尽量少的避免使用游标来进行操作,所以我们还可以对上面的存储过程进行改造,使用下面的方法来实现:
  Create PROCEDURE P_InsertSubject@SubjectId intAS declare @i int,@studentid
  DECLARE @tCanStudent TABLE(studentidint,FlagIDTINYINT)
  BEGINinsert @tCanStudent select studentid,0 from student where StudentGradu = 1SET @i=1WHILE( @i=1)BEGINSELECT @studentid=‘‘
  SELECT TOP 1 @studentid = studentid FROM @tCanStudent WHERE flagID=0SET @i=@@ROWCOUNTIF @i<=0 GOTO Return_LabInsert SelSubject values (@SubjectId,@studentid) IF @@error=0 UPDATE @tCanStudent SET flagID=1 WHERE studentid = @studentidReturn_Lab:ENDEndGO
  我们现在再来分析以上这个存储过程,它实现的方法是先把满足条件的记录集数据存放到一个表变量中,并且在这个表变量中增加一个FLAGID进行数据初始值为0的存放,然后去循环这个记录集,每循环一次,就把对应的FLAGID的值改成1,然后再根据循环来查找满足条件等于0的情况,可以看到,每循环一次,处理的记录集就会少一次,然后循环的往选好课程表里面插入,直到记录集的条数为0时停止循环,此时完成操作。
  比较以上的几种循环方法的应用,就会知道,有时候可能对于同一种功能我们实现的方法不同,而最终应用程序性能的影响的差异就会很大,第二种、第三种就大大的减少的数据库交互I/O操作的频繁,会节省很多时间,方法三又避免用游标又可以节省不必要的开销。

抱歉!评论已关闭.