使用游标更新表记录

时间:2014-07-06 14:44:06

标签: sql sql-server sql-server-2012 cursors

我在工作中有以下任务,并想知道是否有人可以帮助我。 我需要使用游标,以便根据下一条记录更新记录。每当密钥不等于8时,我需要检查密钥以8开头的所有记录,并更新date2及其最大日期

我的数据如下:

╔══════════════════════════════════════╗
║     ID  date1      date2         key ║
╠══════════════════════════════════════╣
║     1   1/2/2014    5/2/2014      1  ║
║     1   5/2/2014    8/2/2014      8  ║
║     1   8/2/2014    9/2/2014      8  ║
║     1   11/2/2014   12/2/2014     1  ║   
║     1   12/2/2014   14/2/2014     8  ║ 
║     2   12/2/2014   14/2/2014     1  ║
║     2   14/2/2014   17/2/2014     8  ║
║     3   20/2/2014   23/2/2014     1  ║
╚══════════════════════════════════════╝

更新后,数据将如下所示:

╔══════════════════════════════════════╗
║     ID  date1      date2         key ║
╠══════════════════════════════════════╣
║     1   1/2/2014    9/2/2014      1  ║
║     1   5/2/2014    8/2/2014      8  ║
║     1   8/2/2014    9/2/2014      8  ║
║     1   11/2/2014   14/2/2014     1  ║   
║     1   12/2/2014   14/2/2014     8  ║ 
║     2   12/2/2014   17/2/2014     1  ║
║     2   14/2/2014   17/2/2014     8  ║
║     3   20/2/2014   23/2/2014     1  ║
╚══════════════════════════════════════╝

2 个答案:

答案 0 :(得分:2)

如果每个ID有一个密钥1行,并且具有相同ID值的所有现有密钥8行与它相关,则可以尝试以下方法:

WITH maxdates AS (
  SELECT
    *,
    maxdate2 = MAX(CASE [key] WHEN 8 THEN date2 END) OVER (PARTITION BY ID)
  FROM dbo.atable
)
UPDATE maxdates
SET date2 = maxdate2
WHERE [key] = 1
  AND maxdate2 IS NOT NULL
;

这是它的工作原理。 maxdates公用表表达式使用窗口MAX函数来确定每个组的最大date2值(在这种情况下为每个ID)。如果这是原始数据集:

╔═══════════════════════════════╗
║ ID  date1      date2      key ║
╠═══════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1   ║
║ 1   5/2/2014   8/2/2014   8   ║
║ 1   8/2/2014   9/2/2014   8   ║
║ 2   12/2/2014  14/2/2014  1   ║
║ 2   14/2/2014  17/2/2014  8   ║
║ 3   20/2/2014  23/2/2014  1   ║
╚═══════════════════════════════╝
CTE将把它变成以下内容:

╔══════════════════════════════════════════╗
║ ID  date1      date2      key  maxdate2  ║
╠══════════════════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1    9/2/2014  ║
║ 1   5/2/2014   8/2/2014   8    9/2/2014  ║
║ 1   8/2/2014   9/2/2014   8    9/2/2014  ║
║ 2   12/2/2014  14/2/2014  1    17/2/2014 ║
║ 2   14/2/2014  17/2/2014  8    17/2/2014 ║
║ 3   20/2/2014  23/2/2014  1    NULL      ║
╚══════════════════════════════════════════╝

UPDATE语句将首先筛选出不需要更新的行,即key为8的行以及没有关联键8行的键1行(由缺少的{{ 1}}),产生这个子集:

maxdate2

然后使用╔══════════════════════════════════════════╗ ║ ID date1 date2 key maxdate2 ║ ╠══════════════════════════════════════════╣ ║ 1 1/2/2014 5/2/2014 1 9/2/2014 ║ ║ 2 12/2/2014 14/2/2014 1 17/2/2014 ║ ╚══════════════════════════════════════════╝ 更新date2

现在,即使每个ID允许多个密钥1行,此方法仍然适用。您只需要提出另一个标准来识别同一ID组中相关行的子组。也就是说,您首先需要像这样转换数据集:

maxdate2

这样的事情:

╔═══════════════════════════════╗
║ ID  date1      date2      key ║
╠═══════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1   ║
║ 1   5/2/2014   8/2/2014   8   ║
║ 1   8/2/2014   9/2/2014   8   ║
║ 1   12/2/2014  14/2/2014  1   ║
║ 1   14/2/2014  17/2/2014  8   ║
║ 1   20/2/2014  23/2/2014  1   ║
╚═══════════════════════════════╝

然后应用该方法。

添加此类标准的一种方法是使用条件运行计数,如此查询:

╔═════════════════════════════════════════╗
║ ID  date1      date2      key  rangeID  ║
╠═════════════════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1    1        ║
║ 1   5/2/2014   8/2/2014   8    1        ║
║ 1   8/2/2014   9/2/2014   8    1        ║
║ 1   12/2/2014  14/2/2014  1    2        ║
║ 1   14/2/2014  17/2/2014  8    2        ║
║ 1   20/2/2014  23/2/2014  1    3        ║
╚═════════════════════════════════════════╝

基本上,WITH partitions AS ( SELECT *, rangeID = COUNT(CASE [key] WHEN 1 THEN 1 END) OVER (PARTITION BY ID ORDER BY date1) FROM dbo.atable ), maxdates AS ( SELECT *, maxdate2 = MAX(CASE [key] WHEN 8 THEN date2 END) OVER (PARTITION BY ID, rangeID) FROM partitions ) UPDATE maxdates SET date2 = maxdate2 WHERE [key] = 1 AND maxdate2 IS NOT NULL ; 是一个运行计数,CASE表达式使其成为条件:计数仅在键1行上增加,在其他行上保持不变。 COUNT() OVER (... ORDER BY ...) CTE为每个ID分区获取独立运行计数。因此,您可以获得rangeID值,如前所示。

partitions CTE读取maxdates的结果,并使用rangeID值作为我正在讨论的附加标准。第二个查询的其余部分遵循第一个查询的逻辑。

可以找到此方法的实时演示at SQL Fiddle

可能有用的相关手册页:

答案 1 :(得分:0)

您不需要CURSOR

您是否尝试执行以下操作?

(注意 - 我已经将日期安排为dd / mm / yyyy,这是在其他星球上完成的方式)

DECLARE @MyData TABLE (ID INT, date1 DATE, date2 DATE, [key] INT)
INSERT INTO @MyData (ID, date1, date2, [key])
    SELECT 1, '2/1/2014',    '2/5/2014',  1 UNION ALL
    SELECT 1, '2/5/2014',    '2/8/2014',  8 UNION ALL
    SELECT 1, '2/8/2014',    '2/9/2014',  8 UNION ALL
    SELECT 2, '2/12/2014',   '2/14/2014', 1 UNION ALL
    SELECT 2, '2/14/2014',   '2/17/2014', 8 UNION ALL
    SELECT 3, '2/20/2014',   '2/23/2014', 1

UPDATE MD
SET MD.date2 = DT.MaxDate2
FROM @MyData    MD
JOIN 
(
    SELECT   ID
            ,MaxDate2   = MAX(Date2)
    FROM @MyData
    WHERE [key] = 8
    GROUP BY ID
)           DT  ON DT.ID = MD.ID
WHERE MD.[key] != 8

SELECT *
FROM @MyData