Tablodaki mükerrer kayıtları silme 

Veri kalitesi problemlerinin önemli bir kısmı tablolardaki tekrarlı kayıtlardır
Burada mükerrerden kastımız PK, ya da diğer  tekrarsız anahtarların tekrar etmesi , veya tüm satırın olduğu gibi tekrar etmesidir

Mükerrer veriyi silmeden önce mükerrerden kastımızı net bir şekilde anlamamız ve geçerli kayıt ile geçersiz kayıt ayrımını neye göre yapacağımızı bilmemiz gerekir
Burada önemli bir nokta; Eğer mükerrerlik bütün satırın aynen tekrar etmesi şeklinde ise  silinecek kaydı ayıracak bir sütun  (pseudo kolonlar hariç) bulunmaz.

Yöntemimiz, ROWID sahte kolonunu ( pseudo column) kullanarak mükerrer kayıtları işaretlemek ve silmek şeklinde olacak. Geçerli kaydı diğerlerinden ayırmak için
ROW_NUMBER sözcüğü ile tekrarlayan kayıtları sıralayacağız.

Aşağıda anlatacağımız yöntemde ROWID kullanılmasının özel bir işlevi var.
Tekrarlı kayıtları birçok şekilde işaretleyebilirsiniz. Silme aşamasında silinecek kaydı ayrımak için uyguladığınız koşul gerçek kayıt ile de birebir uyuşabilir.
Bu durumda ilgili kaydı bütün kopyeleri ile silmiş olursunuz -ki farketmesk de güç olabilir. Mesela kayıtlar birebir tekrarlıyor ise ayırmanız olanaksızdır

Sorgumuzu hazırlamak üzere örnek olarak  bir firmanın şubelerini ve yetkililerinin bilgilerini tutan şu tabloya bakalım

SELECT * FROM firma_sube

 

Örnek tablo- Firmanın  şubeleri

 
Bu tablo firma şubeleri bazında, yani her şube için tek bir kayıt olması bekleniyor. Anahtar kolon sube_id olarak . sube_kisa_ad sütunu da gene sube_id gibi tekrarsız
Tabloda, firmanın her bir şubesi için o şubenin yetkilisi, telefon numarası ve yetkilinin performans puanı bilgisi bulunuyor. Ayrıca her kaydın eklendiği tarih de kayit_ekleme_tarihi sütununda tutuluyor
 
Tabloda, zaman içinde eski kayıtlar silinmeden yeni kayıtlar eklenerek veya yanlışlıkla aynı kayıt iki kere girilerek mükerrer kayıtlar oluşturulmuş. Bazı kayıtlar birebir aynı!

–Şube_id bazında mükerrer kayıtlara bakalım. Önce her şube id’nin kaç kere tekrar ettiği ..

 

Birden fazla tekrarlananları filtreleyelim

  SELECT sube_id, COUNT (*) AS adet
    FROM firma_sube
GROUP BY sube_id
HAVING COUNT(*)>1

Bu sorgu ile aşağıdaki sorgu denktir

SELECT *
  FROM (  SELECT sube_id, COUNT (*) AS adet
            FROM firma_sube
        GROUP BY sube_id)
WHERE adet > 1 
 

Mükerrer kayıtları süzme-Having sözcüğü
 
–14 sube_id için birden fazla kayıt var.

–şimdi sıra geldi mükerrer olanlardan biri hariç diğerlerini silmeye. Silme işlemine geçmeden önce tekrarlayan kayıtlardan ahngisini silmem gerektiğini nasıl belirleyeceğimi bilmem gerekir.
Biz örnek olarak son kayıdı en geçerli kayıt olarak alacağız. Aynı kayıdın tekrarlaması durumunda ise herhangi biri zaten uygundur.

Başlayalım. Önce kayıtları numaralandırarak sonucunu görelim

SELECT s.ROWID AS row_id
     , s.*
     , ROW_NUMBER ()
          OVER (PARTITION BY s.sube_id ORDER BY s.kayit_eklenme_tarihi DESC) AS rn
  FROM firma_sube s 

Mükerrer kayıtları işaretleme- row_number-rowid
 

Buradaki row_number deyimini açalım.
    sube_id bazında tekillik istiyoruz . PARTITION BY sözcüğünün yanına sube_id yi koyduk. Tekillik başka kolonlardan olsaydı,
mesela sube_id ve yetkili_adi bazında tekrarsız olması gerekseydi PARTITON BY sube_id, yetkili_adi yazardık
    en yeni kaydın geçerli kayıt olmasını istiyoruz. ORDER  BY sözcüğü yanına kayit_ekleme_tarihi sütununu yazıyoruz. Fakat geçerli kayıt dışında birçok kayıt olabilir. Geçerli kayıtın row_number değeri her zaman 1 olmalı
    Bu sebeple sıralamayı ters yapıyoruz  ( DESC sözcüğü)
    
 
Silmeden önce kayıtları görmekte fayda var. rn değeri 1 olmayan kayıtları rowid değerinden ayırıyoruz. rowid ile ayrımak bize, aynı kayıdın birebir tekrar etmesi durumunda bile bir kaydı koruma imkanı veriyor

SELECT *
  FROM (SELECT s.ROWID AS row_id
             , s.*
             , ROW_NUMBER ()
               OVER (PARTITION BY s.sube_id
                     ORDER BY s.kayit_eklenme_tarihi DESC)
                  AS rn
          FROM firma_sube s) baz
WHERE baz.rn <> 1

Silinecek tekrarlı kayıtlar

Şimdi bu kayıtları silelim

 

 

DELETE FROM firma_sube
WHERE ROWID IN (SELECT row_id
                   FROM (SELECT s.ROWID AS row_id
                              , s.*
                              , ROW_NUMBER ()
                                OVER (PARTITION BY s.sube_id
                                      ORDER BY s.kayit_eklenme_tarihi DESC)
                                   AS rn
                           FROM firma_sube s) baz
                  WHERE baz.rn <> 1) 
 

 

 

! Dikkat!  ROWID sanal sütunu (presudo column) her satır için farklı ve tekrarsızdır. ROWNUM sütunu ile karıştırılmamalıdır. ROWNUM sanal sütunu sorgu sonucunda üretilir ve değişkendir

 

ROWID ile filtrelemek yerine kayit_ekleme_tarihi değerini çekerek ve numaralandırarak , o değere ve sube_id’ye uyan kayıtları silebilirdik. Fakat bazı kayıtlar tamamen aynı olabilir. O zaman kayit_ekleme_tarihi ve sube_id değerinin uyuştuğu kaydı da silmiş, başka bir hata yapmış olurduk. Bahsettiğimiz durumu ifade eden sql şöyle olabilir;

 

—-Hatalı sql- örnek

–DELETE FROM firma_sube
–      WHERE (sube_id, kayit_ekleme_tarihi) IN (SELECT sube_id
–                                                    , kayit_eklenme_tarihi
–                                                 FROM (SELECT s.*
–                                                            , ROW_NUMBER ()
–                                                              OVER (
–                                                                 PARTITION BY s.sube_id
–                                                                 ORDER BY
–                                                                    s.kayit_eklenme_tarihi DESC)
–                                                                 AS rn
–                                                         FROM firma_sube s)
–                                                WHERE rn = 1)