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