2014-12-18 2 views
3

я следующая таблицаSQL таблицы Сканирование для обновления значения

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

Если значение столбца значение, я тогда хочу обновить предыдущие записи одного и того же ID к I.

Например ID 1 имеет последний recod со значением I так хотите обновить первые 2 записи ID 1 до I. Но ID 1 с ключом 7 значения не должен изменяться

я могу сделать автообъединение и обновить предыдущие записи с помощью ключа значения меньше, чем текущее значение ключа и т.д .. Но таблица имеет большое количество записей, так что это занимает много времени, чтобы сканирование по таблице для каждого значения Id. Могу ли я использовать функцию задержки, но значение смещения будет целой таблицей. Так что не уверен, что это лучшее решение. Или есть другой вариант, кроме самостоятельного присоединения и задержки.


+0

это SQL Server или PostGreSQL? – Lamak

+0

@Lamak SQL Server – TD2013

+0

А что, если это был Key = 7, который был I, а не Key = 3? Вы все еще хотите обновить Key = 1 - 3, даже если ID = 2 строки между этими наборами ID = 1? –

ответ

0

Try это (или альтернативный под результатами):

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 

И если вы измените ключ от 3 до C и ключа 7 в I, он будет менять ключи 1 - 3, в результате чего 4 клавиши - одна 6.

ИЛИ, вы можете попробовать следующее вместо этого, который находит [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]; 
+1

Спасибо за решение, он работал @srutzky – TD2013

+0

@ TD2013 Привет приветствуется. С какой версией вы поехали? Испытывали ли вы производительность как против фактической таблицы? Мне любопытно, что лучше, так как вам нужно отсканировать все строки в какой-то момент (ну, в зависимости от индексов), чтобы узнать, какие из них нужно обновить, но я подозреваю, что вы делаете UPDATE только для поля Key (альтернативный запрос) было бы лучше на UPDATE, но не уверен, что это хуже при первоначальном поиске. –

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  ║ 
╚═════╩════╩═══════╝ 
+0

: @Llamak это должно работать, но если я добавлю еще одну строку после того, как значение будет значение, значение не должно меняться для записи, которая появляется только после предыдущих записей. Для примера add (7, 1, 'C') в таблице это значение должно оставаться 'C' – TD2013

+0

@ TD2013 Я обновил свой ответ сейчас – Lamak

+0

: @Lamak Я хочу, чтобы ключ 7 оставался таким же, но хочу, чтобы клавиши 1 и 2 менялись на I – TD2013

0

Вот один метод. Он использует CTE, чтобы найти идентификаторы, которые соответствуют условию последней строки, являющейся 'I'. Затем он использует этот список для обновления:

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); 

EDIT:

Вы можете определить группы смежных идентификаторов с помощью дифферента row_numbers() подхода. Так что это будет определять группы:

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); 
+0

: @Gordon Linoff Я обновил свой вопрос – TD2013

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 
Смежные вопросы