0

我在工作中有以下任务,想知道是否有人可以帮助我。我需要使用游标来根据下一条记录更新记录。每当密钥不等于 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  ║
╚══════════════════════════════════════╝
4

2 回答 2

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 行(通过缺席确定它们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 ║
╚══════════════════════════════════════════╝

然后更新date2maxdate2.

现在,即使每个 ID 允许多个 key 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   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 行上增加,而在其他行上保持不变。partitionsCTE 获取每个 ID 分区的独立运行计数。结果,您将获得如前所述的 rangeID 值。

maxdatesCTE 读取结果并将partitionsrangeID 值用作我所说的附加标准。第二个查询的其余部分遵循第一个查询的逻辑。

可以在 SQL Fiddle找到此方法的现场演示。

可能有用的相关手册页:

于 2014-07-06T22:54:32.947 回答
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
于 2014-07-06T19:25:54.090 回答