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