2009-08-03 21 views
6

Estoy tratando de eliminar selectivamente registros de una tabla de SQL Server 2005 sin pasar por un cursor. La tabla puede contener muchos registros (a veces> 500,000) por lo que el bucle es demasiado lento.Eliminación de registros de la tabla de SQL Server sin cursor

datos:

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 

clave es: Identificación, UnitID, Día, Intervalo.

En este ejemplo, deseo eliminar los registros 2, 5 y 8, que son adyacentes a un registro existente (basado en la clave).

Nota: el registro 6 no se eliminará porque una vez que 5 se va, ya no está adyacente.

¿Estoy pidiendo demasiado?

+1

No soy positivo, pero un conjunto no entiende "adyacencia" de lo que recuerdo de la teoría de conjuntos. Esto puede necesitar hacerse con un cursor. –

+1

¿Cómo se decide qué filas eliminar? ¿Cuál es el criterio, basado en qué campo (s)? –

+0

Parece ser un "orden secuencial" basado en la clave. –

Respuesta

1

No creo que lo que está pidiendo sea posible, pero es posible que pueda acercarse. Parece que puede casi hacerlo mediante la búsqueda de registros con un auto-unirse de esta manera:

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 
) 

pero el problema es, que va a encontrar id = 6 también. Sin embargo, si crea una tabla temporal a partir de estos datos, puede ser mucho más pequeña que sus datos originales y, por lo tanto, mucho más rápida para escanear con un cursor (para corregir el problema id = 6). A continuación, puede hacer un DELETE FROM table WHERE id IN (SELECT id FROM tmp_table) para matar las filas.

Puede haber una manera de solucionar el problema ID = 6 sin un cursor, pero si es así, no lo veo.

+1

Si ya ha puesto el original seleccione en una variable de tabla, entonces podría usar un MES en lugar de un cursor para corregir el problema de id = 6. –

0

Existe el WHILE statement, que es una alternativa al cursor. Eso combinado con table variables podría permitirle hacer lo mismo dentro de un límite de rendimiento con el que está de acuerdo.

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

Esto eliminará solo el primer valor adyacente. Si tenemos '21',' 22', '23' y' 24', esto eliminará '22' pero saldrá' 24'. – Quassnoi

+0

Tiene razón. Haga que mientras exista (seleccione ...) elimine de ... –

4

ver a estos artículos en mi blog para los detalles de rendimiento:


La idea principal para la consulta a continuación es que debemos eliminar todos incluso filas de rangos continuos de intervalos.

Es decir, si por dada (unitId, Day) tenemos el siguiente intervals:

1 
2 
3 
4 
6 
7 
8 
9 

, tenemos dos rangos continuos:

1 
2 
3 
4 

y

6 
7 
8 
9 

, y debemos eliminar todas las filas pares:

1 
2 -- delete 
3 
4 -- delete 

y

6 
7 -- delete 
8 
9 -- delete 

, de manera que se obtiene:

1 
3 
6 
8 

Tenga en cuenta que "incluso filas" significa "incluso por alcance ROW_NUMBER() s" aquí, no "valores pares de interval ".

Aquí está la consulta:

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 

Actualización:

Aquí es una consulta más eficiente:

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

Si su rango contiguo de intervalos es 2, 3, 4, 5 - Necesitas eliminar las probabilidades y no iguala – Matt

+0

@Matt: Necesitas borrar incluso las filas, ni siquiera los valores. val '3' tendrá un' ROW_NUMBER() 'par de' 2' en tu ejemplo. – Quassnoi

+0

Lo entiendo ahora. Parece que WITH/ROW_NUMBER es el camino a seguir para este – Matt

0

Lieven está tan cerca - que trabajó para el equipo de prueba, pero si Agrego unos cuantos registros más y empieza a extrañar algo.

No podemos utilizar ningún criterio de par/impar, no tenemos idea de cómo caen los datos.

agregar estos datos y vuelva a intentar:

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: reintentado, deja las filas '9',' 11', '13',' 15' y '16', elimina' 10', '12' y '14'. ¿No es lo que quieres? – Quassnoi

Cuestiones relacionadas