2009-08-03 4 views
6

Я пытаюсь выборочно удалять записи из таблицы SQL Server 2005 без прокрутки курсора. Таблица может содержать много записей (иногда> 500 000), поэтому цикл слишком медленный.Удаление записей из таблицы SQL Server без курсора

данных:

ID, UnitID, Day, Interval, Amount 

1 100  10 21  9.345 

2 100  10 22  9.367 

3 200  11 21  4.150 

4 300  11 21  4.350 

5 300  11 22  4.734 

6 300  11 23  5.106 

7 400  13 21  10.257 

8 400  13 22  10.428 

Ключ: ID, UnitID, день, интервал.

В этом примере я хочу удалить записи 2, 5 и 8 - они смежны с существующей записью (на основе ключа).

Примечание: запись 6 не будет удалена, потому что как только 5 уйдет, она больше не смежна.

Я прошу слишком много?

+1

Я не уверен, но набор не понимает «соседство» от того, что я помню из теории множеств. Возможно, это нужно сделать с помощью курсора. –

+1

Как вы решаете, какие строки удалить? Каковы критерии, исходя из того, какое поле (-ы)? –

+0

Это, по-видимому, «последовательный порядок» на основе ключа. –

ответ

1

Я не думаю, что то, о чем вы просите, возможно, но вы можете приблизиться. Оказывается, вы можете почти сделать это, находя записи с автообъединением так:

SELECT t1.id 
FROM 
    table t1 JOIN table t2 ON (
    t1.unitid = t2.unitid AND 
    t1.day = t2.day AND 
    t1.interval = t2.interval - 1 
) 

но проблема в том, что будет найти идентификатор = 6, а также. Однако, если вы создадите временную таблицу из этих данных, это может быть намного меньше, чем ваши исходные данные, и тем более быстрее сканировать курсором (чтобы исправить проблему id = 6). Затем вы можете сделать DELETE FROM table WHERE id IN (SELECT id FROM tmp_table), чтобы убить строки.

Возможно, существует проблема с проблемой ID = 6 без курсора, но если это так, я не вижу этого.

+1

Если вы уже положили оригинал выберите в переменную таблицы, затем вместо курсора вы можете использовать WHILE, чтобы исправить проблему id = 6. –

0

Существует WHILE statement, который является альтернативой курсору. Это в сочетании с table variables может позволить вам сделать то же самое в рамках производительности, с которым вы в порядке.

0
DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.367) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 4.150) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 4.350) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 4.734) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 5.106) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 10.257) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 10.428) 

DELETE FROM @Table 
WHERE ID IN (
    SELECT t1.ID 
    FROM @Table t1 
     INNER JOIN @Table t2 
      ON t2.UnitID = t1.UnitID 
       AND t2.Day = t1.Day 
       AND t2.Interval = t1.Interval - 1 
     LEFT OUTER JOIN @Table t3 
      ON t3.UnitID = t2.UnitID 
       AND t3.Day = t2.Day 
       AND t3.Interval = t2.Interval - 1 
    WHERE t3.ID IS NULL) 

SELECT * FROM @Table 
+0

Это приведет к удалению только первого смежного значения. Если у нас есть '21',' 22', '23' и' 24', это удалит '22', но оставьте' 24'. – Quassnoi

+0

Вы правы. Сделайте это пока существует (выберите ...) удалить из ... –

4

Смотрите эти статьи в моем блоге для деталей производительности:


Основная идея ниже запроса является то, что мы должны удалить все даже строк из непрерывных интервалов интервалов.

То есть, если для данного (unitId, Day) мы имеем следующее intervals:

1 
2 
3 
4 
6 
7 
8 
9 

, мы имеем две непрерывные диапазоны:

1 
2 
3 
4 

и

6 
7 
8 
9 

, и мы должны удалять каждый четный ряд:

1 
2 -- delete 
3 
4 -- delete 

и

6 
7 -- delete 
8 
9 -- delete 

, так что мы получаем:

1 
3 
6 
8 

Обратите внимание, что "даже строки" означает "ROW_NUMBER() сек даже за пределы" здесь, а не «даже значения interval ».

Вот запрос:

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345) 
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345) 
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345) 
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345) 
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345) 
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345) 
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345) 

;WITH rows AS 
     (
     SELECT *, 
       ROW_NUMBER() OVER 
       (
       PARTITION BY 
         (
         SELECT TOP 1 qi.id AS mint 
         FROM @Table qi 
         WHERE qi.unitid = qo.unitid 
           AND qi.[day] = qo.[day] 
           AND qi.interval <= qo.interval 
           AND NOT EXISTS 
           (
           SELECT NULL 
           FROM @Table t 
           WHERE t.unitid = qi.unitid 
             AND t.[day] = qi.day 
             AND t.interval = qi.interval - 1 
           ) 
         ORDER BY 
           qi.interval DESC 
         ) 
       ORDER BY interval 
       ) AS rnm 
     FROM @Table qo 
     ) 
DELETE 
FROM rows 
WHERE rnm % 2 = 0 

SELECT * 
FROM @table 

Update:

Вот более эффективный запрос:

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT) 

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345) 
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345) 
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345) 
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345) 
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345) 
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345) 
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345) 
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345) 
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345) 
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345) 
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345) 
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345) 
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345) 
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345) 

;WITH source AS 
     (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY unitid, day ORDER BY interval) rn 
     FROM @Table 
     ), 
     rows AS 
     (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY unitid, day, interval - rn ORDER BY interval) AS rnm 
     FROM source 
     ) 
DELETE 
FROM rows 
WHERE rnm % 2 = 0 

SELECT * 
FROM @table 
+0

Если ваш смежный диапазон интервалов равен 2, 3, 4, 5 - вам нужно удалить коэффициенты, а не выравнивать. – Matt

+0

@Matt: нужно удалить даже строки, а не значения. val '3' будет иметь в вашем примере четный' ROW_NUMBER() '' ''. – Quassnoi

+0

Я получаю это сейчас. Похоже, что WITH/ROW_NUMBER - это путь для этого – Matt

0

Ливен так близко - он работал в тестовом наборе, но если Я добавляю еще несколько записей, которые он начинает пропустить.

Мы не можем использовать критерии четности/четности - мы понятия не имеем, как данные падают.

Добавить эти данные и повторите попытку:

INSERT @Table VALUES (9, 100,  10, 23,  9.345) 

INSERT @Table VALUES (10, 100,  10, 24,  9.367) 

INSERT @Table VALUES (11, 100,  10, 25,  4.150) 

INSERT @Table VALUES (12, 100,  10, 26,  4.350) 

INSERT @Table VALUES (13, 300,  11, 25,  4.734) 

INSERT @Table VALUES (14, 300,  11, 26,  5.106) 

INSERT @Table VALUES (15, 300,  11, 27,  10.257) 

INSERT @Table VALUES (16, 300,  11, 29,  10.428) 
+0

@Ian: retried, он оставляет строки '9',' 11', '13',' 15' и '16', удаляет' 10', '12' и '14'. Разве вы этого не хотите? – Quassnoi

Смежные вопросы