13Microsoft SQL Server SQL 高级事务,锁,游标,分区

Microsoft SQL Server SQL高级事务,锁,游标,分区


通过采用事务和锁机制,解决了数据库系统的并发性问题。

9.1数据库事务

(1)BEGIN TRANSACTION语句定义事务的起始点

(2)COMMIT TRANSACTION提交事务

(3)ROLLBACK TRANSACTION回滚事务

--开始一个事务

begin tran start_transaction

update yuan set Eno='001' where Ename='脏累'

--保存能回滚的点

save tran savepoint_transaction

update bumen set Eno='001' where Eno='002'

--回滚本次事务到保存点

commit tran start_transaction

 

USE ElecTravelCom

GO

CREATE TABLE employees(

emp_ID INT,

emp_Name VARCHAR(20))

GO

USE ElecTravelCom

BEGIN TRANSACTION

INSERT INTO dbo.employees(emp_ID, emp_Name) VALUES(1, 'Henry')

SAVE TRANSACTION a

INSERT INTO dbo.employees(emp_ID, emp_Name) VALUES(2, 'Josef')

SAVE TRANSACTION b

INSERT INTO dbo.employees(emp_ID, emp_Name) VALUES(3, 'Michael')

ROLLBACK TRANSACTION b

INSERT INTO dbo.employees(emp_ID, emp_Name) VALUES(4, 'Rudolf')

ROLLBACK TRANSACTION a

COMMIT TRANSACTION

SELECT *

FROM employees

GO

9.2数据库锁

可以使用快捷键“Ctrl+2”来查看锁信息,也可以通过系统存储过程sp_lock来查看数据库的锁。

  1. 系统自动加锁

begin transaction

update student

set sname='ooo'

where sno='2008056101'

waitfor delay '00:00:30'

commit transaction

 

begin transaction

select * from student

where sno='2008056101'

commit transaction

 

若同时执行上述两个语句,则select查询必须等待update执行完毕才能执行即要等待30秒

  1. 人为加锁

begin transaction

select * from student with (updlock)

where sno='2008056101'

waitfor delay '00:00:30'

commit transaction

 

begin transaction

select * from student where sno='2008056101'

update student

set sname='vvv'

where sno='2008056101'

commit transaction

若同时执行上述两个语句,则第二个连接中的select查询可以执行 而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒

9.3数据库游标

游标是一种处理数据的方法,具有对结果集进行逐行处理的能力

9.3.1声明游标(DECLARE)

DECLARE 游标名称CURSOR

[LOCAL|GLOBAL]                                                   --游标的作用域

[FORWORD_ONLY|SCROLL] --游标的移动方向

[STATIC|KEYSET|DYNAMIC|FAST_FORWARD]    --游标的类型

[READ_ONLY|SCROLL_LOCKS|OPTIMISTIC]      --游标的访问类型

[TYPE_WARNING] --类型转换警告信息

FOR SELECT查询语句  --SELECT查询语句

[FOR {READ ONLY|UPDATE[OF 列名称]}][,…n]                --可修改的列

 

创建游标cur1,使cur1可以对student表所有的数据行进行操作,并将游标变量@var_cur1与cur1相关联

对应的T-SQL语句为:

DECLARE cur1 CURSOR

FOR SELECT * FROM student

DECLARE @var_cur1 CURSOR

SET @var_cur1=cur1

9.3.2打开游标(OPEN)

声明以后,如果要从游标中读取数据必须要打开游标。打开游标是指打开已经声明但尚未打开的游标,并执行游标中定义的查询。

语法格式为:OPEN 游标名称

如果游标声明语句中使用了STATIC 关键字,则打开游标时产生一个临时表来存放结果集;如果声明游标时作用了KEYSET 选项,则OPEN 产生一个临时表来存放键值。所有的临时表都存在tempdb 数据库中。

在游标被成功打开后,全局变量@@CURSOR_ROWS用来记录游标内的数据行数,@@CURSOR_ROWS的返回值有四个:

-m  表示仍在向游标读入数据,m表示已经读入的行数

-1    动态游标值无法确定

0     无符合调剂的游标或游标已经被关闭

n    从基础表向游标读入数据已介绍,n为游标中已有的数据记录的行数

USE transdatabase

GO

DECLARE CUR2 CURSOR

FOR SELECT top 10 * FROM [dbo].[T_OrderTrans]

GO

OPEN CUR2

SELECT '游标数据行数'=@@CURSOR_ROWS

执行结果为-1,说明该游标是一个动态游标,其值未确定。

9.3.3读取数据(FETCH)

游标被成功打开以后,就可以使用FETCH 命令从游标中逐行地读取数据,以进行相关处理。其语法规则为:

FETCH

[[NEXT | PRIOR | FIRST | LAST | ABSOLUTE{n|@nvar}| RELATIVE {n|@nvar}]

       FROM]                                              --读取数据的位置

{{[GLOBAL] 游标名称} | @游标变量名称}

[INTO  @游标变量名称] [,…n]         

              --将读取的游标数据存放到指定变量中

FETCH语句执行时,可以使用全局变量@@FETCH_STATUS返回上次执行FETCH 命令的状态。在每次用FETCH从游标中读取数据时,都应检查该变量,以确定上次FETCH 操作是否成功,来决定如何进行下一步处理。@@FETCH_STATUS 变量有三个不同的返回值,如表

                       

USE transdatabase

GO

DECLARE CUR CURSOR

FOR SELECT top 100 * FROM [dbo].[T_OrderTrans]

GO

OPEN CUR

SELECT '游标数据行数'=@@CURSOR_ROWS

FETCH NEXT FROM CUR

SELECT 'NEXT_FETCH执行情况'=@@FETCH_STATUS

@@FETCH_STATUS函数值为0。说明执行成功

9.3.4关闭游标(CLOSE)

游标使用完成后要及时关闭。关闭游标使用CLOSE 语句,但不释放游标占用的数据结构。其语法规则为:
 CLOSE { { [GLOBAL] 游标名称} | @游标变量名称}

CLOSE CUR

9.3.5释放游标(DEALLOCATE)

游标关闭后,其定义仍在,需要时可用OPEN语句打开继续使用。若确认游标不再使用,可以删除游标,释放其所占用的系统空间。删除游标用DEALLOCATE语句,定义格式为:

DEALLOCATE { { [GLOBAL] 游标名称} | @游标变量名称}

DEALLOCATE CUR

 

--定义一个游标coursor1,逐行读取SC表中的数据

use 学生课程数据库

go

select * from sc     

--显示sc中的所有数据

go

declare coursor1 cursor

 --声明一个游标

for select * from sc

open cursor1             --打开游标

fetch next from cursor1 

--读取该游标中的第一行数据

/*检查@@fetch_status的值,以确保新位置的有效性*/

while @@fetch_status = 0

 begin

   fetch next from cousor1

 end

close cursor1

deallocate cursor1

--定义一个游标stud_curosr2,删除表SC表的第一行数据。

use student

go

select * from sc   

 --显示修改前表stud_info中的所有数据

go

declare stud_cursor2 cursor 

 --声明一个游标stud_cursor2

  for select * from sc

open stud_cursor2             --打开游标

fetch firster from stud_cursor2  --读取游标中的第一行数据

delete from sc        --删除游标中的第一行数据

  where current of stud_cursor2

close stud_cursor2            --关闭游标

deallocate stud_cursor2

go

select * from sc

--定义一个游标stud_cursor3,更新表student中的数据,将gkfs小于等于600的改为600分。

use 学生课程数据库

go

select * from student     

 --显示修改前表student的所有数据

go

declare stud_cursor3 cursor    --声明一个游标stud_cursor

   for select * from student

open stud_cursor3              --打开游标

fetch next from stud_cursor3   --读取游标中的第一行数据

update student             

  set gkfs = 600

  where current of student

close stud_cursor3             --关闭游标

deallocate stud_cursor3

go

select * from student     

--显示修改后表student中的所有数据

use test

go

declare course_cur1 cursor

    for select * from course

    for read only

go

open course_cur1

fetch next from course_cur1

while @@FETCH_STATUS=0

    begin

       fetch next from course_cur1

    end

close course_cur1

deallocate course_cur1

 

use test

go

declare @id char(4),@name varchar(50)

declare course_cur cursor

    for select cno,cname from course

open course_cur

fetch next from course_cur

into @id,@name

while @@FETCH_STATUS=0

    begin

       print '    '+@id+'  '+@name

       fetch next from course_cur

       into @id,@name

    end

close course_cur

deallocate course_cur

9.3.6游标的运用

Fetch firster:提取游标中的第一行

Fetch next:提取上次提取行之后的行

Fetch prior:提取上次提取行之前的行

Fetch absolute n:如果n为正整数,则提取游标中从第1行开始的第n行。如果n为负整数,则提取游标中的倒数第n行。如果n为0,则没有行被提取。

Fetch relative n:如果n为正整数,提取上次提取行之后的第n行。如果n为负整数,提取上次提取行之前的第n行。如果n为0,则同一行被再次提取。

T-SQL:游标限于一次只能提取一行。API服务器游标则支持每次提取时提取一批行。支持一次提取多行的游标称为块状游标。

--示例:使用fetch返回指定行数据

use test

go

declare course_cur cursor scroll

    for select * from course

open course_cur

fetch first from course_cur

fetch last from course_cur

fetch absolute 4 from course_cur

fetch relative -2 from course_cur

close course_cur

deallocate course_cur

 

declare stu_cur cursor scroll

for select sno,sname,sdept,scomegrade from student

open stu_cur

declare @xh char(10),@xm nvarchar(4),

      @xi nvarchar(30),@rxcj smallint

fetch absolute 8 from stu_cur into @xh,@xm,@xi,@rxcj

print @xh+@xm+@xi+convert(char,@rxcj)

fetch relative -2 from stu_cur into @xh,@xm,@xi,@rxcj

print @xh+@xm+@xi+convert(char,@rxcj)

fetch next from stu_cur into @xh,@xm,@xi,@rxcj

print @xh+@xm+@xi+convert(char,@rxcj)

fetch last from stu_cur into @xh,@xm,@xi,@rxcj

print @xh+@xm+@xi+convert(char,@rxcj)

fetch prior from stu_cur into @xh,@xm,@xi,@rxcj

print @xh+@xm+@xi+convert(char,@rxcj)

close stu_cur

deallocate stu_cur

 

use test

declare student_cur cursor scroll

    for select sname,scomegrade from student

open student_cur

declare @xm nvarchar(8),@rxcj int

fetch first from student_cur into @xm,@rxcj

print '  '+@xm+'  '+convert(char,@rxcj)

fetch last from student_cur into @xm,@rxcj

print '  '+@xm+'  '+convert(char,@rxcj)

fetch absolute 8 from student_cur into @xm,@rxcj

print '  '+@xm+'  '+convert(char,@rxcj)

fetch relative -2 from student_cur into @xm,@rxcj

print '  '+@xm+'  '+convert(char,@rxcj)

close student_cur

deallocate student_cur

declare stu_cur cursor

   for select sname,scomegrade,snation from student

open stu_cur

declare @xm nvarchar(4),@rxcj int,@mz bit

fetch next from stu_cur into @xm,@rxcj,@mz

while @@FETCH_STATUS =0

    begin

           print @xm+convert(char,@rxcj)

           fetch next from stu_cur into @xm,@rxcj,@mz    

    end

close stu_cur

deallocate stu_cur

 

declare s_cur cursor

for select sname,scomegrade,snation from student

open s_cur

declare @xm nvarchar(4),@gkcj smallint,@mz bit

fetch next from s_cur into @xm,@gkcj,@mz

while @@FETCH_STATUS =0

  begin

    print  @xm+' '+str(@gkcj,3)+' '+cast(@mz as char(1))

    fetch next from s_cur into @xm,@gkcj,@mz

  end

close s_cur

deallocate s_cur

--用游标修改数据

use test

go

declare stu_cur cursor

    for select sname,scomegrade,snation from student

open stu_cur

declare @xm nvarchar(4),@rxcj int,@mz bit

fetch next from stu_cur into @xm,@rxcj,@mz

while @@FETCH_STATUS =0

    begin

       if (@mz='true')

           update student

                                   set scomegrade=scomegrade+10

           where CURRENT of stu_cur

       fetch next from stu_cur into @xm,@rxcj,@mz

    end

close stu_cur

deallocate  stu_cur

 

--示例:将课程表中学分不大于3的加1分。

use test

go

declare course_cur cursor

for select ccredit from course

open course_cur

declare @xf tinyint

fetch next from course_cur into @xf

while @@FETCH_STATUS=0

    begin

       if (@xf<=3)

           update course

           set ccredit=ccredit+1

           where CURRENT of course_cur

       fetch next from course_cur into @xf

    end

close course_cur

deallocate course_cur

 

--用游标删除数据

--示例:将课程表中学分不大于3的记录删除。

use test

go

declare course_cur cursor

for select credit from course

open course_cur

declare @xf tinyint

fetch next from course_cur into @xf

while @@FETCH_STATUS=0

    begin

       if (@xf<=3)

           delete course

           where CURRENT of course_cur

       fetch next from course_cur into @xf

    end

close course_cur

deallocate course_cur

 

use test

declare stu_cur cursor

    for select sname,scomegrade,snation from student

open stu_cur

declare @xm nvarchar(4),@rxcj int,@mz bit

fetch next from stu_cur into @xm,@rxcj,@mz

while @@FETCH_STATUS =0

    begin

       if (@mz='true')

           delete from student

           where CURRENT of stu_cur

        fetch next from stu_cur into xm,@rxcj,@mz

    end

close stu_cur

deallocate stu_cur

--游标嵌套

declare sdept_cur1 cursor

for select sdept from sdept_table

open sdept_cur1

declare @xi varchar(50)

fetch next from sdept_cur1 into @xi

while @@FETCH_STATUS=0

    begin

       print @xi+'系学生名单:'

       declare student_cur2 cursor

       for select sname from student  where sdept=@xi

       open student_cur2

       declare @xm varchar(8)

       fetch next from student_cur2 into @xm

       while @@FETCH_STATUS=0

           begin

              print @xm

              fetch next from student_cur2 into @xm

           end

       close student_cur2

       deallocate student_cur2

       print ''

       fetch next from sdept_cur1 into @xi

    end

close sdept_cur1

deallocate sdept_cur1

 

--使用游标变量

游标也是一种数据类型。因此,可以用游标声明变量。声明变量的方式同声明其他变量的方式一样。游标变量的用法与游标一样。

--示例:逐条查看course表中的每条记录。

declare @游标变量 cursor

set @游标变量=cursor

for select * from course

open @游标变量

fetch next from @游标变量

while @@FETCH_STATUS =0

       fetch next from @游标变量

close @游标变量

deallocate @游标变量

--使用游标变量打开student表,并显示所有学生姓名及入学成绩

use test

go

declare @游标变量 cursor

set @游标变量=cursor

for select sname,scomegrade from student

open @游标变量

declare @xm varchar(8),@rxcj smallint

fetch next from @游标变量 into @xm,@rxcj

while @@FETCH_STATUS =0

    begin

       print @xm+'    '+convert(char(3),@rxcj)

       fetch next from @游标变量

       into @xm,@rxcj

    end

close @游标变量

deallocate @游标变量

 

declare @stu_cur cursor

set @stu_cur=cursor

for select sname,scomegrade,snation from student

open @stu_cur

declare @xm nvarchar(4),@rxcj int,@mz bit

fetch next from @stu_cur into @xm,@rxcj,@mz

while @@FETCH_STATUS =0

   begin

     if (@mz='true')

        delete student

        where current of @stu_cur

        fetch next from @stu_cur into @xm,@rxcj,@mz

   end

close @stu_cur

deallocate @stu_cur

 

--使用order by子句改变游标中行的顺序

--使用游标变量打开student表,并显示所有学生姓名及入学成绩,按成绩降序显示

use test

go

declare @游标变量 cursor

set @游标变量=cursor

for select sname,scomegrade from student

order by scomegrade desc

open @游标变量

declare @xm varchar(8),@rxcj smallint

fetch next from @游标变量 into @xm,@rxcj

while @@FETCH_STATUS =0

    begin

       if LEN(@xm)>=3

           print @xm+'  '+convert(char(3),@rxcj)

       else

           print @xm+'    '+convert(char(3),@rxcj)

       fetch next from @游标变量 into @xm,@rxcj

    end

close @游标变量

deallocate @游标变量

 

 

use test

go

declare stu_cur cursor

    for select sname,scomegrade,snation from student

    order by scomegrade desc

open stu_cur

declare @xm nvarchar(4),@rxcj int,@mz bit

fetch next from stu_cur into @xm,@rxcj,@mz

while @@FETCH_STATUS =0

    begin

       print @xm+' '+convert(char,@rxcj)+

                               '  '+ltrim(rtrim(convert(char(5),@mz)))

       fetch next from stu_cur into @xm,@rxcj,@mz

    end

close stu_cur

deallocate stu_cur

9.4创建分区

9.4.1创建分区函数

分区函数指定用于分区数据的键的数据类型、分区数量、分区依据列以及每个分区的边界值。

使用CREATE PARTITION FUNCTION 语句创建分区函数。该命令的基本语法如下:CREATE PARTITION FUNCTION partition_function_name (input_parameter_type)

AS RANGE [LEFT/RIGHT]

FOR VALUES ([boundary_value[,…n]])[;]

9.4.2创建分区方案

创建分区函数以后,必须将其与指定的分区方案相关联。分区方案将在分区函数中定义的分区映射到将物理存储这些分区的文件组。可将所有的分区映射到同一个文件组,也可将部分或全部分区映射到不同的文件组,根据具体需要定。

使用CREATE PARTITION SCHEME 语句创建分区方案。该命令的基本语法如下:

CREATE PARTITION SCHEME partition_scheme_name

AS PARTITION partition_function_name

TO ({file_group_name/[PRIMARY]}[,…n]])[;]

9.4.3创建分区表

若一个表中包含了大量的、以多种不同方式使用的数据,且通常情况下的查询不能按照预期的情况完成,那么这时就可以考虑使用分区表。分区表是将数据水平划分为多个单元的表,这些单元可以分布到数据库中的多个不同的文件组中。

CREATE  TABLE  表名称

(

列名 数据类型,

列名 数据类型,

列名 数据类型,

)

ON  分区方案名称(该表分区的列)

9.4.4管理分区

分区过程不是静态的,可对分区表执行三个主要操作:切换分区、合并分区和拆分分区。可用到这SPLIT、MERGE和SWITCH三个运算符管理分区

切换分区:

      使用ALTER TABLE 语句的SWITCH 子句可将已填充的表或分区与空的表或分区进行交换。

合并分区:

       可使用ALTER PARTITION FUNCTION 语句合并分区。执行合并操作时,在这个语句中,指定了边界值的分区将被删除,并且该数据合并到相邻的分区中。

拆分分区

也使用ALTER PARTITION FUNCTION 语句来拆分分区。将创建新分区,并相应的重新分配数据。新的分区创建在基于分区函数的每一个分区方案中指定为下一个文件组的文件组中。

use test

go

create partition function partfunsale(datetime)

as range right

for values('01/01/2010', '01/01/2011', '01/01/2012', '01/01/2013')

 

create partition scheme partschsale

as partition partfunsale

to (sale2009,sale2010,sale2011,sale2012,sale2013)

Go

 

create table sale

(id int identity(1,1) not null,

 name varchar(16) not null,

 saletime datetime not null

 )

 on partschsale(saletime)

 

insert into sale(name,saletime)

  values('张三','2009-1-1'),

  ('李四','2009-2-1'),

('王五','2009-3-1'),

('钱六','2010-4-1'),

('赵七','2010-5-1'),

('张三','2011-6-1'),

('李四','2011-7-1'),

('王五','2011-8-1'),

('钱六','2012-9-1'),

('赵七','2012-10-1'),

('张三','2012-11-1'),

('李四','2012-12-1'),

('王五','2014-12-1' )

 

insert into sale(name,saletime)  values('张三','2009-1-1')

insert into sale(name,saletime) values('李四','2009-2-1')

insert into sale(name,saletime) values('王五','2009-3-1')

insert into sale(name,saletime) values('钱六','2010-4-1')

insert into sale(name,saletime) values('赵七','2010-5-1')

insert into sale(name,saletime) values('张三','2011-6-1')

insert into sale(name,saletime) values('李四','2011-7-1')

insert into sale(name,saletime) values('王五','2011-8-1')

insert into sale(name,saletime) values('钱六','2012-9-1')

insert into sale(name,saletime) values('赵七','2012-10-1')

insert into sale(name,saletime) values('张三','2012-11-1')

insert into sale(name,saletime) values('李四','2012-12-1')

insert into sale(name,saletime) values('王五','2014-12-1')

 

select $partition.partfunsale(saletime)

as 分区编号,COUNT(*) as 记录

from sale

group by $partition.partfunsale(saletime)

 

select * from sale where $partition.partfunsale(saletime)=1

 

select * from sale where $partition.partfunsale(saletime)=2

 

select * from sale where $partition.partfunsale(saletime)=3

 

select * from sale where $partition.partfunsale(saletime)=4

 

select * from sale where $partition.partfunsale(saletime)=5

 

update sale

set saletime='2019-1-1'

where id=1

 

select * from sale where $partition.partfunsale(saletime)=1

select * from sale where $partition.partfunsale(saletime)=2

select * from sale where $partition.partfunsale(saletime)=3

select * from sale where $partition.partfunsale(saletime)=4

select * from sale where $partition.partfunsale(saletime)=5

 

select $partition.partfunsale(saletime)

as 分区编号,COUNT(*) as 记录

from sale

group by $partition.partfunsale(saletime)

 

1.合并分区

update sale

set saletime='2010-1-1'

where id=1

alter partition function partfunsale()

merge range ('2010-1-1')

--等价于

create partition function partfunsale(datetime)

as range right

for value('01/01/2011','01/01/2012','01/01/2012')

 

select $partition.partfunsale(saletime) as 分区编号,COUNT(*) as 记录

from sale

group by $partition.partfunsale(saletime)

合并分区就是删除分区,分区所处的文件及文件组是不会删除的,只是对数据进行转移合并。

删除的这个边界值(boundary_value)属于哪个分区,那么就会删除这个分区,再向邻近的分区合并。邻近的意思是以这个边界值为临界点的两个分区

查看分区方案的方式:在SQL Server Management Studio中,选择数据库-->存储-->分区方案,右击分区方案名,在弹出的菜单中选择“编写分区方案脚本为”-->CREATE到-->新查询编辑器窗口

 

2.拆分分区

       alter partition scheme partschsale

       next used sale2010

在为分区方案指定一个可用的文件组时,该分区方案并没有立刻使用这个文件组。

 

alter partition function partfunsale()

 split range('2010-1-1')

--等修改了分区函数后分区方案才使用这个文件组

 

拆分分区两个步骤:

为分区方案指定一个可以使用的文件组。

修改分区函数。

use test

go

alter partition scheme partschsale

next used sale2012_2

alter partition function partfunsale()

split range('2012-6-1')

 

select $partition.partfunsale(saletime) as 分区编号,COUNT(*) as 记录

from sale

group by $partition.partfunsale(saletime)

select * from sale where $partition.partfunsale(saletime)=1

select * from sale where $partition.partfunsale(saletime)=2

select * from sale where $partition.partfunsale(saletime)=3

select * from sale where $partition.partfunsale(saletime)=4

select * from sale where $partition.partfunsale(saletime)=5

select * from sale where $partition.partfunsale(saletime)=6

转载于:https://www.cnblogs.com/Aha-Best/p/10870159.html

你可能感兴趣的:(13Microsoft SQL Server SQL 高级事务,锁,游标,分区)