我需要一些SQL Server游标的帮助:
DECLARE @iTerminalID varchar(100)
DECLARE @iID int
DECLARE @iDateTB date
DECLARE @iNom bigint
DECLARE @iTBtoEOD bigint
DECLARE @iSal bigint
DECLARE @iOldNom bigint
DECLARE @iOldSal bigint
DECLARE @check int
DECLARE main_cursor CURSOR FOR
SELECT
TerminalId
FROM AA
WHERE Nom IS NULL
GROUP BY TerminalId
ORDER BY TerminalId
OPEN main_cursor
FETCH NEXT
FROM main_cursor INTO @iTerminalID
WHILE (@@fetch_status = 0)
BEGIN
SET @iOldSal = 0
DECLARE detail_cursor CURSOR FOR
SELECT
ROW_NUMBER() OVER (ORDER BY TerminalId, DateTB) NoID,
DateTB,
Nomi,
TBtoEOD,
Sal
FROM AA
WHERE TerminalId = @iTerminalID
OPEN detail_cursor
FETCH NEXT
FROM detail_cursor INTO @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
WHILE (@@fetch_status = 0)
BEGIN
IF @iNom IS NULL AND @iID = 1
BEGIN
UPDATE AA
SET Nom = 0
WHERE TerminalId = @iTerminalID
AND CONVERT(date, TanggalTB) = CONVERT(date, @iDateTB)
SET @iOldSal = abs(@iTBtoEOD)
END
ELSE IF @iNom IS NULL
AND @iID <> 1
--AND @iOldSal <> 0
BEGIN
UPDATE AA
SET Nom = @iOldSal
WHERE TerminalId = @iTerminalID
AND CONVERT(date, DateTB) = CONVERT(date, @iDateTB)
PRINT concat(@iTerminalID, '----', @iDateTB)
SET @iOldSal = @iNom - @iTBtoEOD
END
ELSE
BEGIN
SET @iOldSal = @iNom - @iTBtoEOD
END
FETCH detail_cursor INTO @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
END
CLOSE detail_cursor
DEALLOCATE detail_cursor
FETCH main_cursor INTO @iTerminalID
END
CLOSE main_cursor
DEALLOCATE main_cursor
我有2个游标 用于循环到详细数据的第一个游标 在我得到我所拥有的数据的详细信息之后,我将根据我编写的计算执行更新
直到光标到2
完成更新过程后才能解决此问题答案 0 :(得分:1)
正如marc_s所建议的那样,除非你别无选择,否则最好除掉SQL Server开发解决方案中的SQL cursor。
我准备了以下T-SQL Update命令,其中没有使用游标
UPDATE AA
set
Nomi = case when ( (NewAA.NoID = 1) and (AA.Nomi is null) )
then 0
when ( (NewAA.NoID > 1) and (AA.Nomi is null) )
then (select sum(t.Nomi) from AA t where t.DateTB < AA.DateTB and t.TerminalId = AA.TerminalId)
else AA.Nomi
end
FROM AA
Inner Join (
SELECT
PK_Field,
NoID = ROW_NUMBER() OVER (Partition By TerminalId ORDER BY DateTB),
DateTB,
Nomi,
TBtoEOD,
Sal
FROM AA
) NewAA
on AA.PK_Field = NewAA.PK_Field
请注意,我使用了名为PK_Field的主键字段。 我希望你的桌子上有主键AA
我不确定Update语句的详细信息,请先在测试环境中测试上面的sql代码
答案 1 :(得分:0)
审查每一步的差异
- 指定光标的类型/方向
- 不是group_by而是分明,不要迷惑自己
- in row_number在通过单个@iTerminalId
过滤时由TerminalId进行不必要的排序- 为抓取和其他陈述保存相同的语法
- 看起来你需要按升序排列row_numbered - 你必须明确指定order_by
DECLARE
@main_cursor cursor,
@detail_cursor cursor
SET @main_cursor = CURSOR FAST_FORWARD FOR
SELECT DISTINCT
TerminalId
FROM AA
WHERE Nom IS NULL
ORDER BY TerminalId
OPEN @main_cursor
FETCH NEXT FROM @main_cursor
INTO @iTerminalID
WHILE (@@fetch_status = 0)
BEGIN
SET @iOldSal = 0
SET @detail_cursor = CURSOR FAST_FORWARD FOR
SELECT
ROW_NUMBER() OVER (ORDER BY DateTB) as NoID,
DateTB, Nomi, TBtoEOD, Sal
FROM AA
WHERE TerminalId = @iTerminalID
ORDER BY NoID
OPEN @detail_cursor
FETCH NEXT FROM @detail_cursor
INTO @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
WHILE (@@fetch_status = 0)
BEGIN
... same inner part for now
FETCH NEXT FROM @detail_cursor
INTO @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
END
CLOSE @detail_cursor
DEALLOCATE @detail_cursor
FETCH NEXT FROM @main_cursor
INTO @iTerminalID
END
CLOSE @main_cursor
DEALLOCATE @main_cursor
- 外部循环除了归零@iOldSal
之外什么都不做- 另一件事 - row_number以1为每个TerminalID
开头- 所以正确顺序所需的所有数据都可以通过单一查询获得
SET @detail_cursor = CURSOR FAST_FORWARD FOR
SELECT
a.TerminalId,
ROW_NUMBER() OVER (PARTITION BY a.TerminalId ORDER BY a.DateTB) as NoID,
a.DateTB, a.Nomi, a.TBtoEOD, a.Sal
FROM AA a
ORDER BY a.TerminalId, NoID
OPEN @detail_cursor
FETCH NEXT FROM @detail_cursor
INTO @TerminalId, @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
WHILE (@@fetch_status = 0)
BEGIN
if @iID = 1
SET @iOldSal = 0
... same inner part for now
FETCH NEXT FROM @detail_cursor
INTO @TerminalId, @iID, @iDateTB, @iNom, @iTBtoEOD, @iSal
END
CLOSE @detail_cursor
DEALLOCATE @detail_cursor
以下更改我假设您在DateTB顺序中为每个终端重新编号行 并且您在第一次更新时有拼写错误(TanggalTB列而不是DateTB) 并且TBtoEOD是某种始终为负的偏移量 并且每次更新只影响一行。这些假设可能不正确 - 请在评论中随意注明。
- @iOldSal计算总是相同的,除了@ iNum = NULL,可以借助IsNull()来处理
- 当@ iID = 1时@iOldSal始终为0所以首先IF没有意义 - 它与第二个相同
- 当@iNom为NULL时
@iNom - @iTBtoEOD
将返回NULL - 您必须初始化它- 光标处理意味着您确实有一个光标位于当前处理的行上,因此您不必在更新中再次找到它 声明;但是你需要修改游标类型(FAST_FORWARD包含 READONLY)FORWARD_ONLY(可能是FOR_UPDATE - 只是可以&#39; t 记得;可能是sqlserver不允许它是&#34; FOR_UPDATE&#34; 因为窗口功能,我没有检查)
- 许多变量变得不必要,因为我们直接引用行值而不需要重复搜索当前处理的行
SET @detail_cursor = CURSOR FORWARD_ONLY, FOR_UPDATE FOR
SELECT
ROW_NUMBER() OVER (PARTITION BY a.TerminalId ORDER BY a.DateTB) as NoID,
a.Nom
FROM AA a
ORDER BY a.TerminalId, NoID
FETCH NEXT FROM @detail_cursor
INTO @iID, @iNom
WHILE (@@fetch_status = 0)
BEGIN
IF @iID = 1
SET @iOldSal = 0
UPDATE a SET
Nom = IsNull(a.Nom, @iOldSal),
@iOldSal = a.Nom - a.TBtoEOD
FROM AA a
WHERE CURRENT OF @detail_cursor
FETCH NEXT FROM @detail_cursor
INTO @iID, @iNom
END
CLOSE @detail_cursor
DEALLOCATE @detail_cursor
所以你只是根据先前编号的行设置一个不具备它的行? 不确定我是否理解一切正确,但在我看来,这个任务可以用这样的代码完成:
;WITH cteAA as
(
SELECT
a.TerminalID, a.TBtoEOD, a.Nom,
ROW_NUMBER() OVER (PARTITION BY a.TerminalId ORDER BY a.DateTB, a.Nom) as NoID
FROM AA a
),
cteAANums as
(
SELECT a.TerminalID, a.NoID, IsNull(a.Nom, 0) as Nom, a.TBtoEOD
FROM cteAA a
WHERE a.NoID = 1
UNION ALL
SELECT a.TerminalID, a.NoID, IsNull(a.Nom, n.Nom-n.TBtoEOD) as Nom, a.TBtoEOD
FROM cteAA a
INNER JOIN cteAANums n on n.TerminalID = a.TerminalID
WHERE a.NoID = n.NoID + 1
)
UPDATE a SET
Nom = n.Nom
FROM AA a
INNER JOIN cteAANums n on n.TerminalID = a.TerminalID and a.DateTB = n.DateTB
没有自连接就无法实现,对于其他情况,光标可能不是一个糟糕的选择。但不是唯一肯定的。