SQL扫描表更新值

时间:2014-12-18 17:00:11

标签: sql sql-server

我有下表

Key ID  Value  
1    1    C  
2    1    C  
3    1    I  
4    2    C  
5    2    C  
6    2    C  
7    1    C 

如果值列的值为I,则希望将同一ID的先前记录更新为I

例如,ID 1的最后一次重新记录的值为I,因此要将ID 1的前2条记录更新为I。 但是具有Key 7值的ID 1不应该改变

我可以使用Key值小于当前键值等自我加入和更新以前的记录。 但是表有大量记录,因此每个Id值扫描表需要很长时间。 我可以使用滞后函数,但偏移值将是整个表格。 所以不确定哪个是最好的解决方案。或者除了自我加入和延迟之外还有其他选择。


4 个答案:

答案 0 :(得分:0)

这应该有效:

WITH CTE AS
(
    SELECT  *, 
            MAX([Key]) OVER(PARTITION BY ID) MaxKey
    FROM dbo.YourTable 
)
UPDATE A
SET Value = 'I'
FROM CTE A
WHERE EXISTS(SELECT 1 FROM CTE
             WHERE [Key] = A.MaxKey
             AND Value = 'I');

Here is a sqlfiddle及其演示。

结果是:

╔═════╦════╦═══════╗
║ Key ║ ID ║ Value ║
╠═════╬════╬═══════╣
║   1 ║  1 ║ I     ║
║   2 ║  1 ║ I     ║
║   3 ║  1 ║ I     ║
║   4 ║  2 ║ C     ║
║   5 ║  2 ║ C     ║
║   6 ║  2 ║ C     ║
╚═════╩════╩═══════╝

答案 1 :(得分:0)

这是一种方法。它使用CTE查找满足最后一行'I'条件的ID。然后它使用此列表进行更新:

with ids as (
      select id
      from (select ft.*, row_number() over (partition by id order by [key] desc) as seqnum
            from followingtable ft
           ) t
      where seqnum = 1 and value = 'I'
     )
update followingtable
    set value = 'I'
    where id in (select id from ids);

编辑:

您可以使用row_numbers()方法的不同来识别相邻ID组。所以这将确定群体:

select t.*,
       (row_number() over (order by [key]) - row_number() over (partition by id order by [key]) as grp
from followingtable t;

然后,您可以在上述查询中使用此信息:

with ft as (
      select t.*,
             (row_number() over (order by [key]) - row_number() over (partition by id order by [key])) as grp
      from followingtable t;
     ),
     idgrp as (
      select id, grp
      from (select ft.*, row_number() over (partition by id, grp order by [key] desc) as seqnum
            from followingtable ft
           ) t
      where seqnum = 1 and value = 'I'
     )
update ft
    set value = 'I'
    where exists (select 1 from idgrp where idgrp.id = ft.id and idgrp.grp = ft.grp);

答案 2 :(得分:0)

使用Window Function查找组中的最新值

CREATE TABLE #test1([Key] INT,ID    INT,Value VARCHAR(2))

INSERT #test1
VALUES (1,1,'C' ),(2,1,'C' ),(3,1,'I' ),
       (4,2,'C' ),(5,2,'C' ),(6,2,'C' ) 

;WITH cte
     AS (SELECT Row_number() OVER(partition BY ID ORDER BY [key] DESC) rn,
                *
         FROM   #test1)
UPDATE A
SET    Value = b.value
FROM   #test1 A
       JOIN cte b
         ON a.ID = b.ID
            AND b.Value = 'I'
WHERE  rn = 1 

答案 3 :(得分:0)

试试这个(或结果下面的替代):

SET NOCOUNT ON;
DECLARE @Data TABLE
(
  [Key] INT NOT NULL,
  [ID] INT NOT NULL,
  [Value] VARCHAR(50) NOT NULL
);

INSERT INTO @Data VALUES (1, 1, 'C');
INSERT INTO @Data VALUES (2, 1, 'C');
INSERT INTO @Data VALUES (3, 1, 'I');
INSERT INTO @Data VALUES (4, 2, 'C');
INSERT INTO @Data VALUES (5, 2, 'C');
INSERT INTO @Data VALUES (6, 2, 'C');
INSERT INTO @Data VALUES (7, 1, 'C');

;WITH cte AS
(
  SELECT DISTINCT tmp.ID,
         MAX(tmp.[Key]) OVER(PARTITION BY tmp.[ID]) AS [MaxKeyOfI]
  FROM   @Data tmp
  WHERE  tmp.[Value] = 'I'
)
UPDATE tbl
SET    tbl.[Value] = 'I'
--SELECT tbl.*
FROM   @Data tbl
INNER JOIN cte
        ON cte.[ID] = tbl.[ID]
        AND cte.MaxKeyOfI > tbl.[Key]
WHERE  tbl.Value <> 'I';


SELECT *
FROM @Data;

返回:

Key ID  Value
1   1   I
2   1   I
3   1   I
4   2   C
5   2   C
6   2   C
7   1   C

如果您将Key 3更改为C并将Key 7更改为I,它将更改按键1 - 3,仅保留按键4 - 6。

OR ,您可以尝试以下方法,找到[Key]值并在UPDATE中使用它们,以便UPDATE本身仅基于PK字段:

;WITH maxkeys AS
(
  SELECT DISTINCT tmp.ID,
         MAX(tmp.[Key]) OVER(PARTITION BY tmp.[ID]) AS [MaxKeyOfI]
  FROM   @Data tmp
  WHERE  tmp.[Value] = 'I'
), others AS
(
  SELECT tmp2.[Key],
         tmp2.[ID]
  FROM   @Data tmp2
  INNER JOIN maxkeys
          ON maxkeys.[ID] = tmp2.[ID]
         AND maxkeys.MaxKeyOfI > tmp2.[Key]
  WHERE  tmp2.Value <> 'I'
)
UPDATE tbl
SET    tbl.[Value] = 'I'
--SELECT tbl.*
FROM   @Data tbl
INNER JOIN others tmp
        ON tmp.[Key] = tbl.[Key];