2009-08-03 23 views
6

Bir imleç döngü olmadan bir SQL Server 2005 tablosundan kayıtları seçici olarak silmeye çalışıyorum. Tablo çok fazla kayıt (bazen> 500.000) içerebilir, bu nedenle döngü çok yavaştır.İmleç olmadan SQL Server tablosundan kayıtları silme

veri:

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 

Anahtar: Kimlik, UnitID, Gün, Aralık.

Bu örnekte Kayıtlar 2, 5 ve 8'i silmek istiyorum - mevcut bir kaydın bitişiğindedir (tuşa bağlı olarak).

Not: 6 kaydı silinmez, çünkü artık yok olduğunda, artık bitişik değildir.

Çok mu soruyorum?

+1

Ben olumlu değilim, ama bir dizi ben set teorisinden çağırmak ne "komşuluklarını" anlamıyor. Bunun bir imleç ile yapılması gerekebilir. –

+1

Hangi satırların silineceğine nasıl karar verirsiniz? Hangi alan (lar) a dayanan kriterler nelerdir? –

+0

Anahtar temel alınarak "sıralı düzen" gibi görünüyor. –

cevap

1

Ne istediğini sanmıyorum, ama yakınlaşabilirsin.

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 
) 

ama sorun o id = 6 yanı bulacağınız anlamına gelir: Size neredeyse bir ile kayıtları bularak bunu yapabilirsiniz görünen böyle kendini katılmak. Ancak, bu verilerden geçici bir tablo oluşturursanız, orijinal verilerinizden çok daha küçük olabilir ve böylece bir imleçle taramak çok daha hızlıdır (id = 6 sorununu gidermek için). Satırları öldürmek için DELETE FROM table WHERE id IN (SELECT id FROM tmp_table) yapabilirsiniz.

İmlecin ID = 6 sorununu çözmenin bir yolu olabilir, ancak eğer öyleyse, onu göremiyorum.

+1

Eğer orijinali koyduysanız Bir tablo değişkeni seçin, daha sonra id = 6 problemini düzeltmek için bir imleç yerine WHILE kullanabilirsiniz –

0

İmlecin alternatifi olan WHILE statement var. table variables ile bir araya geldiğinizde, aynı şeyleri, tamamlandığınız bir performans içinde gerçekleştirebilirsiniz.

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

Bu sadece ilk bitişik değeri silecektir. Eğer '21', '22',' 23' ve ' 24 ', bu' 22 'silinecek ama 24 bırakacaktır – Quassnoi

+0

Haklısınız.Sen var olun (seçin ...) silin ... –

4

performans ayrıntı için bloguma bu makalelere bakın:


aşağıda sorgu için ana fikri hepimizin silmek gerektiğidir aralıkların aralıklarından bile satırlar.(unitId, Day) verilen biz intervals aşağıdaki varsa, bir

:

1 
2 
3 
4 
6 
7 
8 
9 

, iki sürekli aralıkları vardır:

1 
2 
3 
4 

ve

6 
7 
8 
9 

, ve gerektiği her çift sırayı sil:

1 
2 -- delete 
3 
4 -- delete 

ve

6 
7 -- delete 
8 
9 -- delete 

, böylece elde ederiz: "Hatta satırlar" interval ait "bile başına aralığında ROW_NUMBER() s" Burada değil, "Hatta değerleri anlamına gelir

1 
3 
6 
8 

Not ". İşte

sorgu var:

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 

Güncelleme: İşte

daha verimli sorgu var:

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

Eğer bitişik aralık aralığınız 2, 3, 4, 5 ise - Olasılıkları silmeniz ve yok etmene gerek yok – Matt

+0

@Matt: Hatta satırları hatta değerleri silmeniz gerekir. val '3', örneğinizde bile bir' ROW_NUMBER() '' '2 değerine sahip olacaktır. – Quassnoi

+0

Şimdi anladım. Görünüşe göre/ROW_NUMBER bunun için bir yoldur. – Matt

0

Lieven çok yakın - bu deney seti için çalıştı, ama eğer Bazı kayıtları kaçırmaya başladığım birkaç kayıt daha ekliyorum.

Tek/çift kriterleri kullanamıyoruz - verilerin nasıl düştüğüne dair bir fikrimiz yok.

bu verileri ekleyin ve yeniden deneyin:

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: yeniden denenir, 9'lar, 11'leri, 13'leri, 15'leri ve 16'ları siler, 10'unu, 12'yi siler. ve '14'. İstediğin şey bu değil mi? – Quassnoi