Sayfalar

19 Ocak 2013 Cumartesi

HR Şeması İle Uygulamalı SQL(DML ve Veri Tutarlılığı)

Merhaba arkadaşlar!

Biliyoruz ki SQL deyimleri, kendi içerisinde komutların amaçlarına göre bir kaç başlık altında incelenirler. Bu başlıklardan SQL için olanları iki şekilde inceleriz:

  • DML-Data Manipulation Language: Ver işleme dili
  • DDL-Data Definiton Language: Veri tanımlama dili
Neden özellikle SQL için diye belirttim çünkü özelleşmiş SQL versiyonlarında (PL/SQL- T-SQL v.b.) eklenen diğer komut ve deyimlerle bu maddeler çoğalmaktadır. Örneğin COMMIT ve ROLLBACK komutları DDL yada DML komutları değildir. Veri kontrol dili (Data Control Language) başlığı altında incelenen bu komutlarla birlikte, kullanıcıya haklar tanıma, rol oluşturma gibi diğer özel işlemler gerçekleştirilir. Şimdilik SQL ile çalıştığımızdan bunlar ile işimiz yok. Ancak burada bir kaç DCL komutu ile ilgili bilgi vereceğiz. Çünkü DCL Oracle ile çalışmanın püf noktalarından ve veri tutarlılığı için büyük önem arz etmekte. 

Bu yazıda DML komutlarını ele alıp uygulayarak açıklamaya çalışacağız. Bir sonraki yazı da DDL ile ilgili olacak. Başlayalım:
Data Manipulation Language
Tablolar üzerinde bulunan veriler ile çalışırken onları değiştirecek bir takım işlemler yapan komutlardır. Temelde SQL olarak 3 komutla incelenirler. Bakalım DML komutlarımız nelermiş:
  • INSERT: Var olan bir tablo üzerine yeni veriler/satırlar eklemek için kullanılan komuttur. 
  • UPDATE: Var olan tablo üzerindeki var olan verileri güncellemek/değiştirmek istediğimizde kullanacağımız komuttur.
  • DELETE: Yine var olan tablo üzerindeki bir satırı, bilgiyi silmek için kullanacağımız komuttur.
Gördüğünüz gibi DML ifadelerini tanımlarken hep "var olan" ifadesini kullandık. Demek ki DML için aklınızda tutmanız gereken anahtar sözcük bu. Şimdi bu ifadeleri başlıklar halinde inceleyelim.

1. Bir Tabloya Veri Ekleme
İfadenin genel şekli aşağıdaki gibidir ve ilgili tabloya bir satır eklenmesini sağlar:

INSERT INTO  tabloismi [(sütunismi[, sütunismi...])]
VALUES  (deger [, deger...]);

Yukarıdaki ifadeden görüldüğü gibi tablo ismini vermeniz zorunluyken, sütun isimlerini vermeniz zorunlu değildir. Tabi sütun isimlerini vermiyorsanız tablodaki tüm sütunlar için bir değer girmeniz gerekecektir. Tabloda NULL kısıtı bulunan satırlara değer girmeyen bir INSERT yazarsanız ifadeniz düzgün çalışmayacaktır.Tabloya değerler girilirken tablonun sütun sıralamasına göre değer girilmeye dikkat edilmelidir. Bir diğer önemli husus ise sütun veri tiplerini göz önüne alarak tek tırnak kullanıp kullanmayacağınıza karar vermelisiniz. Karakter ve tarih verileri tek tırnak arasında verilir. Hemen örnek bir INSERT ifadesi hazırlayalım:

INSERT INTO countrıes (country_ıd, country_name, regıon_ıd)
VALUES ('TR', 'Türkiye', 1);
1 rows inserted

INSERT INTO countrıes (country_ıd, country_name)
VALUES ('TR', 'Türkiye');

Sorgu bu haliyle de çalışır. region_id kısmı NULL olarak değer alır. Dediğimiz gibi tablodaki daha önceden var olan kısıtları göz önüne alarak satır ekleme işlemlerinizi gerçekleştirirsiniz. Örneğin ikinci bir ekleme yaparken 'TR' ülke ID'sini kullanmanız size hata olarak döner: Çünkü primary key kısıtını ihlal etmiş olursunuz. Yine tabloda ülke ID'si 2 harften fazla olamayacak şekilde kısıtlanmıştır. 3 harfli bir değer girmeniz engellenir. 

Yukarıdaki NULL ile giriş yapılan sorguya kapalı metot denir. Eğer NULL olan sütunları da manuel olarak belirtirseniz bu açık metot olacaktır:

INSERT INTO countrıes (country_ıd, country_name, region_id)
VALUES ('TR', 'Türkiye', NULL);

Sorguyu aşağıdaki gibi düzenleyerek değerleri kullanıcıdan girmesini bekleyebilirsiniz:

INSERT INTO countrıes (country_ıd, country_name, region_id)
VALUES ('&ulkeID', '&ulkeAdi', bolgeID);

Alt Sorgu İle Satır Ekleme: Bazen bir diğer tabloda bulunan verilerden diğer tabloya kopyalama yapmak isteyebiliriz. Bu yüzden bu konuya önem vermek gerekir. Bu işlem ile hem yanlış veri girişlerinin önüne geçilerek veri bütünlüğü de sağlanabilir. Ayrıca aynı anda birden fazla satır eklemesi de bu şekilde mümkündür.

İşlem anında tablo ismi belirtilir ve değer girilecek sütunlar belirtilir. Ancak VALUES kısmında değerleri vermek yerine bir alt sorgu ile değer girilecek sütunlarla eşleşen bir SELECT deyimi yazılır. HR şemasında bu konuyu örnekleyecek bir tablo mevcut değil maalesef. Biz de öyle bir tablo olduğunu varsayarak bir sorgu tasarlıyoruz. Örneğin şirket yöneticilerinin ayrı bir tabloda daha tutulduğunu ve employees tablosundan bbir kaç çalışanın şirket yöneticisi pozisyonuna geçtiğini düşünürsek şöyle bir sorgu ile employees tablosundan ilgili çalışanları diğer tabloya alabiliriz. 

INSERT INTO managers(managerID, managerName, salary, commission_pct)
  SELECT employee_id, last_name, salary + 400, commission_pct * 0.2
  FROM   employees
  WHERE  job_id LIKE '%MAN%';
11 rows inserted

Yukarıdaki alt sorguda gördüğünüz gibi aritmetik işlem operatörleriyle de çalışabilirsiniz. VALUES ifadesi yerine direkt alt sorgu girilmiştir. Böylelikle ana sorgudaki tablo ile eşleşen (eşleşmek zorunda) sütunlar eklenmiştir.

2. Bir Tablodaki Veriyi Güncelleme
Tablodaki var olan satırları değiştirmenizi sağlayan bu sorgu ile istendiği takdirde aynı anda birden fazla sütun üzerinde işlem yapabilirsiniz. Sorgunun genel hali aşağıdaki gibidir:

UPDATE  tabloismi
SET  sütunismi = satirdegeri [, sütunismi = satirdegeri, ...]
[WHERE   sart];

Bu kullanımı incelerseniz; WHERE ile özel bir satır üzerinde işlem yapmak gerektiğinde bu şartı belirtirsiniz. Eğer bunu belirtmezseniz tablodaki tüm satırlar değiştirilir! Buna dikkat etmelisiniz. SET kısmında ise sütun ismini verip ardından bu sütunun yeni değerini belirtirsiniz. 

Countries tablosuna eklediğimiz Türkiye'nin region_id satırını düzenleyelim:

UPDATE countries
SET     region_id = 4
WHERE country_id = 'TR';
1 rows updated

Eğer WHERE şartı kullanmasaydık tablodaki tüm region_id'ler 4 olarak atanacaktı. Yine burada da dilerseniz bir satıra sütunismi = NULL diyerek NULL değer ataması yapabilirsiniz.

Alt Sorgu Kullanarak Satır Güncelleme: Satır ekleme de olduğu gibi satır güncelleme işleminde de alt sorgular kullanabilirsiniz. Bir 103 employee_id'li IT_PROG çalışanının job_id'sini ve maaşını değiştirmek istersek şu sorguyu kullanmamız gerekir:

UPDATE   employees
SET      job_id  = (SELECT  job_id 
                    FROM    employees 
                    WHERE   employee_ıd = 101), 
         salary  = (SELECT  salary 
                    FROM    employees 
                    WHERE   employee_ıd = 101) 
WHERE    employee_id    =  103;
1 rows updated


3. Tablodan Veri Silme
Aşağıdaki şekilde kullanılır:

DELETE [FROM]    tabloismi
[WHERE    sart];

Eğer yine şart belirtmezseniz tablodaki tüm verileri kaybedersiniz. Bu yüzden bu deyimi kullanırken dikkatli olmanızı öneririm. Gerçi işlemi COMMIT etmedikten sonra problem yok demektir ancak yine de elinizi şart yazmaya alıştırmanız gerekiyor.

DELETE FROM countrıes
WHERE country_name = 'Türkiye';
1 rows deleted



Bir de tüm tablonun içeriğini boşaltmak ancak bunu tablonun yapısını (sütunlar, kısıtlar, ID'ler) bozmadan yapmak istiyorsanız kullanabileceğiniz alternatif bir PL/SQL komutu bulunuyor. Bu komutu kullanırken dikkat etmek gerekiyor çünkü geri alması biraz problemli olabilir. Kullanımı çok basit:

TRUNCATE tabloismi;

Diğer 2 DML komutu gibi DELETE ile birlikte de alt sorgu kullanabilirsiniz. Alt sorguyu WHERE şartını belirlemek için kullanmanız gerektiğini artık tahmin ediyorsunuzdur.

İki Önemli DCL Komutu: COMMIT ve ROLLBACK
Bu konuyu anlamanız için öncelikle transaction nedir sorusunun cevabını biliyor olmalısınız. Daha önce bununla ilgili bir yazı paylaşmıştım. Buradan ulaşabilirsiniz.

Bir transaction: DML ifadelerinden, tek bir DDL ifadesinden ve son olarak da bir DCL ifadesinden meydana gelebilir. Bir transaction işlemi ilk DML komutu ile başlar ve bazı durumlarla da sonuçlanır. Bu durumları şöyle açıklayabiliriz:
  • COMMIT veya ROLLBACK kullanılırsa,
  • Sistem aniden çökerse,
  • Transaction'ı çalıştıran kullanıcı SQL*Plus yada SQL Developer'ı kapatırsa,
  • DDL yada DCL ifadeleri çalıştırılırsa otomatik olarak bir transaction sonlanır.
Kısaca Oracle Veri Bütünlüğü: Transaction'ların veritabanı bütünlüğü için kullanıldığını belirttiğimiz yazıda açıklamıştık. Bunu nasıl sağlıyoruz? Bir kullanıcı DML yani veri üzerinde değişiklik yapan komutları veritabanına uygularken transaction işlemini gerçekleştiriyor demektir. Bu işlemleri uyguladığı sürece bu işlemlerin sonucu diğer kullanıcılar tarafından görülmez olur. Aynı şekilde kullanıcının DML işlemi yaptığı satırlar diğer kullanıcıların erişimine karşı kilitlenir. Yani üzerinde değişiklik yapamazlar. Ne zaman ki yukarıdaki transaction sonlandıran bir durum söz konusu olduğunda yapılan değişiklikler diğer kullanıcılara görünür hale gelir ve artık üzerlerinde kendi transaction'larını başlatabilirler. 

Şu ayrıntıları tekrar edelim:
  • Transaction'ı icra eden kullanıcı çalıştırdığı DML komutlarının sonuçlarını veriler üzerinde görebilir ama diğer kullanıcılar transaction sonlanmadan bu değişimleri izleyemez.
  • Veri tutuarlılığı ve karşılıklı dışlama için en ideal çözümdür.
  • Kayıt noktaları: SAVEPOINT'ler kullanarak bir transaction'ı birden fazla parçaya mantıksal olarak bölebilirsiniz. Örneğin bir tablodan DELETE ile gereksiz verileri sildiniz ve oraya bir SAVEPOINT bıraktınız. Daha sonra bazı tablolarda UPDATE sorgusu ile güncellemeler yaptınız. Tekrar baktığınızda bu güncellemelerde hata buldunuz. Tüm transaction'ı mı iptal edersiniz yoksa aldığınız bu SAVEPOINT'e geri dönerek doğru olan işlemleri saklamayı yanlış olanları iptal etmeyi mi tercih edersiniz? Elbette aynı işlemleri tekrar yapmaktan sizi kurtaran SAVEPOINT'ler ile çalışmak daha mantıklıdır. Bu noktalara ROLLBACK yaparak hatalı DML ifadelerini iptal edersiniz.
Şimdi bir kullanıcının transaction başlattığını ve ilgili tablolarda güncellemeler yaptığını düşünelim. Sırasıyla aşağıdaki sorguları çalıştırsın ve her sorgudan sonra da bir SAVEPOINT eklesin.



INSERT INTO countrıes (country_ıd, country_name, regıon_ıd)
VALUES ('TR', 'Türkiye', 1);
1 rows inserted

INSERT INTO countrıes (country_ıd, country_name, regıon_ıd)
VALUES ('GR', 'Almanya', 1);
1 rows inserted




SAVEPOINT ulkeler_eklendi

INSERT INTO managers(managerID, managerName, salary, commission_pct)
  SELECT employee_id, last_name, salary + 400, commission_pct * 0.2
  FROM   employees
  WHERE  job_id LIKE '%MAN%';
11 rows inserted

SAVEPOINT yoneticiler_eklendi

UPDATE   employees
SET      job_id  = (SELECT  job_id 
                    FROM    employees 
                    WHERE   employee_ıd = 101), 
         salary  = (SELECT  salary 
                    FROM    employees 
                    WHERE   employee_ıd = 101) 
WHERE    employee_id    =  103;
1 rows updated

SAVEPOINT maaslar_guncellendi

Kullanıcı son işlemde hata yaptığını fark etsin. Eğer ROLLBACK komutunu çalıştırırsa tüm transaction'ı dolayısıyla tüm işlemleri kaybeder. Bunun yerine hata yaptığı kısımdan bir önceki SAVEPOINT'e aşağıdaki gibi dönüş sağlayabilir. 

ROLLBACK TO yoneticiler_eklendi;

Bu sayede bu kadar işlemin iptalinden kurtulursunuz.

Hangi durumlarda COMMIT hangi durumlarda ROLLBACK yapılır? 

Diyelim ki kullanıcı COMMIT yada ROLLBACK komutunu uygulamadan şu durumlardan biri oluştu: 
  • Bir DDL yada DCL komutu icra edilirse
  • COMMIT ve ROLLBACK yapılmadan SQL Developer yada SQL*Plus'tan çıkılırsa
İşte bu durumlarda sanki kullanıcı COMMIT komutunu çalıştırmış gibi tüm DML değişimleri veri üzerine uygulanır.




Ancak bu programlardan aniden (güç kesintisi gibi) anormal olarak çıkılırsa, yada sistem hata verirse kullanıcı ROLLBACK komutunu çalıştırmış gibi tüm DML işlemleri geri alınır.

Şimdi özet olarak öğrendiklerimize bir bakalım. COMMIT veya ROLLBACK komutundan önce veritabanındaki durum aşağıdaki gibidir:
  • Verilerin transaction başlatılmadan önceki hali saklanarak geri dönüşler için bekletilir
  • Transaction'ı icra eden kullanıcı veriyi değişmiş hali ile görürken diğer kullanıcılar önceki hali ile görürler
  • DML çalıştırılan satırlar kilitlidir, diğer kullanıcılar erişemez.
COMMIT; sonrası:
  • Değişiklikler etkilenen satırlara kalıcı olarak uygulanır.
  • Verinin geri dönüşler için tutulan durumu yeni durum ile değiştirilir.
  • Tüm SAVEPOINT'ler silinir.
  • Etkilenen satırların kilidi açılarak diğer kullanıcılara görülebilir hale gelir.
  • Sonuçlar herkes tarafından görülür.
ROLLBACK; sonrası:
  • Tüm DML değişimleri iptal edilir.
  • Verinin korunmuş olan önceki durumuna dönüş yapılır.
  • Etkilenen satırlar üzerindeki erişim kilitleri açılır.
Oracle ile çalışıyorsanız şöyle bir avantajınız var demektir. Eğer üzerinde çalıştığınız sadece bir DML ifadesi problem çıkarırsa, Oracle otomatik olarak bu ifadeyi ROLLBACK işlemi ile geri alır. Ancak diğer tüm başarılı işlemleriniz korunur. Sizin herhangi bir komut yazmanız beklenmez.

Kısaca Okuma Tutarlılığı: Aslında yukarıdaki işlemlerde okuma tutarlılığını tanımlamış olduk. Çünkü bir veri üzerindeki değişimler sağlanırken diğer kullanıcıların bu verilere erişiminin engellenmesinin en önemli amacı okuma tutarlılığıdır. "Aynı anda iki kullanıcının bir satırı güncellemeye çalışması durumu olabilir mi?" Bu gibi durumların önüne geçmek için transaction'lar esnasında kilitleme işlemleri yapılır. Bu tüm gelişmiş veritabanlarında böyledir. Oracle'ın bu konuda öne çıkan özelliği satır bazında kilitleme yapar. 

Okuma tutarlılığı ile verileriniz çelişmez ve sağlıklı bir görünüm kazanır. Okuma tutarlılığının sağlanması için şu kurallar uygulanır:
  • Bir satırda yazma işlemi yapmak isteyenler aynı satırda yazma işlemi yapanları beklemek zorundadır
  • Bir satırı okuyanlar, aynı satırda yazma işlemi yapanları beklemez.
  • Bir satırda yazma işlemi yapanlar, aynı satırı okuyanları beklemez.
Yazma işlemlerini INSERT ve UPDATE olarak düşünün. Okuma işlemlerini de SELECT sorguları olduğunu düşünün. 

Okuma tutarlılığında veritabanı performansına katkıda bulunabilecek bir ifade vardır. Bu FOR UPDATE ifadesidir. Kullanımı aşağıdaki gibidir:

SELECT employee_id, first_name
FROM employees 
WHERE department_id = 50
FOR UPDATE 
ORDER BY employee_id;

Bu sorgu ile departman ID'si 50 olan satırları ROLLBACK yada COMMIT ifadeleri kullanılasaya kadar kilitlemiş olursunuz. Eğer siz bu sorguyu zaten bir başka kullanıcı tarafından kilitlenen satırlar üzerinde çalıştırırsanız, o zaman sadece sorguya ait sonuçları görürsünüz. Kilitleme işleminiz geçersiz olur. Dilerseniz bu özelliği şu biçimde de kullanabilirsiniz:
...
FOR UPDATE OF sütunisimleri
...

Bu sayede sadece ilgilendiğiniz sütunların satırlarını kilitlersiniz. Diğer kullanıcıların faaliyetlerini daha az kısıtlamış olursunuz. 

Böylece konumuzu tamamlamış oluyoruz. İyi çalışmalar!

Hiç yorum yok:

Yorum Gönder