Sql 使用游标更新表记录

Sql 使用游标更新表记录,sql,sql-server,sql-server-2012,cursors,Sql,Sql Server,Sql Server 2012,Cursors,我在工作中有以下任务,不知道是否有人能帮我。 我需要使用游标,以便根据下一条记录更新记录。每当key不等于8时,我需要检查它之后的所有记录,其中key以8开头,并用其最大日期更新date2 我的数据如下所示: ╔══════════════════════════════════════╗ ║ ID date1 date2 key ║ ╠══════════════════════════════════════╣ ║ 1 1/2/2014

我在工作中有以下任务,不知道是否有人能帮我。 我需要使用游标,以便根据下一条记录更新记录。每当key不等于8时,我需要检查它之后的所有记录,其中key以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   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        ║
╚═════════════════════════════════════════╝
更新后,数据将如下所示:

╔══════════════════════════════════════╗
║     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  ║
╚══════════════════════════════════════╝

您不需要
光标

您是否正在尝试执行以下操作

(注意——我将日期安排为dd/mm/yyyy,这是在其他行星上的做法)


如果每个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
公共表表达式使用window MAX函数来确定每个组(本例中为每个ID)的最大
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   ║
║ 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的行以及没有关联的key 8行的key 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 ║
╚══════════════════════════════════════════╝
然后用
maxdate2
更新
date2

现在,即使允许每个ID有多个键1行,此方法仍然适用。您只需要提出另一个标准来识别同一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  ║
║     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   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        ║
╚═════════════════════════════════════════╝
变成这样:

╔══════════════════════════════════════╗
║     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   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
;
基本上,
COUNT()OVER(…ORDER BY…
是一个正在运行的计数,CASE表达式使其具有条件:计数仅在键1行上增加,在其他行上保持不变。
分区
CTE获得每个ID分区的独立运行计数。因此,您将获得如前所示的rangeID值

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

可以找到这种方法的现场演示

可能有帮助的相关手册页面:


您是如何订购这些记录的?如果我说这张唱片是在这张唱片之后,我是根据什么说的?@kermit,当然我不是要求你做我的工作。我只是想征求意见,因为我以前从未使用过SQL游标,因为不推荐使用它们。无论如何,游标用法的想法来自我的老板。数据保存在SQL数据库中table@AsfakulIslam,我是根据ID和date1来订购记录的。那时几乎肯定会有比游标更好的方法。但我不太明白你想要的结果。为什么
5/2/2014
设置为
9/2/2014
而不是
14/2/2014
?@MartinSmith:
检查它之后的所有记录,其中键以8开头,并用其最大日期更新date2
谢谢您的回复。我真的希望我能在没有光标的情况下解决这个问题,因为数据量很大,光标会破坏性能。不过,这只是一个小查询,以防数据有另一个类似的记录:选择1,'2/11/2014','2/12/2014',1。。选择1,'12/11/2014','2/15/2014',8。。以上是不是很感谢你的更新。但是,在以下情况下,上述操作失败:║ 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 ║SQLFiddle演示包含这个案例,我认为我已经正确地介绍了它。请详细说明错误的位,并解释预期结果是什么?