2023年,第39周。给自己一个目标,然后坚持总会有收货,不信你试试!
今天有个小伙伴咨询一个Sql Server处理数据的问题,刚好重温下SqlServer临时表和游标的知识点
目录
- 一、需求点描述
- 二、临时表
- 2.1、局部临时表(Local Temporary Table)
- 2.2、全局临时表(Global Temporary Table)
- 三、游标
- 3.1、声明游标
- 3.2、打开游标
- 3.3、获取数据
- 3.4、处理数据
- 3.5、关闭和释放游标
- 四、解决方案
一、需求点描述
有如下数据集,有9条记录,如果001前后一条记录都不是001,那么就取001前面一条记录以及本身001这条记录、
如果001下一条记录还是001,则取001最后一条记录以及001刚开始的前一条记录
工作中心 | 序号 | 备注 |
---|---|---|
1001 | 1 | |
1002 | 2 | 取这条记录 |
001 | 3 | 取这条记录 |
1004 | 4 | |
1008 | 5 | 取这条记录 |
001 | 6 | 连续出现的首条前一条记录 |
001 | 7 | |
001 | 8 | 取这条记录 |
1009 | 9 |
1)查询数据集
2)目标数据集
二、临时表
在 SQL Server 中,临时表是一种用于存储临时数据的特殊表。
临时表可以在查询执行期间被创建,并且只在当前会话或连接有效。
它们对于需要存储临时数据的计算和操作非常有用。
SQL Server 提供了两种类型的临时表:局部临时表(Local Temporary Table)和全局临时表(Global Temporary Table)。
2.1、局部临时表(Local Temporary Table)
局部临时表是以 #
开头的表名,只在创建它的会话中可见。
当创建它的会话结束时,该表会自动删除。其他会话无法访问这个表。
- 示例创建局部临时表:
CREATE TABLE #TempTable (ID INT,Name VARCHAR(50)
);
2.2、全局临时表(Global Temporary Table)
全局临时表是以 ##
开头的表名,可以在创建它的服务器实例上的任何会话中可见。
当所有引用该表的会话结束时,该表会自动删除。
- 示例创建全局临时表:
CREATE TABLE ##TempTable (ID INT,Name VARCHAR(50)
);
使用临时表时,可以像操作任何其他表一样进行数据插入、更新、删除和查询。
值得注意的是,临时表的结构(包括列定义和约束)与永久表类似,可以创建索引、触发器等对象。
然而,当会话结束后,临时表和与之相关的对象都会被自动清理和删除。
临时表对于临时性数据存储和处理非常有用,例如在复杂的查询和存储过程中暂存中间结果或存储需要跨多个查询或操作之间共享的临时数据。
三、游标
在 SQL Server 中,游标(Cursor)是一种用于遍历结果集的数据库对象。
它提供了一种逐行处理查询结果的机制,可以在需要逐行操作数据的情况下使用。
- 以下是使用游标的一般步骤:
3.1、声明游标
使用 DECLARE CURSOR
语句声明游标,并指定游标的名称和要遍历的查询。
DECLARE CursorName CURSOR FORSELECT Column1, Column2FROM TableName;
3.2、打开游标
使用 OPEN
语句打开游标,准备开始遍历结果集。
OPEN CursorName;
3.3、获取数据
使用 FETCH NEXT
语句获取当前游标位置的一行数据,并将其存储到变量中。可以使用 INTO
子句将数据存储到多个变量中。
FETCH NEXT FROM CursorName INTO @Variable1, @Variable2;
3.4、处理数据
在循环中对获取的行数据进行处理。这可以是对数据进行计算、更新、删除等操作,或者仅仅是输出数据。
WHILE @@FETCH_STATUS = 0
BEGIN-- 处理数据-- 例如执行一些操作或输出数据FETCH NEXT FROM CursorName INTO @Variable1, @Variable2;
END;
3.5、关闭和释放游标
使用 CLOSE
关闭游标,将游标的状态置为不可使用,但不删除游标。最后使用 DEALLOCATE
释放游标,并从内存中删除。
CLOSE CursorName;
DEALLOCATE CursorName;
注意事项:
- 使用游标时要考虑性能和资源占用,因为游标可能导致性能下降,并占用大量内存。
- 在处理完成后,务必关闭和释放游标,以释放资源。
- 可以使用
@@FETCH_STATUS
系统变量来判断是否还有更多行可供遍历。
游标的使用需要谨慎考虑,只在必要的情况下使用,尽量使用集合操作来替代游标,以提高性能。
四、解决方案
根据上面了解到的临时表和游标,结合需求,可以做如下逻辑操作,得到目标查询结果
- 代码如下
-- 创建局部临时表
if object_id('tempdb..#myTempTable') is not null begindrop table #myTempTable
end
/*else begincreate table #myTempTable(工作中心 varchar(50),序号 int)
end*/-- 临时表不存在情况下
select * into #myTempTable from(select '1001' 工作中心,1 序号 union allselect '1002' 工作中心,2 序号 union allselect '001' 工作中心,3 序号 union allselect '1004' 工作中心,4 序号 union allselect '1008' 工作中心,5 序号 union allselect '001' 工作中心,6 序号 union allselect '001' 工作中心,7 序号 union allselect '001' 工作中心,8 序号 union allselect '1009' 工作中心,9 序号
) a-- select * from #myTempTable-- 定义变狼
declare @工作中心 varchar(50)
declare @序号 int
declare @前一个工作中心 varchar(50)
declare @前一个序号 int
set @前一个工作中心='#'-- 定义游标
declare cursorName cursor for
select 工作中心,序号 from #myTempTable-- 打开游标
open cursorName-- 遍历游标
fetch next from cursorName into @工作中心,@序号;
while @@fetch_status=0 begin-- print(@工作中心)if @前一个工作中心='#' beginset @前一个工作中心=@工作中心set @前一个序号=@序号endelse beginif @工作中心='001' beginif @前一个工作中心!='001' begin-- 输出001上的一条记录print(@前一个工作中心+','+convert(varchar(50),@前一个序号))endendelse beginif @前一个工作中心!=@工作中心 and @前一个工作中心='001' begin-- 输出001最后一条print(@前一个工作中心+','+convert(varchar(50),@前一个序号))endendset @前一个工作中心=@工作中心set @前一个序号=@序号end-- 遍历下一条,一定要加上这句,否则会一直循环fetch next from cursorName into @工作中心,@序号;
end-- 关闭和销毁游标
close cursorName
deallocate cursorName
- 效果如下