You are on page 1of 88

Structured Query Language (SQL)

VERİTABANINA ERİŞİM
Tipik bir iş yeri veri tabanı yüklemesinde, veri tabanının çalıştığı bilgisayarı göremezsiniz.
Veritabanı, server odasındaki server’dadır ve siz masaüstü bilgisayarla çalışacaksınızdır. Veritabanıyla
bağlantı kurmak için ona ağ üzerinden bağlanmalısınız.
Genelde bağlantınız LAN (yerel ağ bağlantısı) veya WAN (geniş ağ bağlantısı)’la sağlanır.
Bilgisayarınız, ağ arayüz kartı ve bu karta takılı ağ kablosuna sahiptir. Bu kartların en yaygını Ethernet
adaptörüdür. Bununla beraber Token ring adaptör de kullanabilirsiniz. Ağ arayüz kartı server’la iletişim
sağlar (istekte bulunur, cevap alır). İletişim, protokoller adı verilen bir grup sözleşmeye göre yapılır.
Bunların en yaygını Transmission Control Protocol / Internet Protocol (TCP/IP)’dir.
Protokoller, ağdaki diğer bilgisayarlarla iletişimi sağlamak için 2 tip isme izin verirler. Her
bilgisayar, kendisini ağda belirten, karakterlerden oluşmuş bir string’e sahiptir. Ağdaki bir bilgisayarla
iletişim sağlamak isterseniz o bilgisayarın ismini Run diyalog kutusuna yazmanız yeterlidir (Windows’un
yerel sürücüde aramaması için başına 2 tane backslash (\\) koymalısınız).
Diğer bir bağlanma yolu da ağ adresiyledir. Ağlar, isimleri adreslere eşlemek için özel servisler
kullanırlar. Bunlardan en yaygınları Internet’te kullanılan Domain Name System (DNS) ve Windows
altında çalışan Windows Internet Name Service (WINS)’tir. Eğer bunlar çalışmazsa bağlanacağınız
bilgisayarın adresini bilmeniz gerekir.
 
SORGULAMA ARACINI KULLANMAK
Bütün veri tabanları değişik tipte sorgulama aracı sunarlar. Windows altında çalışan bütün
sorgulama araçları bazı yaygın özelliklere sahiptir. Hepsi de başladıklarında veritabanıyla bağlantıyı
sağlamak için bağlantı diyalog kutusunu ekrana çıkarır. Her bir bağlantı penceresi 2 parçadan oluşur:
Birisinde SQL sorgusunu yazarsınız, diğerinde de sorgulamanın sonucunu görürsünüz.
SQL Server’ın Query Analyzer aracında bağlantı diyalog kutusunda 3 tip bilgi vardır: Birincisi,
server’ın ismi (adresi de kullanılabilir). İkincisi, eğer server durdurulmuşsa yeniden başlatılıp
başlatılmayacağı. Sonuncusu da sizi doğrulamak için kullanıcı adı ve şifre.
Genel olarak Windows altında veritabanına bağlantı için 2 yöntem vardır: ODBC kullanmak veya
OLE DB kullanmak. (SQL Server Query Analyzer her iki yöntemi de desteklemektedir.)
 
ODBC’Yİ KULLANMAK
Her veritabanı satıcısının veritabanına bağlanmak, login olmak ve sorgu yollamada kendine has
aracı vardı. Kurumlar için karışıklıktan düzenin daha anlamlı olduğu düşünülmüş ve Microsoft ile büyük
satıcılar Open Database Connectivity (ODBC) adı verilen stratejiyi geliştirmişlerdir.
ODBC, kullanıcı tarafında veritabanına bağlanmak için standart bir yol, işletim sistemi tarafında
her satıcıya veritabanının ihtiyaçlarını karşılamak için kullanıcı tarafından sağlanan bilgiyi çevirmeye izin
veren uygulamanın bir katmanı haline geldi.
Denetim Masalarından ODBC applet’ini açtığınızda diyalog kutusunun 3 sekmesini ayarlama
ihtiyacı duyarsınız.
Veritabanına bağlantı kurarkenki ilk sorunuz bu 3 sekmeden hangisini kullanacağınız olacaktır.
Bağlanmak için Data Source Name (DSN) oluşturmanız gerekir. Eğer bir kullanıcı User DSN
oluşturmuşsa bu DSN login olmuş kullanıcıyı bağlar. Eğer siz login olmuşsanız veritabanı bağlantısına
erişebilirsiniz. Eğer ben login olmuşsam sizin oluşturduğunuz User DSN’e erişemem. User DSN’ler,
değişik kullanıcıların aynı bilgisayarı paylaşması, fakat değişik veri kaynaklarına erişmesi gerektiğinde
kullanıma uygun olan bir durumdur.
Bir System DSN, zıttına, sisteme ilişkilendirilmiş veri kaynağı sağlar. Veritabanına herhangi bir
kişi kendi kullanıcı ismiyle login olabilir.
File DSN’ler dosyalarda tutulur ve sonuç olarak taşınabilir çapraz sistemlerde file DSN’i başka
bir sisteme taşımak sadece dosyayı kopyalamaktan ibarettir. File DSN’ler çok kullanışlıdır. Ayrıca
bunları Notepad’le açıp bağlantı stringi’nin bütün bölümlerini kontrol de edebilirsiniz.
DSN oluşturma işlemi her 3 olayda da benzerdir. Biz örnek olması açısından bir System DSN
oluşturacağız. Şu adımlar izlenir:
1.      Bağlantınız için kullanacağınız sürücüyü seçin (Biz SQL Server’ı seçtik) ve Finish’e tıklayın.
2.      Bağlantınız için isim giriniz. İsmi, ilerde veri kaynağına bağlanırken kullanacaksınız.
3.      İsteğe bağlı olarak tanımlama girebilirsiniz.
4.      Bağlanacağınız server’ı seçin. İsim yazabileceğiniz gibi drop-down list’ten de seçebilirsiniz.
5.      Kullanmak istediğiniz güvenlik düzenini seçiniz. Windows NT güvenliği varsayılandır ve
size Windows NT kullanıcı ismini kullanmanıza olanak tanır. Bu düzende alanınıza bir kez girdiğinizde
SQL Server’a bağlanmanıza gerek kalmaz. SQL Server güvenirliliğini seçerseniz veritabanı yöneticisi
tarafından oluşturulmuş olan kullanıcı adı ve şifreyi girmek zorunda olursunuz.
6.      Bir sonraki diyalog kutusu, bazı özel parametreleri ayarlamanıza izin verir. Veritabanı
yöneticisi size aksini bildirmedikçe varsayılan değerleri kullanınız. Değiştirebileceğiniz değer örneğin,
listeden seçebileceğiniz varsayılan veritabanı olabilir.
7.      Son diyalog kutusu size daha ince ayarlar yapmanıza imkan tanır. Veritabanı oturumunuz için
log dosyalarını ayarlayabilir, varsayılan şifreleme düzeneğini değiştirebilir veya sistem için kullandığınız
dili değiştirebilirsiniz. Genelde varsayılan değerleri değiştirmenize gerek kalmaz. Finish’e tıklayın.
8.      Son diyalog kutusu, bağlantı için kullanacağınız ayarların bir özetini oluşturur. En önemli
(kritik) kontrol Test Data Souce düğmesidir. Bağlanabileceğinizden emin olmak için tıklayın. Eğer
bağlanamazsanız Cancel diyerek ayarları gözden geçirin. Bağlantı ayarlarını sonlandırmak için OK’e
basın.
Bu noktada ODBC kullanarak veritabanına bağlantı sağlamış oldunuz. Şunu unutmayın ki bütün
veritabanı satıcıları bu metodu desteklerler.
 
 
OLE DB’Yİ KULLANMAK
ODBC, bilgisayar endüstrisinin veritabanına genel erişimi sağlamada ilk adımıydı. İşletim
sisteminiz için ODBC sürücünüz olduğu zaman veritabanına bağlanabilirsiniz. Bununla beraber, işletim
sisteminin servislerini kullanarak bağlantı ayarlarını yapmak zorundasınız. Bu da her makine için bağlantı
ayarlarının ayrı ayrı yapılması anlamına geliyor. Örnek verirsek, 2000 düğümden oluşan bir ağda 2000
defa bu ayrların yapılması gerekir ki bu da pek kolay bir şey değildir.
Her bir bilgisayar için bağlantı ayarları yapılma zorluğunu aşabilmek için Microsoft umumi
veritabanı erişim stratejisi üzerinde durdu. Distributed Internetworking Architecture (DNA)’nın bir
parçası olarak herhangi bir veri kaynağına bağlantısız bağlantıları sağlamaya karar verdi. DNA için
umumi veri erişiminin arkasındaki kavram, provider olarak bilinen yüklenebilen sürücüydü.
Gerçekte siz bir kullanıcıysanız ODBC Control Panel Applet haricinde direk olarak
bağlanamazsınız. Bununla beraber siz bir programcıysanız bağlanabilirsiniz ve bunu yapmak için de
ODBC ayarlarını yapmanıza gerek yoktur. Bağlantı string’i provider’ın veritabanı bağlantısını başlatması
için gerekli bütün bilgileri içerir. Bu teknolojiyi uygulamak için Microsoft, OLE DB ismini seçti. OLE
DB bağlantısı yapmak için olan programlama teknolojisi ActiveX data objects diye anılır.
Bu teknoloji, veritabanına programlama bağlantısı üzerine odaklandığı bağlantının nasıl
yapıldığına şimdilik değinmiyoruz.
 
PROGRAMDAN BAĞLANMAK
Veritabanıyla kullanışlı bir iş yapmak için programdan bağlantının nasıl yapılacağını
öğrenmelisiniz.
Bağlantı kurmak için veritabanına arayüz kullanmalısınız. Alçak seviyede, makine dili emirleri
bağlantı kurar ve programınıza veri servisleri sağlar. Makine dili veya assembler’da yazmak kolay bir iş
olmadığından dolayı yüksek seviyeli dillerde güç anlaşılır, karışık formdaki program yazımları basit
şekilde tasarlanmıştır. Bellekte veri depolamak için adresleri ve kütükleri yönetmek için şunu yazarız:

Str MyVariable = “Hello World”

Veritabanıyla bağlantı kurmak için özelleştirilmiş bir arayüze ihtiyaç vardır. Veritabanı satıcıları
bunları destekler ve bunlara tipik olarak Application Programmming Interfaces (API) adı verilir. API’ler
nispeten alçak seviyeli olabilir (programınıza bağlantı kurabileceğiniz ve veriye erişim için
çağırabileceğiniz bir grup C dili fonksiyonları gibi). API’ler oldukça yüksek seviyede de olabilirler
(Microsoft’un halkın anlayabileceği bir şekle getirdiği nesneya dayalı arayüzler gibi). Alçak seviye
arayüzler hız sağlarken, yüksek seviye arayüzler de kolaylık sağlarlar.
 
BAĞLANTI KÜTÜPHANESİNİ KULLANMAK
Alçak seviye arayüze örnek olarak C’de yazılmış ve dosya isimlerinde ODBC içeren bir grup
DLL(dynamic-link libraries)’de depolanmış ODBC API’ı verebiliriz. Tipik olarak bu kütüphaneler belirli
bir veritabanı erişim programıyla ilişkilendirilmiştir.
Bu dosyalarda içerilen fonksiyonlar fazla değildir. Bunları görmek için en kolay yol Visual
Studio development environment’ta içerilen ODBCAPI.BAS’ı açmaktır.
Usüle göre veri tabanına bağlanmak için veritabanı işaretçisine veya handle’a ihtiyaç vardır.
Handle elde etmek 2 işlem adımıyla gerçekleştirilir. İlk önce bağlantı kurmak için SQLDriverConnect’i
çağırırsınız, ardından handle’ı elde etmek için SQLAllocHandle’ı çağırırsınız. Diğer fonksiyonlar imleci
yönetmeye, kayıt kümelerini almaya ve saireye izin verir.

Bir kere uygun dahil edilme işlemini izlediğinizde veriye erişmek için fonksiyonları çağırabilirsiniz.
ODBC çağrısını direk olarak kullanarak veritabanına bağlanmanın en kolay yolu SQLDriverConnect
fonksiyonunu kullanmaktır. Bu fonksiyonu kullanmak istiyorsanız veritabanına bağlanmak için
kullanılacak geçerli bir bağlantı stringi’ne sahip olmanız gerekir. Bağlantı stringi ODBC ile 2 temel biçim
alır. İlkinde sürücü ismini ve server ismini aşağıdaki gibi açıkça belirtisiniz:
“DRIVER={SQL Server}; SERVER=AYKUT; UID=sa; PWD=MyPwd;              
DATABASE=pubs;”

Bağlantı stringi’nin bu biçiminde sürücü ismi parantez içinde yazılır. Server adı ve ek bağlantı
bilgileri düz yazıyla yazılır. Diğer biçimde, ODBC Control Panel applet’ini kullanarak kurduğunuz
DSN’in adını kullanırsınız. Bu biçim şu şekilde gözükür:

“DSN=MyRemote; UID=sa; PWD=;”

Bu durumda veritabanı server’ın ve varsayılan veritabanı’nın ismi DSN’de içerilir. Siz sadece
DSN’le bağlantı oluştururken kullanacağı kullanıcı adı ve şifreyi string’e ekliyorsunuz.
Gerçek bir bağlantı yapmak için bağlantı stringi’ni bir string değişkende saklamak
isteyeceksinizdir. Ardından, SQLDriverConnect fonksiyonunu çağırırsınız ve birkaç önemli parametre
aktarılır. Aşağıdaki Visual Basic kodu, nelerin gerekli olduğunu gösteriyor:

Dim strConnectionString As String


strConnectionString= “Driver={SQL Server}; SERVER=AYKUT; UID=sa;
PWD=MyPwd; DATABASE=pubs;”

Call SQLDriverConnect(SQL_NULL_HANDLE, Null, strConnectionString,


LenB(strConnectionString), vbNullString, 0, 0, SQL_DRIVER_NOPROMPT)

Bu üç deyimin hepsi bağlantı kurmak için gereklidir. İlki string değişken için bellekte yer tahsisi
yapar. İkincisi bağlantı stringini değişkene atar. Üçüncüsü de bağlantıyı yapar.
SQLDriverConnect birkaç gizemli parametre gerektirir. İlki, var olan bağlantı için bir handle’dır.
Var olan bir bağlantıyı tahsis etmediğimizde, aslında SQLDriverConnect’e bizim var olmayan bir
bağlantıyı ona verdiğimiz gerçeği ile baş etmesini anlatan SQL_NULL_HANDLE değerini şart
koşmalıyız. İkinicisi, çağrı yapan pencerenin handle’ıdır. Eğer, kullandığımız pencereyle ilişkilendirilmiş
bağlantı istiyorsak değer girebiliriz, aksi halde null yazarız. Üçüncü parametre bağlantı stringi’nin
kendisidir, dördüncü parametre uzunluğudur. 5. 6. ve 7. parametreler yerleşmiş olan buffer’daki bir string
buffer alıcı, uzunluğu ve adresidir. Alıcı bufferlar düşük seviyeli C programlarında yaygındırlar.
SQLDriverConnect dönüş bilgilerini bu değişkene yerleştirir ve bu sayede biz, bizimle hangi
fonksiyonların bağlantı kurmuş olduklarını görerek kontrol edebiliriz. Bu örnekte buffer kullanmadık, o
yüzden null string verdik. Son parametre ise bağlantı kurmaya çalışırken kaybolan bağlantı bilgileri için
kullanıcıyı uyarıp uyarmadığını belirten SQLDriverConnect değeridir.
Böylece ODBC kullanarak nasıl bağlantı yapılacağını öğrenmiş oldunuz.
 
DAO’YU KULLANMAK
Microsoft’un veritabanı programcılarına sağladığı ilk arayüz Data Access Objects (DAO) diye
anıldı. Kolay kullanımlarından dolayı popüler oldu. Çoğu geliştirici çevreler desteklemeye başladı. DAO,
size nesne arayüzü belirten bir referansı projenize eklemenize izin verir.
Eğer veriye erişmek istiyorsanız veritabanı nesnesini kullanmalısınız.
Microsoft dillerinde nesneleri, değişken gibi deklare ederek oluşturursunuz. Veritabanı nesnesini
oluşturmak için Visual Basic’te aşağıdaki deyimi yazarız:

Dim dbMyDatabase As DAO.Database

Burada nesneyi deklare ettik ama kullanmak için hazır değiliz. Instantiate, soyut nesneye fiziksel
bir varlık verir. DAO Veritabanı nesnesini instantiate yapmak için DAO tip kütüphanesinde tanımlanan
metodlardan birini kullanırız. DAO, fazla açıklama gerektirmeden kullanmamıza hazır olan DBEngine
diye adlandırılan nesneyi sağlar. Bu nesne veritabanı nesnelerini bizim için instantiate eden
OpenDatabase metoduna sahiptir. Visual Basic’te herhangi bir nesneyi instantiate yapmak için Set
deyimini kullanırsınız. Veritabanına DAO bağlantısı oluşturmak için şu deyimi kullanın:
Set dbMyDatabase= DBEngine.OpenDatabase(“c:\NorthWind.mdb”)
Bu, yerel dosyada saklanan veritabanı için bağlantı sağlayan en kısa bağlantı biçimidir.
OpenDatabase gerçekte 4 parametre alabilir:
* Veritabanı ismi: Veritabanı için bağlantı stringini gösteren veya dosyanın adını veren string’tir.
* Seçenekler: Bağlantı stringi’ndeki kaybolan bilgiler için kullanıcıların uyarılıp
uyarılmayacağını belirten sabit değerdir. Mümkün değerler: dbDriverNoPrompt, dbDriverPrompt,
dbDriverComplete (gerekli olduğunda diyalog kutusunu gösterir), dbDriverCompleteRequired (daha önce
belirtilen seçenekleri geçersiz sayarak gerekli durumlarda diyalog kutusunu gösterir).
* Read-Only: Veritabanı bağlantısını salt okunur yapmak için True, okuma-yazmaya açık yapmak
için False seçiniz.
* Connection: Veritabanı ismi parametresinde belirtilmeyen ek bağlantı bilgilerinin yazıldığı
string değerdir.
Bütün bilgileri içeren DAO bağlantı deyimi şöyledir:

Set dbMyDatabase = DBEngine.Open Database(“Pubs”, dbDriverPrompt, False,


“DRIVER={SQL Server}; SERVER=AYKUT; UID=sa; PWD=MyPwd;”)

 
ADO’YU KULLANMAK
ActiveX Data Objects (ADO), Microsoft’un OLE DB’ye arkadaşlık etmesi için tasarladığı yeni
veri erişim stratejisidir. ADO nesneleri veritabanına erişim için değişik bir nesne hiyerarşisi sağlar.
Nesnelerin yapısı DAO’ya göre Microsoft Access’e daha az dayalıdır. Ayrıca, çoğu kişiler ADO’yu
veritabanıyla ilişkide bulunmak için DAO’dan daha sezgisel bulmaktadırlar.
ADO kullanarak veritabanına bağlantı kurmak için geliştirme projenize referansları eklemelisiniz.
Bunu yaptıktan sonra veritabanı bağlantısı istiyorsanız bir bağlantı nesnesi oluşturmalısınız. Bağlantı
nesnesinin tek ayarlamanız gereken özelliği vardır (Connection string özelliği). Bu nesnenin bağlantıyı
açmak için çağıracağınız Open Metodu vardır. ADO’da bağlantı kurmak için aşağıdaki kodu inceleyelim:

 
    Dim cnnDatabase As ADODB.Connection

  With cnnDatabase

   .ConnectionString=”Provider=SQLOLEDB.1;Persist “_
     “Security “Info=False;User ID=sa; Initial Catalog=pubs;” _

     “Data Source=Henrys2000”

   .Open

  End With

Bağlantı stringi’ni ayarlamak oldukça açıktır. Bütün bilgiler aynı özellik kümesindedir. Bununla
birlikte, bağlantı stringi biraz değişiktir. Burada, Provider’ın ismini vermelisiniz. Provider’lar
OLEDB’nin bağlantı kurmak için kullandıkları sürücülerdir.
Bu kod çalıştığında veritabanına bağlantı sağlamış olursunuz.

VERİTABANINDAN VERİ ÇEKMEK


SQL’in desteklediği bütün veritabanları ilişkili veritabanlarıdır.
SQL’de tablo adı, verilen temel ünite veriyi organize eder. Tek bir satırdaki veriler birbirleriyle
ilişkilidir ve her bir satır kayıt adını alır.
Tip: Özel Terimler tablolarla ilişkilendirilmiştir. Tablodaki satır sayısı “cardinality” olarak
adlandırılırken, sütun sayısı ise tablonun derecesi olarak adlandırılır. Eğer iki sütun aynı veri tipine
dönüştürülebilir ise onlara alan uyumlu (domain compatible) denir. Eğer iki tablo aynı dereceye sahipse
bunlara birleşme uyumlu (union compatible) adı verilir.
Sütunlar veritabanında özel sınıflandırmada rol oynar. (Onlarda hangi tip veri tutulacağını
belirler)
Kısaca özetlersek: Veri, tablolarda depolanır. Tablolar, veri parçalarıyla ilişkilendirilmiş grup
olan satırlardan ve verinin tipine göre sınıflandırma olan sütunlardan oluşur. Bu bölümde bizim amacımız
veritabanından veriyi almak olduğu için bu, tablodan verinin alınmasına dönüşmüş olur. SQL bu işlemi
uygulamak için SELECT adında bir eylem oluşturur. Tablodan veriyi almak için bu SELECT deyiminin
değişik formlarını kullanacağız.
 
SELECT’İ KULLANMAK
SELECT emri tek başına bir iş yapmaz. (Çalıştırdığımızda hata mesajı verir) Bu yüzden
SELECT’i ek nesne ve kelime gruplarıyla beraber kullanmalısınız.
Bu deyimi kullanırken tablodan çekeceğimiz sütunları tek tek virgüllerle ayırabilir ya da * ile
bütün sütunları belirtebilirsiniz.
Bununla beraber FROM kelimesini de kullanmanız gerekir. Bu kelimenin nesnesi tablonun
ismidir (birden çoksa virgüllerle ayrılır). Örnek verirsek:

SELECT * FROM authors


Burada dikkat etmeniz gereken hususlardan ilki, sütun isimlerinde boşluk kullanılmamasıdır.
Eğer boşluk kullanmak istiyorsanız yazacağınız ismi köşeli parantezler içine yazmalısınız.
İkinci önemli husus ise, her bir kayıdın kimlik numarasının ayrı olmasıdır. Kimlik numaralarının
olduğu sütuna kimlik sütunu (identity column) adı verilir. Bu numara aynı ada sahip kayıtları
birbirlerinden ayırt etmeye yarar.
Üçüncü husus ise okunabilirlik ve anlaşılabilirlik açısından kelime gruplarını birleştirmek yerine
“_” kullanmaktır. (kimlikno yerine kimlik_no) Diğer bir yöntemi de kelimelerin ilk harfinin büyük
yazılmasıdır. Ayrıca kullanacağımız kelimeler açık olmalı (Adres yerine A yazmak anlaşılabilirliği
olumsuz etkiler).
Eğer bütün sütunları değil de belli sütunları çekmek istiyorsak:

SELECT au_fname, au_lname FROM authors

şeklinde sütun isimlerini virgüllerle ayırarak * yerine yazabiliriz.


Tip: SELECT deyimiyle döndürülen tablodaki sütunların sıralanışı ana tablodaki sıralanış olarak
değil, deyimde kullanılan sıralanış olarak döndürülür.
Tablodaki sütunları döndürürken birleştirme işlemini de yapabiliriz. Önce aşağıdaki örneği
inceleyelim:

SELECT au_lname + ‘, ’ + au_fname AS full_name FROM authors

Burada + işlemi iki sütunu birleştiriyor (string için tek tırnak kullanıldığına dikkat edin) ve
sütunun ismi de AS deyimi yardımıyla full_name olarak adlandırılıyor.
Tip: Bazı veritabanları SQL deyimlerinin sonuna  “;” konmasını ister. Buna dikkat etmek gerekir.
(SQL Server istemezken Access, Oracle ve Sybase ister)
 
WHERE
Eğer tablodan döndürülecek kayıt sayısını sınırlamak istiyorsak SELECT’te WHERE ifadesini
kullanmalıyız.
Tablonun başından itibaren belli sayıda kayıt çekmek istiyorsak TOP deyimini kullanabiliriz.
Örneğin:

SELECT TOP 10 au_lname+‘, ’+au_fname AS full_name FROM authors

TOP deyimi sadece tablodan sırasıyla kayıtları çeker. Eğer belli bir koşula göre tablodan kayıt
çekmek istersek WHERE ifadesini kullanmalıyız. Kullanımına örnek verirsek:

SELECT * FROM authors WHERE state=’UT’


WHERE’i daha iyi incelemek için veri türlerini incelemeliyiz.
Tip: Bazı veri tiplerini bazı veritabanları tanımayabilir. Örneğin MSSQL Server char ve
varchar’ın Unicode extens. Kabul etmesine karşın diğerleri etmez.

binary.................. 8000 byte’a kadar binary veri


bit....................... 0 veya 1 olan integer
char.................... Unicode olarak kodlanmamış sabit uzunluktaki string
datetime.............. 1/1/1753 den 12/31/9999 a kadar tarih va zaman değerleri
decimal............... -10^38-1’den 10^38-1’e kadar sayılar (31.2 gibi)
float.................... -1.79E+308’den 1.79E+308’ekayan noktalı sayılar
image.................. maximum 2^31 büyüklüğünde değişken uzunluk stringi
int....................... -10^38-1’den 10^38-1’e kadar onluk sayılar (31 gibi)
money................. -2^63 den 2^63 e kadar para ifade eden sayılar (10.68 gibi)
nchar.................. Unicode olarak kodlanmış sabit uzunluktaki karakter stringi
ntext................... maximum 2^31-1 uzunlukta değişken uzunluklu string
nvarchar.............. Unicode olarak kodlanmış değişik uzunlukta string
numeric............... decimal ile aynı
real..................... -3.40E+38 den 3.40E+38 e kayan noktalı onluk sayılar
smalldatetime...... 1/1/1900 den 6/6/2079 a tarih ve zaman değeri
smallint .............. -2^15-1 ten 2^15-12 e integer sayılar
smallmoney......... -214,748.3648 den 214,748.3647 ye para ifade eden sayılar
sysname............. nvarchar (128)
text..................... max 2^31 uzunluklu Unicode olamayan değişken uzunluklu string
timestamp........... Veritabanındaki kendine has sayı
tinyint.................. 0 dan 255 e integer
varbinary.............. max 8000 byte uzunluklu binary veri
varchar................ Unicode olamayan değişken uzunluklu karakter stringi
uniqueidentifier..... Dünyada kendine has olan global belirleyici
 

SIRALAMA
Verinin sıralanmasını ORDER BY deyimiyle gerçekleştirebiliriz. Örneğin:

SELECT * FROM authors WHERE contract=1 ORDER BY au_lname

Burada sıralama çeşidini belirtmezsek artan (ascending =ASC) olarak sıralanır. Azalan şekilde
sıralanmasını istiyorsak deyimin sonuna DESC ifadesini de koymalıyız. (isteğe bağlı olarak [DESC] de
yazabiliriz.)
Eğer birden çok sütuna göre sıralama istiyorsak sütunları virgüllerle ayırarak (ORDER BY
au_lname, au_fname) ve öncelik sırasına göre yazmalıyız.
 
ÖZET EKLEMEK
AVG : ortalama değeri, COUNT : döndürülen eleman sayısı (boş olmayan, sayılabilir), MAX : en
büyük değeri, MIN : en küçük değeri, SUM : toplam değeri döndürür. Örnek verirsek:

SELECT AVG(qty) FROM sales


Bu örnek qty sütunundaki değerlerin ortalamasını döndürür. (WHERE kullanılsaydı sadece o
koşulu sağlayanların ortalaması alınırdı)
 
GRUPLAMA
Eğer kayıtları gruplamak istiyorsak GROUP BY ı kullanmalıyız. Örnek üzerinde incelersek:

SELECT ord_num,SUM(qty) FROM sales WHERE qty>9 GROUP BY ord_num

Burada SQL ilk önce WHERE koşuluna uyan kayıtları ord_num a göre gruplar. Ardından da bu
gruptaki her kayıtın qty bilgilerini toplar ve ord_num sütununun yanına qty toplamlarını da ayrı bir sütun
şeklinde yazar. Eğer bu örnekte GROUP BY ı kullanmasaydık SQL hata verirdi. (Ya GROUP BY
kullanmalısınız ya da sales.ord_num bir aggregate(toplam) değer değildir diye bizi uyarırdı)
Eğer aggregate fonksiyonlarını WHERE’de kullanmak istiyorsak WHERE’de alt sorgulama
kullanılıyor olmalıdır. Aksi halde WHERE yerine HAVING ifadesini kullanmalıyız. HAVING, GROUP
BY’dan sonra icra edilir.
Veritabanından sadece birbirinden ayrı olan elemanlarını almak istiyorsak:

SELECT DISTINCT title_id FROM sales

yazarız. Başka bir örnek incelersek:

SELECT COUNT(title_id) FROM sales HAVING SUM(qty)>30

Burada COUNT ile geçerli olan 21 değer alınıyor. Bu 21 kayıdın qty toplamları 30’dan büyük
olduğu için HAVING sağlanır ve COUNT’un sonucu olan 21 değeri döner.
Eğer toplama fonksiyonunda sütun ismi verilmemişse GROUP BY’da verilmesi lâzımdır.
Ayrıca, bütün sütun isimleri toplama fonksiyonlarında içerilirse HAVING’i GROUP BY’sız
kullanabiliriz.
 

TABLOLARI BİRLEŞTİRMEK
Şimdiye kadar hep verilerimizi tek bir tablodan aldık. SQL, join adı verilen işlemle birden çok
tabloyla işlem yapmaya izin verir.

SELECT * FROM table1, table2 WHERE table1.identity = table2.identity

Burada anlaşıldığı üzere her iki tablonun da aynı kimliğe sahip elemanları döndürülüyor. Bu tür
birleşmeye iç birleştirme (inner join) adı verilir. Burada tabloya erişim tablo adı ile sağlanıyor.
(tablo_adi.sütun_adi = veritabani_ismi.sahip_adi.tablo_adi.sütun_adi)
Inner join oluşturmak için kullanılan yapı SQLS2 standartıyla değiştirildi:

SELECT * FROM table1 INNER JOIN table2 ON table1.au_id = table2.au_id

Tip: Terminolojide, On yapısında eşitlik içerene equi joins, diğer operatörleri içerene nonequi
joins, bunların her ikisine theta joins, koşulsuz joinlere de cross join adı verilir.
Inner join haricinde iki tür birleştime daha var ki bunlara outer join de deniliyor. Bunlar right ve
left join’dir.
Right join sağdaki tabloyu aynen alır, soldaki tablodaki aynı elemanlarla birleştirir. Left ise tam
tersidir. Genel yapı şöyledir:

SELECT * FROM table1 LEFT JOIN table2 ON table1.au_id = table2. au_id

Başka bir örnek verirsek: Diyelim ki bir tabloda yazarlar, diğer tabloda yazılar, başka bir köprü
tablosunda da yazı ve yazar kimlikleri var. Bu tabloları şöyle birleştirebiliriz:
SELECT * FROM authors INNER JOIN titleauthor ON authors.au_id =
titleauthor.au_id INNER JOIN titles ON titleauthor.title_id = titles.title_id

Left ve right joinleri full join kullanarak birleştirebiliriz (iki tabloyu birleştirmek).
 
ALANLARI BİRLEŞTİRMEK VE SÜTUN OLUŞTURMAK

Alanları birleştirmeyi daha önce görmüştük. (+ operatörü ile) Bunun daha bir çok yolu vardır. Önemli
olan nokta, birleştirilerek oluşturulan sütuna sonradan tekrar erişebilmek ve isimlendirmek için AS
deyimi ile ad vermektir. Örneğin:
SELECT au_lname + ‘, ’ + au_fname AS full_name FROM authors

Ayrıca matematik işlemlerini de uygulayabiliriz (+, -, /, *, ^) Örneğin:

SELECT qty*price AS total_sale FROM sales

Bunlara ek olarak tabloya ek bir sütun ekleyip, bu sütunun elemanlarına istediğimiz sabit değeri
verebiliriz:
SELECT *, ‘Temmuz’ AS the_month FROM titles

Tip: As kullanılarak oluşturulan her isme ilişki ismi (correlation name) denir.
SQL bir bilgisayar programı içinde kullanılmışsa embedded SQL denir.
Outer join’in eski hali tablo isminden sonra + koymaktı.

VERİ EKLEME
INSERT’Ü KULLANMAK
Insert deyiminin amacı veriyi tabloya yerleştirmektir. Bu nedenle minimal formunda INSERT şu
temel elemanlara ihtiyaç duyar:
         İfadeyi başlatıcak olan INSERT emri
         INSERT işlemi için hedef
         VALUES anahtar kelimesi ile belirtilen değerlerin listesi.
         Böylece minimal yapı aşağıdaki gibi olur:

INSERT authors VALUES (‘010-10-1012’, ‘lname’, ‘fname’, ‘5551212’, NULL, NULL,


0)

Tip: Bazı veritabanlarında VALUES sözcüğüne ihtiyaç duymayacaksınızdır. VALUES, SQL3


standartıyla gerekli hale getirilmişti.
NULL değerini, tablonun o alanı NULL’a izin veriyorsa kullanabilirsiniz. Aksi takdirde hata
verir.
SQL programcısı hangi sütunların NULL’a izin verdiğini nerden bilecek sorusunun cevabı
şöyledir: Eğer veritabanı yönetici araçlarına erişiminiz yoksa veritabanı yöneticisine size söylemesini
istemelisiniz. Eğer erişebiliyorsanız, o araçlardan biri size söyleyecektir.
INSERT’teki bir diğer sınırlama da anahtar sınırlaması (key constraints)dır. Anahtar, kayıtları
birbirinden ayıran bir ayraçtır (author_id gibi). Buradaki sınırlama ise şöyledir: Eğer bir kayıt ekliyorsak
önce authors’a eklersek sorun çıkmaz. Ama önce titleauthor’a ekliyecek olursak authors’ta öyle bir kayıt
bulunmadığından hata verecektir. Bu sınırlama, anahtarın birden çok tabloda bulunduğu zamanlar ortaya
çıkar.
Tip: Grafik ortamda key’i Database Management sayesinde küçük bir ikonla belirtebiliriz.
Diğer bir sınırlama da kontrol sınırlama (check constraint)dir. Bu sınırlama, girilen değerin check
constraints’te belirtilen şekilde olup olmadığına bakar. Ona göre INSERT’ü kabul veya red eder.
Constraint expression örneğine bakarsak:

([au_id] like ‘[0-9][0-9][0-9][0-9][0-9][0-9][0-9]’)

Burada, sütun ismini en sola yazıyor, peşine like komutunu ekliyor ve tek tırnak içinde girilmesi
gereken ifadeyi belirtiyoruz. (Bu örnekte, 7 haneli, 0 ile 9 arasında değişebilen rakamlardan oluşan sayı
girilmesi isteniyor.)
INSERT emri, INTO kullanmaya izin verir. (INSERT INTO) Böylece deyim, okuma açısından
daha anlaşılır olur.
Diğer bir yapı ise sütun isimlerini teker teker girmektir. Genel yapı:

INSERT INTO authors (au_id, au_lname, au_fname) VALUES (‘010-10’, ‘lname’,


‘fname’)

Burada, girilmeyen sütunlar Null olmaz. Sütun özelliklerinde bulunan varsayılan değer
bölümündeki değer olur. Eğer sütun isimlerini vermezsek ilk değere ilk sütun, ikinci değere ikinci
sütun…. a göre yerleştirilir.
Gömülü SQL INSERT’lerde sütun isimlerinin listesini kullanmalıyız.
Not: Eğer INSERT işlemini kullanan bir programa sütun eklemek istersek o sütunu ya nullable
(hükümsüz olabilen sütun) yapmalısınız ya da o sütunu varsayılan bir değerle doldurmak lazımdır. Aksi
takdirde ya INSERT’e sütunu eklemeliyiz ya da hata mesajıyla karşılaşırız.
Not: Aynı değeri birden çok tabloya eklemek istiyorsanız her biri için ayrı INSERT
kullanmalısınız.
Sütun listesini, herhangi bir kişi tablonun tanımlanmasında değişiklikler yaptığında kodun
etkilenmemesi için kullanırız.
Verinin tipini değiştirmek için 2 yol vardır: CAST ve CONVERT:
CAST (SahipOlduğunuzVeriTipi AS OlmasınıİstediğinizVeriTipi)
CONVERT (OlmasınıİstediğinizVeriTipi, SahipOlduğunuzVeriTipi, (OpsiyonelStilTanımlayıcı))
Soru: Veriyi eklemeden önce herhangi bir yolla kontrol etmeli miyiz?
Cevap: Veritabanı programlamada bir ana kural vardır:İşe yaramayanı veritabanına koymamak.
Kullanıcıdan değerleri aldığında, onların doğru (istenilen) veri tipinde olduğunu ve doğru formatta
olduğunu kontrol etmek gerekir. Eğer her defasında yanlış formatta sonuç geliyorsa CAST veya
CONVERT kullanırız. Veri doğruluğu için birden çok gereksiz kontroller karışık programlar için kötü bir
fikir değildir. Her zaman, özellikle Windows programlarda sizin farkında olmadığınız program boyunca
bir yol daha vardır. Yol belki de bütün kontrollere engel olarak veriyi veritabanına sokmaktır.
Hatırlarsanız, kötü veriler her veritabanının kullanıcı ömrünü zorlaştırır.
 
SELECT’İ INSERT’LE KULLANMAK
INSERT’ü kullanırken önce yapmamız gereken tablo oluşturulmamışsa oluşturmanızdır (SQL
Server’da Enterprise Manager ile). Diğer husus ise bu türde SELECT * ı kullanmamanızdır. Aşağıdaki
örneği incelersek:
INSERT INTO temptable SELECT authors.au_id, au_lname, au_fname, phone,
address, city, titles.title_id, title, type, pubdate FROM authors INNER JOIN
titleauthor ON authors.au_id = titleauthor.au_id INNER JOIN titles ON
titles.title_id = titleauthor.title_id

Burada SELECT * ın kullanılması teknik olarak 2 tane au_id elde etmemize neden olacaktır.
(Aynı şekilde title_id de) Bu hatadan kaçınmak için tablonun bütün sütunlarını liste halinde alıyoruz.
Aynı yapıyla INSERT te SELECT içinde WHERE ve HAVING sözcüklerini de kullanabiliriz.
 

SELECT INTO’YU KULLANMAK


Bazıları tablo oluşturmanız gerektiğini ama henüz tablo oluşturmayı görmediğinizi
söyleyebilirler. Aslında, tablo oluşturmak için tablo oluşturmayı bilmenize gerek yoktur. Tek bilmeniz
gereken küçük bir hiledir. SELECT deyiminin, tek bir SQL deyiminde kayıtları seçen, tablo oluşturan ve
kayıtları tabloya yerleştiren bir şekli vardır. Bu deyim SELECT … INTO dur. Aşağıdaki örneği
inceleyelim:

SELECT authors.au_id, au_lname, au_fname, phone, address, city,


titles.title_id, title, type, pub_id, pub_date INTO temptable FROM authors
INNER JOIN titleauthor ON authors.au_id = titleauthor.au_id INNER JOIN titles
ON titles.title_id = titleauthor.title_id

Kabul ediniz ki bu kullanışlı bir hiledir. Burada da SELECT * kullanamayız (belirsizlikten


dolayı). INTO sözcüğü INSERT INTO gibidir. Farkı ise tabloyu oluşturmasıdır. FROM, WHERE,
HAVING… istediğimizi kullanabiliriz. Ayrıca yeni bir satır da ekleyebiliriz. (…. INTO temptable, 1 AS
NewColumn FROM…) Bu örnekte içeriği tüm kayıtlar için 1 olan NewColumn isminde bir sütun
oluşturulur.
 
 

VERİYİ GÜNCELLEMEK
Bu ünitede tabloda bulunan veriyi değiştirmeyi inceleyeceğiz. Veritabanında hazır yerleştirilmiş
bir veriyi değiştirmek için kullanılan SQL emri UPDATE’dir. UPDATE, veriyi değiştirmek için esneklik
sağlarken aynı zamanda da tablodaki bütün verileri değiştirmeye gücü yettiğinden tehlikeli bir komuttur.
Kritik bir noktayı da vurgulamalıyız ki: WHERE veya HAVING’i kullanmayı unutmamalısınız. Eğer
unutursanız tablodaki bütün kayıtlar değiştirilecektir.
 
UPDATE’İ KULLANMAK

Güncelleme deyimi UPDATE emri ile başlar, güncellenecek tablonun ismiyle devam ettirilir, atama
operatörü ve ardından sütuna yerleştirilecek değerle sonlandırılır. Güncelleme için sadece bir tablo
kullanabilirsiniz. Buna karşın birden çok sütunu virgüllerle ayırarak aşağıdaki şekilde
güncelleyebilirsiniz:
UPDATE authors SET Contract=1, Phone=’’

 
WHERE SÖZCÜĞÜ EKLEMEK

Eğer veri tabanınızdaki bütün kayıtları güncellemek istemiyorsanız WHERE yapısını kullanmalısınız.
Örneğin:
UPDATE authors SET Conract=1 WHERE Zip=’40205’

WHERE yapısını kullanınca kayıtların tanımlanması önem kazanıyor.


Herhangi bir matematiksel veya mantıksal karşılaştırma işlemlerinde WHERE yapısı iyi çalışır.
Ayrıca LIKE yapısıyla da kullanabiliriz. Örneğin:

UPDATE authors SET Contract=1 WHERE Zip LIKE ‘402%’

Bu örnekte dizi 402 ile başlar ve diğer karakterler herhangi bir karakter kombinasyonu olabilir
(40205, 40213, 402AB).
Tip: Eğer wildcard karakterleri (% gibi) kullanma ihtiyacı duyarsanız, bu karakterleri özel anlam
taşımaması için köşeli parantez içinde yazmalısınız. (‘4020[%]’ gibi) Diğer bir örneği incelersek:

UPDATE authors SET Contract=1 WHERE Zip NOT LIKE ‘402%’

Bu örnek NOT sözcüğünün kullanılmasını gösteriyor. Bu, ifadenin tersini alır.

UPDATE authors SET Contract=1 WHERE Zip LIKE ‘[456]0205’

Bu örnek, ilk karakter olarak köşeli parantezler içinde kullanılan karakterlerden birisinin gelmesi
gerektiğini gösteriyor. (40205,50205,60205) Başka bir örnek:
UPDATE authors SET Contract=1 WHERE Zip LIKE ‘[^456]0205’

Burada kullanılan ^ tersi anlamındadır. Yani ilk karakter 4,5,6 haricinde başka bir karakter, diğerleri 0205
olmalı anlamındadır.
UPDATE authors SET Contract WHERE Zip LIKE ‘4020_’

Burada, alt çizgi karakteri, bu yere geçerli bir karakter konulacağını gösteriyor. Böylece 4020 ile
başlayan bir sonuç istenmiş oluyor.
WHERE yapılarında kullanabileceğimiz diğer ilginç ayar ise IN sözcüğünü kullanmanızdır. IN’i
aşağıda da gösterildiği gibi sütun değerlerinin bir listesini belirlemek için kullanırız:

UPDATE authors SET Contract=1 WHERE Zip IN (‘40205’, ‘40213’)

IN, argümanı parantezler arasında virgüllerle ayrılmış bir değerler listesi olarak alır. Belirtilen sütunda
IN’den sonra gelen, parantezlerle belirtilen değerler listesi elemanlarının olup olmadığına bakar. Hangi
kayıdın sütununda o değerlerden birisini bulursa o kayıdı günceller.
WHERE discounttype LIKE ‘%20!%%’ ESCAPE ‘!’

Bu örnekte ilk ve son %’ler wildcard karakter olarak algılanıyor. ESCAPE karakter olarak
belirlenen ! den sonra gelen karakter olan % ise karakter olarak algılanıyor. Yani bu örnekte 20% içeren
elamanlar aranıyor.
Tip: Wildcard karakterleri string’inizde ESCAPE yardımıyla harf olarak kullanabilirsiniz. Yani,
# , % gibi ifadeler sade şekilde yazılsalar rakam, harf gibi anlamları vardır. Bunları ESCAPE yardımıyla
string içinde gerçek harf olarak kullanabiliriz. Burada yapmamız gereken, normal harf olarak
kullanacağımız karakterden önce bir ESCAPE karakter kullanmak ve bu ESCAPE karakteri sonda
ESCAPE ifadesi ile belirtmektir.
 
HAVING YAPISI EKLEMEK
Biraz karmaşık gözüken bir örneğe bakarsak:

UPDATE sales SET payterms=’cash only’ FROM (SELECT title_id FROM sales GROUP
BY title_id HAVING SUM(qty)>30) AS MySelect WHERE sales.title_id =
MySelect.title_id
Bu örnekteki UPDATE’i de SELECT’i de daha önce gördük. Görmediğimiz, bu metodların
birbirine bağlanma şekliydi. FROM’la, FROM anahtar sözcüğünü takip eden bilgiye göre sales
tablosundan kayıtların bir listesini alıyoruz. Bu kayıt listesine AS yardımıyla sonradan kullanabilmek için
isim veriyoruz ve WHERE yapısında bu kayıtları kullanıyoruz. Diğer bir deyişle, sales tablosunun title_id
sütunu gruplandırılıp, qty toplamı 30’dan büyük olanlar MySelect tablosu adıyla seçilip, sales
tablosundan MySelect’te olanlarının payterm sütunu ‘cash only’ ye setleniyor.
 
BİRDEN ÇOK TABLO KULLANMAK

UPDATE titles SET ytd_sales=titles.ytd_sales+ SUM(sales.qty) FROM titles,


sales WHERE titles.title_id=sales.title_id AND DATEPART(yyy,
sales.ord_date)=2000

Bu örnekte birden çok tablodan eleman alıyoruz. (Ama birden çok tabloyu tek UPDATE ile
güncelleyemeyiz) Burada dikkat çeken bir konu, WHERE yapısında birden çok olan koşulu AND ifadesi
ile birleştirebiliyor olabilmemizdir. Ayrıca DATEPART fonksiyonu da çok kullanışlıdır. Parantez
içindeki ilk ifade neyin çekileceği, ikinci ifade hangi sütundan çekileceğini belirtir. Datepart int değer
döndürür. (yıl-yy,yyyy. çeyrek-qq,q. ay-mm,m. yılın günü-dy,y. gün-dd,d. hafta-wk, ww. saat-Hh.
dakika-min. saniye-ss, milisaniye-Ms.)
Tip: FROM’un içinde SELECT’i kullandığınız zaman, SELECT için ayrı bir WHERE yapısı
kullanmamalısınız. Bütün WHERE koşulları tek bir WHERE yapısında FROM sözcüğü ile ilişkili olarak
kullanılmadır. Örneğin:

UPDATE authors SET Contract=1 FROM (SELECT authors.au_id, titleauthor.title_id


FROM authors INNER JOIN titleauthor ON authors.au_id = titleauthor.au_id) AS
MySelect WHERE MySelect.au_id = authors.au_id AND MySELECT.title_id IN
(‘BU1111’, ‘PC9999’)

Not: UPDATE’de HAVING veya JOIN kullanmak için FROM (SELECT …) yapısına
ihtiyacınız vardır.
 
 

VERİYİ SİLMEK
SQL, veriyi silmek için 2 seçenek sunuyor: DELETE ve TRUNCATE TABLE. Herhangi bir geri
alma yolu yoktur. Sonuçlar kalıcıdır.
Tip: Veri silme işleminden önce veritabanınızın backup’ını almalısınız.
Silme işlemi için 2 yol var:

DELETE FROM authors

TRUNCATE TABLE authors

Bu iki ifade de tablodaki bütün verileri siler. Tablonun sütun yapısı sabit kalır. Böylece silme
işleminden sonra yeni veri rahatlıkla eklenebilir.
Peki, bu iki silme işleminin farkları nelerdir? Öncelikle şunu söyleyebiliriz ki: DELETE’i bütün
SQL veritabanları desteklerken TRUNCATE TABLE’ı hepsi desteklemez. (MsSQLServer bir çok
durumda TRUNCATE TABLE’ı desteklemesine rağmen bir veritabanından diğerine tablo kopyalarken
Data Transformation Services’ı kullanıyorsanız ana veritabanındaki tablonun verilerini silmek için
TRUNCATE TABLE’ı kullanmanıza izin vermez)
Bununla birlikte TRUNCATE TABLE yalnızca, tablonun bütün elemanlarını silinecekse
kullanılabilir. DELETE ise kayıtlarda seçime olanak sağlar.
WHERE yapısını diğer deyimlerde kullandığımız gibi kullanırız.
Bazı SQL veritabanları, onlardan birisi silindiğinde hepsi silinen ilişkili kayıtları tanımlamanıza
izin verir. Bu tür silme işlemine cascading delete adı verilir.
 

KONTROLLÜ SİLME İÇİN SORGULAMA KULLANMA


DELETE FROM titleauthor WHERE title_id IN (SELECT title_id FROM sales
GROUP BY title_id HAVING SUM(qty)>30)

Bu örnekte sorgulama title_author’daki her bir title_id’yi SELECT deyiminin sonucuyla


karşılaştırıyor. Ve SELECT deyiminin sonucundaki ifadeyle title_id’si aynı olan kayıtları tablodan
siliyor.

Bu SQL yapısı bütün SQL veritabanlarında kullanılır. Microsoft’un Transact SQL’i aşağıdaki yapıyla
JOIN’i de kullanıyor:
DELETE titleauthor FROM titleauthor INNER JOIN titles ON
titleauthor.title_id = titles.title_id WHERE titles.title_id LIKE ‘%novel%’
Microsoft, toplama fonksiyonları ve gruplandırmaya bu yapıda izin vermiyor. Yani toplama
fonksiyonu ya da gruplandırma kullanacaksanız JOIN kullanamazsınız.
Yanlış kayıtları silmemenin en iyi yolu her kayıdın kendine has bir kimlik no’sunun olmasıdır.
Diğer bir yol da transaction kullanarak veriyi silmeden önce ne olacağını görebilmektir.
Eğer DELETE’i bir SELECT koşulu ile kullanmayı planlıyorsanız her zaman SELECT INTO’yu
kullanın. SELECT INTO, tablo oluşturarak verilerin kopyasını oraya atar. Böylece hangi kayıtların
tablodan silindiğini görebilirsiniz.
TRANSACTION’LARI KULLANMAK
TRANSACTION size, kazayla silinen kayıt için bir kurtarma noktası sağlar. Bu kurtarma noktası
tam bir geri alma değildir. Çünkü transaction’a tamamlamasını söylediğiniz zaman yaptığınız işlemlerden
geri dönemezsiniz. Buna rağmen transaction ne olacağını size gösterir ve buna göre karar vermenize
olanak sağlar.
Transaction’lar ya tamamı başarılı ya da tamamı başarısız olması gereken SQL deyim gruplarıdır.
Grup, tek bir SQL deyimi kadar küçük olabilir. Transaction işleminde herhangi bir hata oluştuğunda
bütün emirler başarısız olur. Hiç hata olmazsa, hepsi başarılı olur. Transaction’ın bir dönüm noktası
vardır. Bu noktada hataları kontrol edebilir ve transaction’ı nasıl tamamlayacağınıza karar verebilirsiniz.
Transaction’ı uygulamak veya geri dönmek için başlangıç ve bitiş noktalarını işaretlemek gerekir.
Bu amaçla birkaç T-SQL deyimi vardır:
         BEGIN: Başlangıç noktasını belirler.
         COMMIT: Transaction’ı veritabanının geri dönülmeyen kalıcı parçası yapar.
         ROLLBACK: Transaction’ın başından itibaren yapılanları unutmak istediğimizi belirtir.
         SAVE: Kısmi geri almalar sağlamak için özel işaretçiyi belirtir.
BEGIN TRAN: Bir birimin başlangıcı olan işareti gösterir. Bazı sebeplerden dolayı
başaramadığımız veya transaction’ın uygulanmamasını istediğimizde bütün veritabanı işlemlerinin geri
döndürüleceği noktadır. Yapısı şöyledir:
BEGIN TRAN[SACTION] [transactionName | @transactionVariable]
COMMIT TRAN: Transaction’ın uygulanması, tamamlanmış transaction’ın sonlandırılmasıdır.
Bu, transaction’ı kalıcı yapar (sistem hatası olsa bile). Transaction’ın uyguladıklarını geri almanın tek
yolu, ilk transaction’ın fonksiyon olarak tersi olan başka bir transaction oluşturmaktır. Yapısı:
COMMIT TRAN[SACTION] [transactionName | @transactionVariable]
ROLLBACK TRAN: Rollback, transaction’ı başa döndürür. Begin’den itibaren yapılan işlemler
unutulur. Begin’e dönmenin tek istisnası savepointler kullanmaktır (Begin yerine bu saklı noktalara da
dönebilirsiniz). Yapısı:
ROLLBACK TRAN[SACTION] [transactionName | savepointName | @transactionVariable]
SAVE TRAN: Transaction’ı saklamak esasen bir işaret koymaktır. Bu noktadan sonraki işlemleri
geri almak isterseniz Rollback’te bu işaretin adını verebilirsiniz. Bir transaction’da birden çok işaret
koyabilirsiniz. Yapısı:
SAVE TRAN[SACTION] [savepointName | @savepointVariable]
Bir örnek incelersek:

BEGIN TRAN TranStart

INSERT INTO Orders DEFAULT VALUES

SAVE TRAN FirstPoint

INSERT INTO Orders DEFAULT VALUES

ROLLBACK TRAN FirstPoint

INSERT INTO Orders DEFAULT VALUES

SAVE TRAN SecondPoint

INSERT INTO Orders DEFAULT VALUES

ROLLBACK TRAN SecondPoint

INSERT INTO Orders DEFAULT VALUES

COMMIT TRAN TranStart

Burada Transaction başlatılıyor, bir kayıt ekleniyor ve buraya işaret koyuyor. Sonra bir kayıt daha
ekliyor sonra ilk koyduğu işarete geri dönüyor. Ardından yine satır ekliyor ve ikinci bir işaret daha
koyuyor. Bir kayıt daha ekleyip ikinci işarete geri dönüyor. Son olarak bir kayıt daha ekliyor ve bu
transaction’ı saklıyor, yani veritabanının bir parçası haline getiriyor (değişiklikler kaydediliyor). Burada
ROLLBACK deyiminde, saklama noktasının ismi verilmemiş olsa veya begin’deki isim verilmiş olsaydı
en başa dönerdi.
 TABLO OLUŞTURMAK
Daha önce tablo oluşturmak için SELECT INTO’yu kullanmıştık. Yalnız, bu metodun bazı
sınırlamaları vardı. İlki, tablonun bu sorgulama üzerine kurulması gerektiği, ikincisi de unique identifier’a
izin vermemesi idi.
 
CREATE TABLE’I KULLANMAK

Genel yapısı şöyledir:


CREATE TABLE NewTable (NewValue INT, NextValue VARCHAR (6))

Not: Mümkün oldukça char yerine varchar kullanın. Çünkü char’da, belrtilen sayıda karakter
girilme zorunluluğu vardır. Varchar’da ise o sayıya kadar istenilen sayıda karakter girilebilir.
Eğer kesinlikle Unicode’a ihtiyaç duymayacağınızdan eminseniz n ile başlayanlar (örneğin nchar)
yerine standart veri tipini (örneğin char) kullanın. Çünkü, Unicode’u depolamak için ek bir byte
kullanılmaktadır.
Mümkün oldukça küçük veri tiplerini seçmeye çalışın. Tiny ve small şekillerini yeterli olduğu
zamanlar kullanmaya çalışın. Çünkü bu veri tipleri daha az yer harcar.
Mümkün oldukça binary ve text veri tiplerinden kaçının. Eğer resim koleksiyonu gibi binary
large objects’in bir koleksiyonunu depolamanız gerekiyorsa, grafik dosyalarının adresini veritabanında,
dosyaları başka bir klasörde depolama yöntemini kullanın. Böylece veritabanınız daha az yer tutacak ve
sorgulamalarınız daha hızlı olacaktır. (Aynısı uzun text’ler için de düşünülmelidir)
Ayrıca, veri tiplerini belirlerken varsayılan değer ve nullability’yi de tablodaki sütunlar için
belirleyebiliriz. Yapı şöyledir:

CREATE TABLE NewTable (NewValue INT DEFAULT 0)

CREATE TABLE NewTable (NewText CHAR (6) NULL)

CREATE TABLE NewTable (NewText CHAR (6) NULL DEFAULT ‘ABCDEF’)

İlk örnekte varsayılan değeri 0 olan bir sütun ile tablo oluşturduk. İkincisinde sütunun değerinin
boş olmasına olanak sağladık (NULL yazarsak boş olabilir, aksi halde 6 karakterlik string girmeliyiz).
Üçüncüsünde ise varsayılan olarak ‘ABCDEF’ yi aldık ve isteğe bağlı olarak (NULL yazarsak boş)
kendimizin de değer girebileceği şekilde ayarladık.
Not: Eğer veritabanınız, dosya isimlerini veya diskte veri içeren dosyaları temsil eden dosya
gruplarını destekliyorsa ON kelimesini kullanarak tablo oluşturmak için hangi dosyaların kullanılacağını
belirleyebiliriz.
Şimdiye kadar gördüklerimize özet olması açısından aşağıdaki örneğe bakabiliriz:
CREATE TABLE publishers (pub_id char(4) NOT NULL, pub_name varchar(40)
NULL, city varchar(20) NULL, state char(2) NULL, country varchar(30) NULL
DEFAULT(‘TURKIYE’))

Burada dikkat etmeniz gereken nokta, NULL gibi NOT NULL ifadesinin de kullanılabiliyor
olmasıdır (Boş olmaması gereken ifadelerde)
 
TABLOLAR ARASI ANAHTARLAR
Tablolar genellikle anahtar değerler içerirler. Anahtar değerler her bir kayıt için kendine hastır.
Anahtar, yalnızca bir sütun olmak zorunda değildir. Bir anahtar oluşturmak için birkaç sütunu
birleştirebilirsiniz. (Bunlara multipart key denir) Key’ler 2 genel tipte olur: primary ve foreign.
Primary key’ler SQL veritabanlarının key sistemlerinin temeli olduğu için bu konuyla başlayalım:
Her tablo, kayıtları birbirinden ayıran tek bir sütun veya sütunların birleşimi şeklinde olan primary
key’lere sahip olmalıdır.
Tip: SQL dökümantasyonları birkaç key türünü tanımlar: Candidate key, bütün kayıtları
birbirinden ayıran bir sütundur. Surragate key, asıl manası olmayan bir key’dir (Arabanın seri numarası
gibi). Intelligent key, bazı gerçek anlamları olan bir key’dir (coğrafik bir yeri belirtmek için key’de
kullanılan enlem ve boylam gibi). Super key ise bir çok sütun içeren bir key’dir.
Bir sütundan oluşan primary key oluşturmak için şu yapıyı kullanırız:

CREATE TABLE NewTable (NewText CHAR(6) NULL PRIMARY KEY)

Bir foreign key, diğer bir tabloda ortaya çıkan bir primary key örneğidir. (Örneğin SQL Server’ın
pubs veritabanında titleauthor tablosu foreign key’ler içerir (au_id ve title_id)) Başka tablodaki primary
key’lere yönlendirmenin sebebi, tabloları birleştirmek için basit anlamlar kazandırmaktır. (Biz şimdiye
kadar titleauthor’un bize bir yazarın bütün başlıklarını seçmemize olanak tanıdığını gördük) Foreign key’i
temsil eden bir sütun oluşturacağımız zaman aşağıdaki yapıyı kullanırız:

CREATE TABLE employees (job_id int REFERENCES jobs(job_id))

Tip: Foreign key’i oluşturmadan önce primary key’in mevcut olup olmadığından emin olun.
 
INDEX’LER EKLEMEK
Veritabanınızdaki sorguların hızını artırmak için en uygun yol, verinizin fihristesini (index)
oluşturmanızdır. Bir index, değerleri bir sütunda hızlı şekilde bakma yoludur. Index’ler size değerler
listesinde bir değere işaret koymanıza izin verir ve ondan sonra o değeri bir alan veya bir dizi alanla çift
çift tertip eder. Böylece hızlı arama algoritmaları çalıştırılır.
Key değerinde index oluşturmak kolaydır. Bir çok veritabanı, tablodan primary key’i otomatik
olarak index’ler. Primary key’ler her kayıtı kendine has şekilde belirlediği için JOIN’de kullanmak için
iyi bir düşüncedir. JOIN’ler hızlı olması gerekir ve primary key’leri index’lemek, başka kullanıcı veya
yöneticinin müdahalesi olmadan JOIN’in hızını artıran emin bir optimizasyon stratejisidir. Bir çok SQL
veritabanında constraintleri oluşturduğunuzda index oluşturabilirsiniz. Ayrıca, CREATE INDEX’i de
kullanabilirsiniz, index oluşturmak için.
Index’ler üç tipte olabilir: Bir unique index, iki kayıtın aynı index değerini almadığı index’dir.
Böylece her satır index’te ayrı ayrı belirlenmiştir. Bir clustered index, değerlerin sıralandığı bir index’tir.
Bu sıralama yapısı her bir kayıtı sıralı şekilde depolar. Bir nonclustered index ise kayıtları index’ler fakat
kayıtların kayıt olma sırasını değiştirmez. (Ayrıca kendine has olmalı)
Key oluşturduğunuzda index oluşturmak için aşağıdaki yapıyı kullanırız:

CREATE TABLE NewTable (NewText CHAR(6) NULL) PRIMARY KEY CLUSTERED

Burada CLUSTERED yerine UNIQUE veya NONCLUSTERED da kullanabilirsiniz. CREATE INDEX


kullanımı için de şu örneği inceleyebiliriz:
CREATE UNIQUE CLUSTERED INDEX employeeID_ind ON emloyees(emloyeeID)

Burada index’in türünü belirten kelimeler INDEX kelimesinden önce kullanılıyor. INDEX’ten
sonra ise index’in ismini yazıyorsunuz. ON’dan da sonra index’in yapılacağı tablo(sütun)’u
belirtiyorsunuz.
Tip: CREATE INDEX yazarsak (tipini belirtmeden) sade nonclustered olarak alınır.
 
CONSTRAINT’LERİ EKLEMEK
Constraint’ler primary key, foreign key, referans veya geçerli bir veri kuralı olabilir. Key
değiştiğinde neler olduğunu da belirtebilirler.

Primary key ve foreign key’leri CONSTRAINT anahtar kelimesini kullanarak da oluşturabilirsiniz.


Örneğin:
CREATE TABLE publishers (pub_id char(4) NOT NULL CONSTRAINT UPKCL_pubind
PRIMARY KEY CLUSTERED CHECK (pub_id IN (‘1389’, ‘0736’, ‘0817’, ‘1622’,
‘1756’) OR pub_id LIKE ‘99[0-9][0-9]’), pub_name varchar(40) NULL, city
varchar(20) NULL, state char(2) NULL, country varchar(30) NULL DEFAULT
(‘TURKIYE’) )

Constraint sözcüğünün primary key oluşturulmasındaki kullanımını incelersek: Ondan sonra


pub_id sütununda constraint’in uygulanacağı kayıtların belirtildiğini görürüz. Key constraint’ten sonra
CHECK ifadesini yazıyoruz. (Örnekte de görüldüğü gibi değerleri girerken IN, örnekleri girerken LIKE’ı
kullanıyoruz)
FOREIGN KEY’li yapı da aşağıdaki gibidir:

CONSTRAINT FK_backorder FOREIGN KEY (stor_id, ord_num, title_id) REFERENCES


sales(stor_id, ord_num, title_id)

Burada foreign key bir multipart key’dir ve multipart primary key’i referanslamaktadır. Şimdi de
multipart key’in nasıl oluşturulduğunu inceleyelim:

CREATE TABLE NewTable ( [stor_id] [char] (4), [ord_num] [varchar] (20),


[ord_date] [datetime] NOT NULL, [qty] [smallint] NOT NULL, [payterm] [varchar]
(12), [title_id] [tid] NOT NULL CONSTRAINT [UPKCL_new] PRIMARY KEY CLUSTERED
([stor_id], [ord_num], [title_id]) ON [PRIMARY] ) ON [PRIMARY]

Dikkate alınması gereken husus şu ki: constraint’in tanımlanması, içerilen bütün sütunların
oluşturulmasını takip etmesi gerekir. Bundan dolayı da karışıklıklar olabildiğinden multipart key’lerin
oluşturulmasının en yaygın yolu ALTER TABLE’ı kullanmaktır.
Bunlara ek olarak, veritabanında key olmayan sütunda eşsiz (unique)’liği zorlayan bir
constraint’le indeks oluşturabilirsiniz. Örneğin:

CONSTRAINT U_store UNIQUE NONCLUSTERED (stor_name, city)

Bu SQL ifadesi, unique (kendine has) olması gereken stor_name ve city sütunlarında unique
index oluşturur. Bu constraint’ten dolayı aynı şehirde aynı isimle iki mağazanız olamaz.

Foreign Key’lerde ON DELETE’e bir örnek verirsek:


CREATE TABLE employee (emp_id empid CONSTRAINT PK_emp_id PRIMARY KEY
NONCLUSTERED CONSTRAINT CK_emp_id CHECK (emp_id LIKE ‘[A-Z] [A-Z] [A-Z][1-9]
[0-9] [0-9] [0-9] [0-9][FM]’), fname varchar(20) NOT NULL, minit char(1) NULL,
lname varchar(30) NOT NULL, job_id smallint NOT NULL DEFAULT 1 REFERENCES
jobs(job_id) ON DELETE NO ACTION, job_lvl tinyint DEFAULT 10, pub_id char(4)
NOT NULL DEFAULT (‘9952’) REFERENCES publishers(pub_id), hire_date datetime
NOT NULL DEFAULT (getdate()) )

ON DELETE veya ON UPDATE’i kullanırken NO ACTION veya CASCADE ifadesini


kullanırız. NO ACTION primary key’leri referanslayan tablolarda işlemin geçerli olmamasını sağlar.
Eğer CASCADE’i kullanıyorsanız, işlem primary key’leri referanslayan tablolara da uygulanır.
 
TABLOLARI DEĞİŞTİRMEK (ALTERING TABLES)

Adından da anlaşıldığı gibi ALTER TABLE, var olan bir tabloyu değiştirir. Temel yapısı şöyledir:
ALTER TABLE MyTable ADD MyColumn VARCHAR(20) NULL

Bu örnek var olan tabloya MyColumn adında bir sütun ekler. Şimdi de bir tabloya 2 constraint
ekleyen örneği inceleyelim:

ALTER TABLE [dbo].[sales] ADD FOREIGN KEY ([stor_id]) REFERENCES [dbo].


[stores]([store_id]), FOREIGN KEY ([title_id]) REFERENCES [dbo].[titles]
([title_id])

Aşağıdaki yapıyla da bir sütun silebiliriz:

ALTER TABLE MyTable DROP COLUMN MyColumn

Biraz daha karmaşık bir örneği incelersek:

ALTER TABLE MyTable ADD

/* Bir kaçparça ekleyelim. Önce bir primary key kimlik sütunu */


column_1 INT IDENTITY CONSTRAINT column_1_pk PRIMARY KEY,

/* Sonra, aynı tabloda diğer bir sütunu referanslayan sütun */


column_2 INT NULL CONSTRAINT column_2_fk REFERENCES MyTable(column_0),

/* Daha sonra da check constraint’li bir sütun */


column_3 VARCHAR(16) NULL CONSTRAINT column_3_chk CHECK (column_3 IS
NULL OR column_3 LIKE “[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]” OR column_3 LIKE
“([0-9][0-9][0-9])[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]”

/* Son olarak varsayılan değerli bir null olmayan sütun */


column_4 DECIMAL (3,3) CONSTRAINT column_4_default DEFAULT .081

Dikkat edilmesi gereken bir husus da şudur ki: ADD bir defa yazılır ve her birinin arasına virgül
konur.
 
TABLOYU SİLMEK

DROP TABLE [dbo].[sales]

Burada tablo, içindeki bütün kayıtlarla beraber silinmiş oluyor.


Tip: Drop (silme) anahtar sözcüğü COLUMN, CONSTRAINT’le ve ALTER TABLE’la da
kullanılabilir.
Tip: Bir multipart key ayrıca composite key, multivalue key ve referential integrity constraint
olarak da adlandırılabilir.

VERİTABANI OLUŞTURMAK
Veritabanı oluşturmak için aşağıdaki bilgilere ihtiyacınız vardır:
         Veritabanı için kullanmak istediğiniz isim,
         Kullanacağınız en az ve en fazla veritabanı büyüklüğü,
         Kullanacağınız en az ve en fazla kayıt (transaction) log’u büyüklüğü,
         Kullanacağınız dosya büyüme faktörü,
         Veritabanı ve transaction log dosyalarının tutulacağı yer (adres).
Şimdi bunları teker teker inceleyelim:
Veritabanı ismi oldukça açıktır, herhalde. Veritabanına erişileceği zaman kullanılır.
En küçük veritabanı büyüklüğü, veritabanı oluşturulduktan sonra boş bir veritabanının dosya
depolama büyüklüğüdür. Biz bu veri büyüklüğü ile başlarız. Genelde 10 MB uygun bir çözümdür.
Bir diğer madde, transaction log’un büyüklüğüdür. Çoğu SQL veritabanları her veri değiştirme
işlemlerini transaction olarak var sayar. Eğer bir ekleme yapmaya çalışır ve gerçekten de yanlış olursa
veritabanı kendi kendine başlangıç durumuna geri döndürür. Bu kapalı transaction size kontrol imkanı
vermez. Her bir transaction backup amacıyla log’lanmıştır ve bu loga transaction log adı verilir. Bu
dosyalar veri dosyalarından ayrı tutulur. Bu yüzden birini kaybettiğinizde diğerini kaybetmemiş
oluyorsunuz. Eğer verinizi kaybederseniz en son backup’a geri dönüp, bütün geçmiş transaction’ları
uygulayarak hatanın oluştuğu noktaya kadar gelebilirsiniz.
Tip: Transaction log’ları ayrı sürücüde tutabilirsiniz.
Eğer transaction log’lar kapasiteye ulaşıp aşamazsa hata mesajı alırsınız ve veritabanı
değişikliklerini uygulamaya devam edemeyeceğinizden dolayı transaction log’lar için geniş bir kapasite
isterseniz 5MB genelde başlangıç için uygun bir büyüklüktür.
Maksimum veritabanı ve transaction log büyüklüğü boşluğa göredir. Bu büyüklükleri nasıl
tutacağınız veritabanınıza bağlıdır. Bazı SQL veritabanları alanın dışına çıktığınızda ALTER
DATABASE iledeğiştirmenizi gerektirir. Diğer veritabanları veritabanının işaret edilen depolama
ünitesinin alanının sonuna kadar büyümenize izin verir. Tavsiye edilen ise, eğer veritabanınız limitsiz
büyüklüğü destekliyorsa limitsiz kullanmanız, desteklemiyorsa işletim sistemi ve depolama ünitesinin
izin verdiği en büyük değeri vermenizdir.
Büyüme (growth) faktörü veritabanına bağlı olarak değişik yollarla ifade edilebilir (Megabyte ve
percent). Genellikle, megabyte’ı kullanmak zorunda iseniz büyüme faktörü olarak veritabanının ilk
büyüklüğünü kullanmak uygundur. Burada istenildiğinde alan büyüyebiliyor. (Megabyte’da lineer bir
büyüme var) Ama önemli olan nokta her alan ayrımı için geçen zaman faktörüdür. Eğer percent’a göre
büyümeyi seçerseniz üstel bir büyüme uygulamış olursunuz. Bir veritabanı örneği inceleyelim:

CREATE DATABASE Portfolio ON (NAME=Portfolio_dat,


FILENAME=’c:\mssql7\data\portfoliodat.mdf’, SIZE=10 MAXSIZE=50 FILEGROWTH=5)
LOG ON (NAME=’Portfolio_log’, FILENAME=’c:\mssql7\date\portfoliolog.ldf’,
SIZE=5MB MAZSIZE=25MB FILEGROWTH=5MB)
Veritabanı oluşturma konusu çok spesifiktir. Biz burada MSSQL ve MS Data Engine’a göre
veriyoruz.
 
 

NORMALLEŞTİRME
Veritabanıyla ilk defa uğraşanlar için tabloların niçin ve nasıl oluşturulduğu konusu ilginçtir.
OLTP (Online Transaction Processing), en yaygın olarak kullanılan veritabanı tipinden uzaktır.
Eğer veritabanıyla çalışıyorsanız, özel olarak aksini söylemedikçe başka şeyler OLTP’yi kaplar. OLTP
veritabanları isminden de anlaşıldığı gibi transaction işlemiyle tasarlanmıştır. OLTP veritabanları veri
bütünlüğü ve performans nedenlerinden dolayı bütün fazlalıkları elimine etmeyi dener.
OLAP (Online Analytical Processing) veritabanları ise tipik olarak o düşünceyle zıt kutuptadır.
OLAP veritabanları analizleme –raporlama- gibi şeyler için nesneleri değişik bir yönüyle düşünmeye
çalışırlar (az ya da hiç yapılmayan veri güncelleme, çok miktarda veri okuma gibi). OLAP’la biz asıl
olarak sadece veri okuruz. Bu yüzden genellikle fazlalıklar bize zarar vermez (yardım edebilir).
En yaygın veritabanı türü OLTP’dir. Bunun gibi, normalleştirilmiş veritabanı katmanlarının çoğu
3. normal form diye adlandırılan katmana sahiptir. Bu yüzden normal formun ne olduğu  konusunu
inceleyelim.
Veritabanındaki merkezi nesne olan tablonun ne olduğuna kısaca bir göz atarsak:
Bir tablo, benzer genel nitelikteki veri örneklerinin bir koleksiyonudur. Bu veri örnekleri, verinin
satır ve sütunlarına organize edilir. Bir tablo, gerçek hayattaki verileri veya varlığı betimlemelidir ve
diğer tablolardaki verilerle ilişkilere sahip olmalıdır. Çeşitli tablolar ve ilişkilerin çizilmesi genellikle bir
‘varlık ilişki diyagramı’ (ER Diagram) olarak anılır. (ER diagram kısaca ERD olarak kısaltılabilir)
Çeşitli ilişkilerle bir veya daha çok tabloya bağlanılmasıyla, her iki tablodaki verilerin
kombinasyonlarından kaynaklanan ihtiyacın giderilmesi için geçici tablolar oluşturulabilir.
Normalleştirme, OLTP veritabanı tasarımının köşe taşı modelidir. Normalleştirme ilk olarak
ilişkili veritabanları kavramıyla ortaya çıkmıştır. Her iki kavram da E. F. Codd’un bir çalışmasıyla
gelmiştir. Codd, “metotsal olmayan işlemleri kullanarak işlenebilen bir dizi sıralanmamış tablodan oluşan
bir veritabanı” olan 4. düşünceyi ortaya attı. Birkaç şey bu düşünce için anahtardır:
         Sıra önemli değildir.
         Metotsal olmayan bir yolla tablolar birbirleriyle ilişkili olmalıdır.
         Temel tabloları birbirleriyle ilişkilendirirken, yeni ihtiyaç durumuna gelen sanal tabloyu
oluşturabilirsiniz.
Normalleştirme, veritabanı ilişki tasarımının doğal yan ürünüdür. 6 adet normal form vardır.
(Genelde sadece 3 tane olduğu söylenir)
Normalleştirme kavramı, büyük ölçüde primary key ve ona bağlı olan sütunların ne olduğunun
belirtilmesiyle çevrelenen konuyla bağımlıdır. Normalleştirmede sık sık duyabileceğiniz bir deyim :
“The key, the whole key, and nothing but the key”
(Key, tam key, ve hiçbir şey fakat key) ‘dir. Bu deyim, 3.normal form hariç, normalleştirmenin ne
olduğunun çok kısa bir özetidir. Eğer, bütün sütunlarınız sadece tam key’e bağımlı ise siz 3. normal
formdasınızdır.
Verinizi 1. normal forma getirmeye çalışmadan önce birkaç şeyi bilmeniz gerekir:
         Tablo, bir varlığı (entity) belirtmelidir.
         Bütün satırlar kendine has olmalı ve tabloda bir primary key bulunmalıdır.
         Sütun ve satır sırası önemli olmamalıdır.
Bir tablonun bir varlık belirtmesi gerekir. Bir varlık, hakkında veri kaydetmek istediğiniz kişi, yer
veya herhangi bir şeydir. Bir varlıkta değişik şeyleri toplama cazibesinden kaçınmanız gerekir. (müşteri
ve siparişler gibi)
Ardından, sahip olmak için doğru varlıkları belirtmelisiniz. Bunların bazıları oldukça aşikar
olacaktır. Varlıkların bir çoğu meydana çıkarılmış ve normalleştirme işlemi boyunca arıtılmış olacaktır.
Eğer nesneye dayalı programlamaya aşina iseniz, çoğu mantıksal yüksek seviye varlıkları nesne modeli
içindeki nesnelere benzetebilirsiniz.
Çok basit bir örnek ele alalım. (satışlar örneği) Bu örneğe başlarken değişik çeşitlerin
mümkünlüğünden veya hangi sütunlara sahip olacağımızdan endişe duymayız. Fakat, sistemimizin temel
varlıklarını belirtmekten endişe duyabiliriz.
İlk olarak en temel işlemi düşünelim. Yapmak istediğimiz şey, işlem boyunca tutmak
isteyeceğimiz veri için her bir atomik üniteye bir varlık oluşturmaktır.
Eğer varlığınızın nasıl olmasını istediğinize dair düşünceleriniz varsa, bir sonraki gideceğiniz
adım, sütunlara başlama ve bir primary key’i hesaplamaktır.
Sütun listenizi incelerseniz key adayları ortaya çıkar. Key adayları listesi, varlığınız içindeki her
bir satırı kendine has olarak belirtecek potansiyele sahip sütunları içermelidir. (Bazı durumlarda hiçbir
key adayı bulamıyabilirsiniz ve bir tane bulma ihtiyacı hissedersiniz)
İçinde OrderNo, CustomerNo, CustomerName, CustomerAddress, CustomerCity, CustomerState,
CustomerZip, OrderDate, ItemsOrdered, Total sütunları bulunan bir tablomuz var.
Mantıksal olarak sipariş numarası orders tablosundaki bütün kayıtlar için ayrı olmalıdır. Bu
yüzden OrderNo’yu primary key olarak seçebiliriz.
Böylece temel bir varlık oluşturulmuş oldu. Şimdi normalleşme işlemlerini inceleyelim:
 
1.    NORMAL FORM
Birinci Normal Form (1NF)’un tamamı, tekrarlayan veri gruplarının elimine edilmesi ve
atomic’liğin (veri kendini içerir ve bağımsızdır) teminat altına alınmasından ibarettir. Yüksek seviyede
primary key oluşturulur, ardından, tekrarlayan veri gruplarının yeni tablolara taşınması ve yeni tablolar
için primary key’lerin oluşturulması şeklinde devam eder. (Ayrıca biz, her bir veriyi ayrı satırlara
birleştiren her hangi bir sütundan kaçınırız.)
Daha geleneksel düz dosya tasarımlarında veri tekrarlanması olağandır. (bir sütunda birden çok
bilgi gibi). Bu, şu yönlerleden şüpheliydi:
         O zamanlar disk depolama son derece pahalıydı. Çok defa veriyi saklamak işe yaramayan
alan anlamına gelir. Veri saklama şimdilerde daha ucuzladı ve eskisi gibi büyük sorun
olmaktan çıktı.
         Tekrarlayan veri, daha çok veri hareketi ve daha büyük I/O hesabı anlamına gelir. Bu da veri
yolundan veya ağdan geniş veri bloğunun geçmesi gerekmesi gibi performansı düşürür. Bu,
bugünün hızlı teknolojisiyle bile performanstaki esas negatif etkiyi yapar.
         Tekrarlanmış olması gereken satırlar arasındaki veri, ekseriyetle, çelişkili gibi görünen
verinin herhangi bir şeyini ve veri bütünlüğünün genel ihtiyacını karşılamayı kabul etmez.
         Birleştirilmiş veri içeren bir sütundan bilgi sorgulamak isterseniz ilk olarak o sütundaki
veriyi çözümlemelisinizdir. (Bu da hız kaybı demektir)
 
 
 
Order Order Customer Customer Customer ItemsOrdered
No Date No Name Address
100 1/1/1999 54545 ACME Co 1234 1st 1A4536, Flange,
St. 7lbs, $75; 4-
OR2400, Injector, .
5lbs, $108; 4-
OR2403, Injector, .
5lbs, $116; 1-4I5436,
Head, 63lbs, $750
101 1/1/1999 12000 Sneed 555 Main 1-3X9567, Pump,
Corp. Ave. 5lbs, $62.50
102 1/1/1999 66651 ZZZ  Co. 4242 SW 7-8G9200; Fan, 3lbs,
2nd $84; 1-8G5437, Fan,
3lbs, $15; 1-3H6250,
Control, 5lbs, $32
103 1/2/1999 54545 ACME Co 1234 1st 40-8G9200, Fan,
St. 3lbs, $480; 1-
2P5523, Housing,
1lbs, $165; 1-
3X9567, Pump, 5lbs,
$42

Eğer tablonuzu normalleştirecekseniz, bu tabloda ilgileneceğiniz belli sayıda konu vardır.


Fonksiyonel bir primary key’iniz oldukça, 1. Normal Form’un ana alanlarıyla probleminiz olur:
         Tekrarlayan veri gruplarınız varsa (customer bilgileri) bunları başka tablolara bölmelisiniz.
         ItemsOrdered sütunu atomic yapıda veri içermiyorsa (sadece bir sütun olmasına rağmen
birden çok değer içeriyorsa) her bir veriyi ayrı olarak saklamalısınız.
İşe, birkaç sütunu tablodan çıkarıp kendi tablosuna koyarak başlayabiliriz.:
Order Order Customer ItemsOrdered
No Date No
100 1/1/1999 54545 1A4536, Flange, 7lbs, $75; 4-OR2400, Injector, .
5lbs, $108; 4-OR2403, Injector, .5lbs, $116; 1-
4I5436, Head, 63lbs, $750
101 1/1/1999 12000 1-3X9567, Pump, 5lbs, $62.50
102 1/1/1999 66651 7-8G9200; Fan, 3lbs, $84; 1-8G5437, Fan, 3lbs,
$15; 1-3H6250, Control, 5lbs, $32
103 1/2/1999 54545 40-8G9200, Fan, 3lbs, $480; 1-2P5523, Housing,
1lbs, $165; 1-3X9567, Pump, 5lbs, $42

 
Customer No Customer Name Customer Address
54545 ACME Co 1234 1st St.
12000 Sneed Corp. 555 Main Ave.
66651 ZZZ  Co. 4242 SW 2nd

Yeni ve eski tabloların her ikisinde de dikkat etmeniz gereken bir-iki husus var:
         Her bir satırın kendine has olduğundan emin olmanız için yeni tabloda bir primary key’iniz
olmalıdır. Customers tablosu için 2 primar key adayı vardır: CustomerNo ve CustomerName.
CustomerNo, gerçekten bu amaca hizmet etmek için oluşturulmuştur ve mantıksal tercih
olarak gözükür. CustomerName ise aynı isimde birden çok müşteri olabileceğinden
adaylıktan düşürülür.
         Orders tablosundan verileri çıkartmanıza rağmen yeni Customers tablosundaki verilerle
bağını devam ettirmelisiniz. Aksi halde, bir önceki siparişi kimin verdiğini
ilişkilendiremezsiniz. (Orders tablosunda CustomerNo’nun foreign key gözükmesinden)
         Örneğin ACME CO’nun bilgilerini elimine edebiliyordunuz. Bu, tekrarlayan gruplarda (bir
defa depolamak için) veri hareketinin amacının bir parçasıdır. Bu size daha çok yer depolar
ve değerlerin çakışmasını önler.
         Siz sadece tekrarlayan veri gruplarını taşıdınız. Dikkat ederseniz aynı sipariş tarihi çok defa
tekrarlanmasına rağmen gruplandırılmayacaktır. (Tablonun dışında ilgisi bulunmayan veri
kısmı görecelidir)
Şimdi, 1. Normal Formu bozan ikinci ihlali –atomicity- kaldırabilirsiniz. ItemsOrdered sütununda
birkaç değişik veri vardır. Part Number, Weight ve Price, her biri ayrı tutulduğunda atomic parçalardır,
fakat biçimsiz bir grupta toplanmışlardır. Her birini parçalara ayırmalısınız.
Order Order Customer Part No Description Qty Unit Total Wt
No Date No Price Price
100 1/1/1999 54545 1A4536 Flange 5 15 75 6
100 1/1/199 54545 OR2400 Injector 4 27 108 .5
100 1/1/1999 54545 OR2403 Injector 4 29 116 .5
100 1/1/1999 54545 4I5436 Head 1 750 750 63
101 1/1/1999 12000 3X9567 Pump 1 62.50 62.50 5
102 1/1/1999 66651 8G9200 Fan 7 12 84 3
102 1/1/1999 66651 8G5437 Fan 1 15 15 3
102 1/1/1999 66651 3H6250 Control 1 32 32 5
103 1/2/1999 54545 8G9200 Fan 40 12 480 3
103 1/2/1999 54545 2P5523 Housing 1 165 165 1
103 1/2/1999 54545 3X9567 Pump 1 42 42 5

Burada ortaya çıkan problem ise her bir bilgiyi parçalara ayırdığınızda artık primary key’in her
satır için farklı olamadığıdır. Bu yüzden, tabloya line item numarası ekleyerek satırları kendine özgü
biçimde belirtebilirsiniz. (OrderNo ile LineItem ikisi beraber primary key oluşturuyorlar)
Order Line Order Customer Part No Description Qty Unit Total Wt
No Item Date No Price Price
100 1 1/1/1999 54545 1A4536 Flange 5 15 75 6
100 2 1/1/1999 54545 OR2400 Injector 4 27 108 .5
100 3 1/1/1999 54545 OR2403 Injector 4 29 116 .5
100 4 1/1/1999 54545 4I5436 Head 1 750 750 63
101 1 1/1/1999 12000 3X9567 Pump 1 62.50 62.50 5
102 1 1/1/1999 66651 8G9200 Fan 7 12 84 3
102 2 1/1/1999 66651 8G5437 Fan 1 15 15 3
102 3 1/1/1999 66651 3H6250 Control 1 32 32 5
103 1 1/2/1999 54545 8G9200 Fan 40 12 480 3
103 2 1/2/1999 54545 2P5523 Housing 1 165 165 1
103 3 1/2/1999 54545 3X9567 Pump 1 42 42 5

Böylece 1. Normal Form Kriterleri tamamlanmıştır. (Artık tekrarlayan gruplarınız yoktur ve


bütün sütunlar atomic yapıdadır)
 
2.    NORMAL FORM
Bu form, veri tekrarlarının (grup olmasına gerek yok) üzerine düşmeyi azaltıyor. 2 kuralı vardır:
         Tablo, 1. Normal Formun kurallarına uygun olmalıdır
         Her bir sütun tam (whole) key’e bağımlı olmalıdır.
Örnekte bu alan için bir problem vardır: Order tablosuna dikkat ederseniz, bütün sütunlar whole
key’e bağımlı değildir. Key’in bir parçasına ihtiyaç duyanlar vardır.
Sadece OrderNo sütununa bağlı 2 sütun vardır. OrderDate ve CustomerNo sütunlarının her ikisi
de line item sütunundan bağımsızdırlar. Bu yüzden başlık ile ayrıntı tablosu kavramlarını
incelemelisinizdir.
Bazen pratikte bir varlığın 2 tabloya bölünmesi gerekir ve böylece 2 varlık oluşturulur. Başlık,
biraz, ilişkilerdeki 2 tablonun parent tablosuna benzer. Sadece bir defa ihtiyaç duyulacak bilgileri saklar.
Ayrıntı tablosu ise birden çok örnekte içerilebilen bilgileri tutar. Başlık genellikle orijinal tablonun adını
alır. Ayrıntı tablosu ise başlık tablo adı ve ayrıntı tablosu olduğunu belirten eklerin olduğu bir isim alır.
(OrderDeatils gibi) Her bir başlık kaydı için çoğunlukla bir çok (en az bir tane) ayrıntı kaydınız olur.
Tabloyu tekrar parçalarken dikkatli olmanız gerekir. Sütunların çoğunu details tablosu tuttuğu
için artık bu tabloyla ilgilenmeniz gerekecek.
Order Line Part No Description Qty Unit Total Wt
No Item Price Price
100 1 1A4536 Flange 5 15 75 6
100 2 OR2400 Injector 4 27 108 .5
100 3 OR2403 Injector 4 29 116 .5
100 4 4I5436 Head 1 750 750 63
101 1 3X9567 Pump 1 62.50 62.50 5
102 1 8G9200 Fan 7 12 84 3
102 2 8G5437 Fan 1 15 15 3
102 3 3H6250 Control 1 32 32 5
103 1 8G9200 Fan 40 12 480 3
103 2 2P5523 Housing 1 165 165 1
103 3 3X9567 Pump 1 42 42 5

Bundan sonra, geri kalan sütunları başlık tablosu olarak hizmet edecek yeni bir tabloya
aktarmalısınız.
Order No Order Date Customer No
100 1/1/1999 54545
101 1/1/1999 12000
102 1/1/1999 66651
103 1/2/1999 54545

Böylece 2. Normal Form tamamlanmıştır. (Sütunların hepsi bütün key’e bağımlıdır)


 
3.    NORMAL FORM
Bu form satırın sonuna bağlıdır. Teknik olarak bundan sonra gelecek normalleştirme seviyeleri
olmasına rağmen bunların hiçbiri akademik dairenin dışında fazla ilgi görmez.
Normal Form, tablomuzdaki bütün sütunların tamamı ile  bazı şeylere bağımlı olmaması
konusuyla ilgilidir. 3 kuralı vardır:
         Tablo, 2NF biçiminde olmalıdır.
         Hiçbir sütun, diğer key olmayan sütunların hiçbiriyle bağımlı olmamalıdır.
         Çıkarılan veri olmamalıdır.
Tabloda, primar key olmayanlara bağımlı herhangi bir sütun olup olmadığı araştırılır. Örnek
tabloda, tablonun primary key’inden daha çok part numarasına bağımlı olan 2 sütun (Description ve
Weight) vardır. Bu yüzden tekrar ayrı tablolara ayrıştırılır.
İlk olarak Part bilgilerini tutmak için Products adında yeni bir tablo oluşturmalısınız. Bu yeni
tablo, OrderDetails’teki OrderID ve LineItem’dan daha çok PartNo’ya bağımlı olan bilgileri tutar.
Part No Description Wt
1A4536 Flange 6
OR2400 Injector .5
OR2403 Injector .5
4I5436 Head 63
3X9567 Pump 5
8G9200 Fan 3
8G5437 Fan 3
3H6250 Control 5
8G9200 Fan 3
2P5523 Housing 1
3X9567 Pump 5

Ardından da OrderDetails tablosunun dışındaki foreign key hariç hepsini parçalayabilirsiniz.


 
Order No Line Item Part No Qty Unit Price Total Price
100 1 1A4536 5 15 75
100 2 OR2400 4 27 108
100 3 OR2403 4 29 116
100 4 4I5436 1 750 750
101 1 3X9567 1 62.50 62.50
102 1 8G9200 7 12 84
102 2 8G5437 1 15 15
102 3 3H6250 1 32 32
103 1 8G9200 40 12 480
103 2 2P5523 1 165 165
103 3 3X9567 1 42 42

Bu arada 1. problem (cross-column dependency) oluşur, fakat, çıkarılan veriyle ilgilenilmez.


Qty*UnitPrice ın çarpımından çıkarılabilen veriyi içeren TotalPrice adında bir sütun oluşturuldu. Bu,
normalleştirmede yasak şeydir. Bu yüzden 3. Normal Form’a erişmek için TotalPrice’ı kaldırmalısınız ve
gerekirse hesaplattırmalısınız.
 
DİĞER NORMAL FORMLAR
Normalleştirme modelinin bir parçası olarak akademisyenler tarafından kabul edilen birkaç
değişik form daha vardır. Bunlar:
Boyce-Codd (3. Normal Formun bir çeşidi olarak kabul edilir): Bu, birden çok çakışan aday
key’lerin yerlerini adreslemeyi dener. Tipik olarak herhangi bir sayıda çözümün çalıştığı yerdir ve
nerdeyse akademik bağlantı dışında mantıksal bir amacı yoktur.
4.Normal Form: Bu çeşit, çok değerli bağımlılıklarla çevrelenmiş konularla ilgilenir.
5.Normal Form: Kayıp olan ve olmayan analizlerle ilgilidir.
6.Normal Form (Domain-Key Normal Form): Bu, sadece değişiklik anormalliği ihtimalinin
elimine edildiğinde meydana getirilir. Sanaldır, gerçek dünyada imkansızdır.

OPERATÖRLERİ KULLANMAK
İLİŞKİSEL ve BOOLEAN OPERATÖRLER
İlişkisel Operatörler daha yaygın olarak matematiksel ilişkiler olarak bilinir. Bu operatörler
matematikteki anlamına benzer anlam taşırlar. (= eşittir, > büyüktür, < küçüktür, >= büyük eşittir, <=
küçük eşittir, <> eşit değildir)
Bu operatörler sayılarla kullanılabileceği gibi diğer değerlerle de kullanılabilir. Tam anlamları,
veritabanınızın kullandığı kodun türüne (ASCII veya EBCDIC) bağlıdır. Kodda her bir karakterin sayısal
değeri vardır (ASCII’de ‘D’ 68’tir). İlişkisel operatörleri karakterlerle kullandığınızda aslında onların kod
değerlerini karşılaştırmış olursunuz.
Bu operatörleri kullanmak çok basittir (WHERE contract=1 gibi)
Boolean operatörler ise mantıktaki anlamlarıyla kullanılmaktadır (True AND False = False, True
OR False = True, NOT True = False)
Boolean operatörler kullanılarak WHERE’de birden çok şart koşulabilir (SELECT * FROM
address WHERE city=’Louisvelle’ AND NOT state=’KY’ gibi)
 
EXISTS
Bir grup sütunun veritabanınızda var olup olmadığını öğrenmek istiyorsanız EXISTS anahtar
kelimesini kullanabilirsiniz. Örneğin SQL Server’ın Pubs veritabanında yazısı olan yazarları bulmak
istiyorsanız şu ifadeyi kullanabilirsiniz:

SELECT * FROM authors WHERE EXISTS (SELECT * FROM titles INNER JOIN
titleauthor ON titles.title_ID = titleauthor.title_ID WHERE titleauthor.au_ID
= authors.au_ID)

EXISTS, basit şekliyle sorgu içinde sorgu diye ifade edebileceğimiz alt sorgulamayı kullanmayı
gerektirir. Alt sorgular EXISTS kelimesinden sonra parantezler içinde yazılır. Ana sorguyu alt sorguya
bağlamak için ana sorgudaki bazı değerlerin alt sorgudaki WHERE yapısında gözükmesi gerekir. Eğer iki
sorgulama arasında şartlı ilişki (WHERE titleauthor.au_ID = authors.au_ID) yoksa, alt sorgulama (her
zaman) bütün satırları döndürür.

Başka bir örnek incelersek:


SELECT * FROM authors WHERE EXISTS (SELECT * FROM titles INNER JOIN
titleauthor ON titles.title_ID = titleauthor.title_ID WHERE NOT
titleauthor.au_ID = authors.au_ID)

Burada bütün yazarlar döner. Alt sorgu her zaman ana sorguda verilen yazarların her biri için
sonuç döndürür. Çünkü ana sorgudaki yürürlükte olmayan yazarların bir listesini döndürür. Sonuç olarak
ana sorguda verilen her yazar için alt sorguda satırlar olacaktır
EXISTS alt sorgusu, sonuç kümesine satırları seçmek için temel olarak bir grup satır kullanır.
Diğer operatörler benzer biçimdeki diğer satırları seçmek için satır gruplarıyla çalışmaya izin verir. Bu
seçeneklerin her biriyle hatırlamanız gerekir ki bir satırlar grubu buluyorsunuz ve diğer bir satır grubunu
seçmek için birkaç şekilde o satır grubunda çalışıyorsunuz. Bu araçlar sorguları uygun şekilde sınırlamak
için çok kullanışlı olabilmektedir.
 
BETWEEN

Between, kayıt kümesine satırları döndürmek için kullanılacak bir sütun değerleri grubu oluşturmanıza
izin verir. Temel yapısı şöyledir:
SELECT title, ytd_sales FROM titles WHERE ytd_sales BETWEEN 1000 and
7000

Between, sütun değeri verilen aralıktaki satırları seçmenizi sağlar. Buradaki and, Boolean
operatör değildir.
 
IN, ANY, ALL, SOME
IN sözcüğü, kayıt kümesi üretimini başka bir yoldan kontrol etmek için alt sorgu kullanmanıza
izin verir. IN, değerler listesinden bir sütun değeri arar. Yapı şöyledir:

SELECT au_lname, au_fname FROM authors WHERE au_id IN (SELECT au_id FROM
title_author WHERE royaltypes = 10)
Bu örnekte WHERE yapısı, au_id’nin, alt sorguyla oluşturulan listede bulunması gerektiğini
belirtiyor.
Tip: IN, alt sorgu kullanmayı gerektirmez. Parantez içinde virgüllerle ayrılmış bir değerler
kümesi de kullanabilirsiniz.
ALL, ana sorgudaki satırın, belirtilen yolla alt sorgudaki bütün sonuçlarla eşleşmesini gerektirir.
Aşağıdaki örneği inceleyelim:

SELECT au_lname, city FROM authors WHERE city <> ALL (SELECT city FROM
publishers)

Ana sorguda authors tablosunu çekiyoruz. Alt sorguda da publishers tablosunu çekiyoruz.
WHERE ifadesi, ana sorgudaki city sütununa karşı alt sorgudaki city sütununu kontrol eder. İki sütunun
eşit olmaması gerekir (<> kullanıldığı için). Böylece, publishers tablosunda bulunmayan şehirlerin olduğu
authors kayıtlarının au_lname ve city sütunları döndürülmüş oluyor.
Eğer bunun tam tersini (publishers tablosunda bulunan şehirlerin olduğu authors kayıtlarını
çekmek) isterseniz ANY’yi kullanabilirsiniz.

SELECT au_lname, city FROM authors WHERE city = ANY (SELECT city FROM
publishers)

Not: ANY ile SOME operatörleri aynı fonksiyonu icra ederler. (Aynı şeyi ifade ederler)
 
LIKE ve ISNULL

LIKE anahtar sözcüğü size, sütun için belirttiğiniz örüntüdeki satırları seçmenizi sağlar. Basit bir örnek
verirsek:
SELECT * FROM authors WHERE phone LIKE ‘502%’

Burada authors tablosunun telefon numarasının ilk 3 rakamı 502 olan ve ondan sonra herhangi bir
rakam gelen kayıtlar çekilir.
Bu konuyu UPDATE konusu içinde işlediğimiz için bu kadarla yetineceğiz.

SQL size, veritabanından veri çekildiğinde uygun olman NULL karakterleri kontrol etmeniz için bir
yöntem sağlar. ISNULL fonksiyonu NULL karakterleri kontrol eder ve sütun değeri NULL olduğunda
belirtilen değeri döndürür. Yapı şöyledir:
SELECT ISNULL(price, $0.00) FROM titles
Bu örnekte titles tablosundaki fiyat bilgilerini çekiyoruz. Ve gösterirken eğer bir NULL değere
rastlarsak onu $0.00 olarak gösteriyoruz. (NULL değilse kendi değeri)

ISNULL fonksiyonu aşağıdaki örnekte verilen IS NULL (ve IS NOT NULL) ile karıştırılmamalıdır.
SELECT price FROM titles WHERE price IS NOT NULL

Burada sadece NULL olmayan değerler döndürülür.


 
 

FONKSİYONLARI KULLANMAK
SQL, bir dil olarak diğer programlama dilleri gibi fonksiyonları destekler. Fonksiyonlar, yaygın
işlemleri icra etmek için kullanışlı rutinlerdir. Önceden belirtilmişlerdir. Temel olarak, tekrar
kullanmamız için başka birileri tarafından yazılmış kayıtlı prosedürlerdir.
Burada programcıların sık sık kullanacağı fonksiyonları inceleyeceğiz (her fonksiyon sonunda
birer örnek vereceğiz):
 
AGGREGATE FONKSİYONLAR
Aggregating, veriyi özetlemenin bir yoludur. Aggregate fonksiyonların kullanım amacı sonuçları
inceleyenlerin, gidişi hızlı bir şekilde anlamasını sağlamaktır. Bu fonksiyonları aritmetik operatörlerle
beraber kullanıp birden çok sütunun sonucunu da özetleyebilirsiniz. Bu fonksiyonlarda genel yapı
Agg_Fonk_İsmi (sql_deyimi) şeklindedir. Fonksiyonlara kısaca bir göz atalım:
AVG: AVG veya GROUP BY ile belirtilmiş bir gruptaki değerlerin ortalamasını döndürür.

sql_deyimi tipik olarak bir sütun ismi olabileceği gibi daha karışık deyimler de olabilir. ALL veya
DISTINCT deyimi değiştirmek için kullanılabilir. Döndürülen değer sql_deyimi veri tipiyle aynıdır.
SELECT AVG (DISTINCT price) FROM titles

COUNT: COUNT veya GROUP BY ile belirtilmiş gruptaki eleman sayısını döndürür. Döndürülen değer
COUNT’u ifade eden bir integer’dır.
SELECT COUNT (city) FROM authors

MAX: Deyimle ilişkilendirilmiş değerlerden en büyüğünü döndürür.

sql_deyiminde sütun isminden başka aritmetik ifadeler de kullanılabilir. ALL veya DISTINCT
kullanılabilir. DISTINCT’in fonksiyonun genel durumunda bir anlamı yoktur. Verilen ifade için sadece
bir tane max değer vardır. ALL, deyimin bütün değerlerine fonksiyonun uygulanmasını zorlar. Bu,
işlemin varsayılan biçimidir.
SELECT MAX (ytd_sales) FROM titles

MIN: Deyimle ilişkilendirilmiş olan en küçük değeri döndürür. Kullanılan parametre MAX ile aynı
özelliğe sahiptir.
SELECT MIN (ytd_sales) FROM titles

SUM: Deyimle işaret edilen elemanların toplamını döndürür.

sql_deyimi sütun ismi olabileceği gibi diğer tipteki deyimler de olabilir. sql_deyimi sayısal bir veriyi
göstermelidir ve NULL değerler atılmıştır. DISTINCT birden çok kullanılan değerleri sadece bir defa ele
almak için kullanılabilir.
SELECT SUM (advance) FROM titles

STDEV: Deyimle ilişkilendirilmiş bütün değerlerin örneğinin standart sapmasını döndürür.

Sql_deyimi tipik olarak bir sütun ismidir ve sayısal bir veri tipini göstermek zorundadır. Aggregate
fonksiyonlar deyimde kullanılamazlar. NULL değerler atılmıştır.
SELECT STDEV (ytd_sales) FROM titles

STDEVP: Deyimle ilişkilendirilmiş bütün değerlerin popülasyonunun standart sapmasını


döndürür. Kullanımı STDEV ile aynıdır.

VAR: Deyimle belirtilen veri elemanlarının bir örneğinin varyansını döndürür.


SELECT VAR (ytd_sales) FROM titles

VARP: Deyimle belirtilen veri elemanlarının bir popülasyonunun varyansını döndürür.


 
TARİH VE ZAMANIN TUTULMASI
Verinin özetlenmesinin raporlanması için genellikle tarih ve saate ihtiyaç duyulur. SQL size, tarih
eklemek, çıkarmak, tarih stringinin bir kısmını almak ve içinde bulunulan tarihi almak için özel
fonksiyonlar sunar:
DATEADD: Tarih değerleriyle matematiksel işlemler (toplama ve çıkarma) yapmanızı sağlar.
Yapısı şöyledir: DATEADD(Part_of_date_code, Number, sql_date)
Part_of_date_code, matematiksel işlem yapmak istediğimiz tarihin kısmının tek tırnak içinde
belirtilmemiş bir grup karakter karşılığıdır. Geçerli değerler şunlardır: (yıl-yy,yyyy. çeyrek-qq,q. ay-
mm,m. yılın günü-dy,y. gün-dd,d. haftanın günü-Dw. hafta-wk, ww. saat-Hh. dakika-min. saniye-ss,
milisaniye-Ms.)
Number, tarih kısmına eklemek istediğiniz sayıdır. Number, negatif değer alarak çıkarma işlemi
de yapabilir.

sql_date de matematik işlemlerini uygulayacağınız geçerli bir tarih ifadesidir.


DATEADD (d, 21, Posting_Date)

DATEDIFF: Belirtilen iki tarih arasındaki tarih ünitelerinin sayısını döndürür. Yapısı şu
şekildedir: DATEDIFF (Part_of_date_code, sql_startingdate, sql_endingdate)

Sql_startingdate ile sql_endingdate parametreleri sırayla sorgulayacağınız başlangıç ve bitiş tarihlerini


ifade eder.
DATEDIFF (d, Posting_Date, getdate())

DATENAME: Belirtilen tarih kısmını karakter stringi olarak döndürür. Yapısı şöyledir:
DATENAME (Part_of_date_code, sql_date)

DATENAME (Dw, ‘12/29/1981’)

Bu ifade sonuç olarak Tuesday döndürür.


DATEPART: Belirtilen tarih kısmını integer olarak döndürür. Yapı şöyledir: DATEPART
(Part_of_date_code, sql_date)

DAY: Tarih ifadesi ile ilişkilendirilmiş günü integer olarak döndürür. Yapı şu şekildedir: DAY (sql_date)
DAY (‘12/27/1954’)

GETDATE: Fonksiyonun çalıştığı sistemdeki tarih ve zamanı döndürür. (Çoğu zaman


server’daki tarih ve zaman döndürülür) Yapısı: GETDATE() şeklindedir. Parametre almaz.
MONTH: Tarih ifadesi ile ilişkilendirilmiş ay’ı integer olarak döndürür. Yapı MONTH
(sql_date) biçimindedir.
YEAR: Tarih ifadesi ile ilişkilendirilmiş yıl’ı integer olarak döndürür. Yapı YEAR (sql_date)
şeklindedir.
 
MATEMATİK FONKSİYONLARI
SQL’in matematik fonksiyonları ile aritmetiğin de ötesinde matematik fonksiyonlarını icra
edebilirsiniz. Bunlar, eğer bilgileri açılarla veya sayısal değerlerle tutuyorsanız veya üslerle çalışıyorsanız
çok kullanışlıdırlar.
ABS: Mutlak değer diye bilinen, sayının pozitif biçimini döndürür. Yapısı: ABS(sql_number)
biçimindedir. Sql_number geçerli bir sayı ifadesidir.

ABS (5+12)

ACOS: Cosinüs’ü, bildirilen argüman olan açının raydan değerini döndürür. Yapı: ACOS
(sql_float_expression) şeklindedir. Sql_float_expression, kayan noktalı, geçerli bir sayı ifadesidir.
ACOS (-1.00)

ASIN: Sinüs’ü, bildirilen argüman olan açının raydan değerini döndürür. Yapı: ASIN
(sql_float_expression) şeklindedir.
ATAN: Tanjant’ı, bildirilen argüman olan açının raydan değerini döndürür. Yapı: ATAN
(sql_float_expression) şeklindedir.

ATN2: Tanjantı, argüman olarak verilen 2 değerin arasındaki açının raydan değerini döndürür. Yapı
şöyledir: ATN2 (sql_float_expression_low, sql_float_expression_high)
ATN2 (35.1, 128.01)

CEILING: Argüman olarak bildirilen sayısal değerden yüksek en küçük integer değeri döndürür. Yapı:
CEILING (sql_numeric_expression)
CEILING (10.95)

COS: Argüman olarak verilen açının cosinüs’ünü döndürür. Argüman raydan cinsinden verilmiş
olmalıdır. Yapı: COS (sql_float_expression)
COT: Girilen açının cotanjant’ını döndürür.
DEGREES: Argüman olarak raydan cinsinden verilmiş açının derece cinsinden değerini
döndürür. Yapı: DEGREES (sql_numeric_expression)
EXP: Verilen ifadeyi e tabanına üs alarak sonucu döndürür. Yapı: EXP (sql_float_expression)
FLOOR: Argüman olarak verilen sayısal ifadeye eşit veya ondan küçük en büyük integer değeri
döndürür. Yapı: FLOOR (sql_numeric_expression)
LOG: Girilen sayının doğal logaritmasını alır. Yapı: LOG (sql_floating_point_expression)
LOG10: Verilen ifadenin 10 tabanındaki logaritmasını alır. Yapı: LOG10
(sql_floating_point_expression) şeklindedir.
PI: PI değerini döndürür. Yapı PI () şeklindedir.
POWER: İlk argümana ikinci argümanı üs olarak alarak sonucu döndürür. Yapı: POWER
(sql_numeric_expression, power)
RADIANS: Argüman olarak sağlanan, derece cinsinden açıyı raydan cinsine çevirir. Yapı:
RADIANS (sql_numeric_expression)
RAND: 0 ile 1 arasında rastgele kayan noktalı sayı döndürür. Yapı: RAND (sql_integer_seed)
şeklindedir. Sql_integer_seed, rastgele sayı algoritmasını başlatmak için integer türünde bir sayıdır.
(RAND (5) gibi)
ROUND: İlk argüman olarak verilen sayıyı belirtilen haneden sonra yuvarlar veya atar. Yapı:
ROUND (sql_number_to_round, sql_precision [, sql_truncate]) şeklindedir. Sql_number_to_round, bit
veri tipinde olmayan herhangi bir sayı ifadesidir. Sql_precision, virgülden sonra kaç hanenin
yuvarlanacağını bildiren integer değerdir. Örneğin ROUND(745.045, 2) ifadesi 745.05 döndürür. Eğer
sql_precision negatifse virgülden öncesini yuvarlar. Örneğin ROUND (745.045, -2), 700 sonucunu
döndürür. Sql_truncate de sayıyı yuvarlamadan kesmeye yarar.
SIGN: Sayı pozitifse 1, diğer durumlarda -1 döndürür. Yapı: SIGN (sql_numeric_expression)
şeklindedir.
SIN: Raydan cinsinden verilmiş açının sinüsünü döndürür. Yapı: SIN
(sql_floating_point_expression) şeklindedir.
SQUARE: Girilen argümanın karesini alır (kendisiyle çarpar). Yapı: SQUARE
(sql_floating_point_expression) şeklindedir.
SQRT: Argümanın karekökünü alır. Yapı: SQRT (sql_floating_point_expression)
TAN: Argüman ifadesinin tanjantını alır. Yapı: TAN (sql_floating_point_expression)
 
STRING FONKSİYONLARI
Veri genellikle karakterler stringi şeklinde saklanır ve sizin stringler hakkında bilgi edinmeniz
gerekir. SQL’in string fonksiyonları, stringin bir kısmını almanıza, boşlukları silmenize ve string
hakkında bilgi edinmenize imkan tanır.

ASCII: String ifadesinin en sol karakteri diye anılan ilk karakteriyle ilişkili ASCII kodu döndürür. Yapı:
ASCII (string_expression) şeklindedir.
ASCII (‘Le Monde’)

CHAR: Argüman olarak verilmiş ASCII kodla ilişkilendirilmiş karakteri döndürür. Yapı: CHAR
(sql_integer) şeklindedir.

CHARINDEX: Aranacak olan string içinde, belirtilen string parçasının başlangıç yerini döndürür. Yapı:
CHARINDEX (string_to_find, string_to_search [, starting_position]) şeklindedir. String_to_find, bulmak
istediğiniz; string_to_search, içinde arayacağınız geçerli string veri tipindeki ifadelerdir. Starting_position
ise arama işlemine kaçıncı karakterden başlanacağını belirten integer’dır. (Belirtilmediği takdirde 1 olarak
alınır)
CHARINDEX (‘find me’, ‘look here to find me’)

DIFFERENCE: 2 stringin SOUNDEX (ses benzeşmesi) farkını döndürür. DIFFERENCE fonksiyonunu


2 stringin birbirine telaffuz bakımından ne kadar benzediğini belirlemek için kullanabilirsiniz. (4 sonucu,
2 stringin birbirine çok benzediğini, 0 ise hiç benzemediğini belirtir) Yapı: DIFFERENCE
(string_expression1, string_expression2) şeklindedir.
DIFFERENCE (‘Jane’, ‘Jayne’)

LEFT: İlk argümanla belirtilen stringin soldan itibaren ikinci argümanla bildirilen sayı kadar
karakterini döndürür. Yapı: LEFT (sql_string_expression, sql_integer_expression) şeklindedir.
LEN: Argüman olarak girilen stringin karakter sayısını döndürür. Yapı: LEN
(sql_string_expression) şeklindedir.
LOWER: Argüman olarak belirtilen stringi, bütün karakterleri küçük karakter olacak şekilde
döndürür. Yapı: LOWER (sql_string_expression) şeklindedir.
LTRIM: Argüman olarak bildirilen stringi, başındaki boşluklar silinmiş şekliyle döndürür. Yapı:
LTRIM (sql_string_expression) şeklindedir.
NCHAR: Argüman olarak belirtilen kodun Unicode karakter karşılığını döndürür. Yapı: NCHAR
(sql_integer_expression) şeklindedir.

PATINDEX: İlk parametre olarak girilen örüntüyü ikinci parametre olarak girilen stringte arar ve
bulduğu zaman ilk karakterin stringteki kaçıncı karakter olduğunu döndürür. Yapı: PATINDEX
(‘%sql_pattern%’, sql_string_expression) şeklindedir. Sql_pattern, wildcard içerebilen bir dizi
karakterdir. İlk % karakteri örüntünün ondan sonra gelen karakter ile başlanacağını, sondaki % karakteri
de ondan önce gelen karakter ile bitirileceğini belirtir.
PATINDEX (‘%My%’, ‘My string’)

QUOTENAME: Unicode string alır ve stringin başına ve sonuna tırnak işareti (ya da sizin belirteceğiniz
işareti) koyar. Yapı: QUOTENAME (‘sql_character_string’ [, ‘sql_quote_character’])
QUOTENAME (‘column_1’, ‘/’)

REPLACE: İlk argüman olarak verilen stringte ikinci argüman olarak verilen stringi bulur ve
üçüncü argüman olarak verilen stringle değiştirir. Yapı: REPLACE (sql_string_to_search,
sql_string_to_find, sql_string_to_replace_with) şeklindedir.

REPLACE (‘My string’, ‘My’, ‘Our’)

REPLICATE: İlk argüman olarak verilen stringi, ikinci argüman olarak verilen sayı kadar
tekrarlar. Yapı: REPLICATE (sql_sting_expression, sql_integer_expression)
REVERSE: Argüman olarak girilen karakter stringinin karakterlerini ters sırada (ilk karakter son
karakter olacak şekilde) sıralar. Yapı: REVERSE (sql_string_expression)
RIGHT: İlk argüman olarak girilen stringin sağdan itibaren ikinci argüman olarak bildirilen
sayıda karakterini döndürür. Yapı: RIGHT (sql_sting_expression, sql_integer_expression)
RTRIM: Girilen stringin, sonundaki boşlukları silinmiş şeklini döndürür. Yapı: RTRIM
(sql_string_expression)

SOUNDEX: SOUNDEX, iki stringin ses benzeşmesini hesaplamada kullanılır. Bu fonksiyon, verilen
stringin soundex kod karşılığını döndürür. (Soundex kod, birbirine telaffuz açısından benzeyen iki
kelimede aynıdır. (Jane ile Jayne gibi)) Yapı: SOUNDEX (string_expression) Aşağıdaki örnek S500
döndürür.
SOUNDEX (‘Jane’)

SPACE: Belirtilen sayıda boşluk karakteri döndürür. Yapı: SPACE (sql_integer_expression)


şeklindedir.
STR: Kayan noktalı bir sayıyı string şekline çevirir. Yapı: STR (sql_float_expression [,
sql_length [, sql_decimal]]) şeklindedir. Sql_length, sonuç stringinde olabilecek eleman sayısını
(varsayılan 10 dur), sql_decimal ise noktadan sonraki eleman sayısını belirtir.

STUFF: Girilen stringin belirtilen karakterinden itibaren belirtilen sayıda karakterini siler ve yerine,
girilen diğer bir stringi koyar. Yapısı: STUFF (sql_string_expression_to_modify, sql_integer_start,
sql_integer_length, sql_string_expression_to_insert)
STUFF (‘To be or not to be’, 7, 2, ‘well, maybe’)

Bu örnek ‘To be well, maybe not to be’ stringini döndürür.


SUBSTR: String, text, binary veya image ifadesinin belirtilen karakterinden itibaren belirtilen
sayıda karakterini döndürür. (önceki örnekte ‘or’ u döndürür). Yapı: SUBSTRING (sql_expression,
sql_integer_start, sql_integer_length)
UNICODE: Girilen stringin ilk karakterinin Unicode kod değerini döndürür. Yapı: UNICODE
(string_expression) şeklindedir.
UPPER: Argüman olarak belirtilen stringi, küçük harfleri büyük harfe çevrilmiş şekliyle
döndürür. Yapı: UPPER (sql_string_expression) şeklindedir.
 
KRİTİK SİSTEM FONKSİYONLARI
Sistem fonksiyonları tipik olarak veritabanı sisteminin durumu hakkında bilgi verir. Sistem
fonksiyonları, veri tipini değiştirmeye zorlamada, NULL yerleştirmede, tarih yerleştirmede ve sayıları
belirtmede çok kullanışlıdırlar. En çok kullanılan sistem fonksiyonlarına göz atalım:
CASE: Bu fonksiyon C’de olduğu gibi, koşulların olduğu bir listeye göz atar ve hangi koşul
doğru ise ona bağlı değeri döndürür. Yapı:
CASE sql_expression
WHEN sql_expression_value THEN return_value
ELSE return_value şeklindedir.
Birden çok ifadenin değerlerini kontrol etmek için ise şu yapı kullanılır:
CASE
WHEN sql_Boolean_expression THEN return_value
ELSE return_value.

WHEN ifadesini çok defalar kullanılabilirsiniz.


CASE column 1

WHEN ‘Lincoln’ THEN ‘Issued Emancipation Proclemation’

WHEN ‘King’ THEN ‘Fought for civil rights’

WHEN ‘Kennedy THEN ‘MAnaged the world’s first nuclear conflict’

ELSE ‘You have not chosen a political leader on the list’

CASE

WHEN column1>0 THEN column1*10

WHEN column2>0 THEN column2*10

ELSE 0

CAST: Argüman olarak girilen verinin tipini, belirtilen veri tipine çevirir. Yapı: CAST (sql_expression
AS sql_data_type)
CAST (date_column AS varchar(10))

CONVERT: Verilen ifadeyi belirtilen formatta belirtilen veri tipine döndürür. Yapı: CONVERT
(sql_data_type, sql_expression, sql_format_code) şeklindedir. Sql_data_type, geçerli bir SQL veri tipidir.
Sql_format_code ise veritabanınız tarafından belirtilen bir format kodudur. Yaygın olanları şunlardır:
(1=mm/dd/yy, 101=mm/dd/yyyy, 2=mm.dd.yy, 102=mm.dd.yyyy, 3=dd/mm/yy, 103=dd/mm/yyyy,
4=dd.mm.yy, 104=dd.mm.yyyy)

Varsayılan tarih kodu 0 (veya 100) ise şu formattatır: mon dd yy hh:mmAM (mon dd yyyy hh:mmAM)
CONVERT (datetime, column1, 101)

ISDATE: Eğer ifade, veritabanınız için geçerli bir tarih değeri ise true, aksi halde false döndürür.
Yapı: ISDATE (sql_expression) şeklindedir.
ISNULL: Eğer ifade NULL ise true, değilse false döndürür. Yapı: ISNULL (sql_expression)
ISNUMERIC: Eğer ifade sayı olarak alınabiliyorsa true, aksi halde false döndürür. Yapı:
ISNUMERIC (sql_expression) şeklindedir.

KAYITLI PROSEDÜRLERİ KULLANMAK


Şimdiye kadar, veritabanından veri döndürmek için bir SQL deyimi kullanmak hakkında
konuştuğumuz zaman bir SELECT deyimi yazmaktan bahsederdik. SELECT deyimi bağlantı nesnesine
bağlanmış bir komut nesnesinin içine girer ve bağlantıya ilişkilendirilmiş komutun icrası, kayıt kümesi
nesnesine döndürülür. Daha önce, bu adımları birleştirdiğimiz yolun, veritabanıyla etkileşimimizin
verimini ve hızını nasıl etkilediği konusuna değinmemiştik. Şimdi bu konulara bir göz atalım.
Başlangıç için, tavsiye ettiğimiz etkileşim biçimi tamamen bizim kontrolümüz altındadır. Biz,
kendi istemci makinelerimizde çalışırız. Bağlantı tanımlanmıştır ve bizim istemci makinelerimizde işleme
tabi tutulmuştur. Komut oluşturulmuştur ve istemci makinelerimizde işleme tabi tutulmuştur. Kayıt
kümesi de bizim istemci makinelerimizde işlenmiştir. Bu yüzden bizim istemci bilgisayarımız,
veritabanıyla etkileşimin nasıl olacağını kontrol eder.
Sorgu işleminin kontrolünü istemci bilgisayarda tutmak başka çeşitte hız engellerine sebep
olabilir. İşin çoğu, server’ın CPU’suna göre daha yavaş olan istemcinin CPU’sunda yapılır. Ayrıca
server’lar muhtemelen yapılacak işleri paylaştırmak için birden çok CPU’ya sahiptirler. İstemcinin SQL
derleyicisinden daha verimli olacak maharetli bir derleyiciye sahip olma bakımından, server’da çalışan
veritabanı uygulaması, istemci uygulamasından daha münasiptir. Server sorgu optimizasyon rutinleri
server’da mevcut olan donanım tipiyle direk ilişkilidir ve her zaman istemcinin derleyicisiyle istemci
tarafında gerçekleştirilen herhangi bir optimizasyon tipinden daha verimlidir. Sonuç olarak, muhtemelen
server, sorguları derleme ve icrada herhangi bir istemci bilgisayardan daha verimlidir. Ayrıca, bir server
kayıtlı prosedürü uygularken o prosedür için icra planını saklar (icra planı, sorgunun tam optimize edilmiş
biçimidir). Eğer icra planı cache’den silinmeden önce kayıtlı prosedürü yeniden kullanırsanız, sorguyu
yeniden derlemeye ve optimize etmeye gerek duymazsınız. (Zaten hazırlanmış olan icra planını
kullanırsınız)
Not:Verimlilik için işlemlerin uygulanacağı yer seçimi, istemci uygulaması ve kullandığınız
veritabanına bağlıdır. Bazı istemci geliştirme sistemleri SQL deyimini istemcide derlemeye izin verir.
Diğerleri deyimin derlenmesini ve optimizasyonunu server’da gerçekleştirir. Son sorgu optimizasyonu
her zaman server’da gerçekleşir, fakat bazı istemciler bazı optimizasyonları kendi kendilerine
gerçekleştirebilirler.
Veritabanı performansında ve istemci program performansında mümkün iyileştirmeler için
server’a sorguları tutması için izin vermeniz gerekir. Örnek olarak 30 tane istemcinin bir server’a
eriştiğini düşünelim. Eğer sorgularımız istemci programında tutulursa sorguda yapılacak bir değişiklik
yüzünden 30 istemciye program, düzeltilmiş haliyle yeniden dağıtılması gerekir. Eğer sorguları sadece
veritabanı server’ında tutarsak sorguyu sadece bir yerde değiştirmekle bütün istemcilerde değiştirmiş gibi
olacağız.
Sorguları basitleştirmeye ek olarak sorguları server’da tutmak, akıllı özellik konusunda da yardım
eder. Büyük bir iş yeriniz olduğunu düşünün. Burada, değişik tipte prosedürler kullanılmıştır. Kimisi
lojistik, kimisi matematiksel, kimisi de verimliliği artırmak için özel prosedürlerdir. Bu tip prosedürlerin
tümüne birden iş yeri kuralları adı verilir. Onların çoğu, iyice düşünülmüş iş sırlarıdır ve onların
rakiplerinizin anlayamayacağı bir şekilde olmasını istersiniz. Bu kuralları server’ınızda işi nasıl
yaptığınızı belirtmeden, güvenli ve kullanışlı bir şekilde tutabilirsiniz. İstemciler veritabanı server’la
etkileşimde bulundukları zaman sır niteliğindeki işlerin sadece sonucunu alırlar. İşin nasıl yapıldığını
öğrenemezler.
Veritabanı server’ında tutulan sorgu prosedürleri, kayıtlı prosedürler (stored procedures) olarak
adlandırılır.
 
PROSEDÜRÜ HAZIRLAMAK
Basit bir örnekle başlayalım:

SELECT * FROM School

Bu SQL deyimi, bir kayıtlı prosedür gibi iyi çalışacaktır. Bu sorgu, School tablosundan bütün
satır ve sütunları döndürür. Bununla beraber kayıtlı prosedür olarak kullanmak için bu sorgu biraz daha
açık olmalıdır. Bu sorguyu ilerde geri döndüğümüzde devam ettirmek istediğimizde, en son istediğimiz
şey * in ne anlama geldiğini unutmak olacaktır. (Üç sütun mu, dört sütun mu, kaç sütun var?) Ayrıca
whitespace kullanarak sorguyu plana göre düzenlemek istersiniz. Dönen sütunları kolayca
yerleştirebilmek istersiniz.
Sorguyu plana göre düzenlemenin genel yolu şöyledir: SQL anahtar kelimeleri büyük harfle
yazılır, her bir sütun ismi ayrı satırlarda yazılır ve anahtar kelimeler satırların solunda diğerlerinden
yalıtılmış şekilde yazılır. Bu stil, prosedürleri daha iyi anlaşılır hale getirir:
 
SELECT School_ID

       School_Name

       Active

FROM School

 
CREATE PROCEDURE’Ü KULLANMAK
Oluşturduğunuz sorguyu kayıtlı prosedüre yerleştirmek için kullanacağınız deyim şu yapıdadır:
CREATE PROCEDURE name [; int number] AS sql_query
Name, prosedürün ismini ifade eder. Eğer noktalı virgül ve bir integer içerirseniz, aynı kök ismine
sahip bir grup kayıtlı prosedür oluşturursunuz ve onlar birbirinden, onlara eklenen sayılarla ayrılırlar.
Örneğin bir grup sipariş kaydı prosedürü OrderEntry;1, OderEntry;2, OrderEntry;3 şeklinde
isimlendirilebilir. Bu isimlendirme, prosedürleri self-documenting yollarla isimlendirmenize izin vermez,
fakat bütün OrderEntry prosedürleri DROP deyiminde sadece kök ismini kullanarak tek bir defada
silmenize olanak tanır. Sql_query de yukarda yaptığımız gibi bir sql sorgusudur.

CREATE PROCEDURE selfFrmSchoolAdodc1

AS

SELECT School_ID,

       School_Name,

       Active

FROM School

Burada kullandığımız isimlendirmeye dikkat edin. İlk üç harf (küçük harf olarak yazılmış)
sorguda kullandığınız emiri belirtir (Buradan SELECT sorgusu olduğu anlaşılıyor). Sonraki üç karakter,
bu sorgunun ilişkili olduğu programlama elemanının tipini belirtir (Frm, form olduğunu belirtiyor).
Sonraki karakterler sorguyla ilişkili programlama elemanının adıdır (FrmSchool). Sorgunun ismindeki
son eleman ise sorgunun kullandığı prosedür veya kontrolün ismidir.
Tip: Birden fazla program veritabanına eriştiği zaman, kayıtlı prosedürün ismine programı
belirtecek ek yapmak uygun olur. Eğer inceleme ihtiyacı duyarsanız kolayca hangi programın kodunu
çağıracağınızı bilirsiniz.
Dikkat etmeniz gereken bir ayrıntı daha var. Eğer kayıtlı prosedürü Enterprise Manager’da sağ
tıklayarak incelerseniz SQL kodunun en sonunda (FROM School’dan sonraki satırda) GO deyimini yazılı
bulursunuz.
GO deyimi, ondan önce gelen deyim yığınının şimdi koşulacağını işaret eder. GO’yu icrayı
başlatan bir işaretçi gibi düşünebilirsiniz. Bununla beraber kayıtlı prosedürdeki GO, bütün SQL
veritabanları tarafından desteklenmez. Onu, kayıtlı prosedürün sonunda çalışıyor bulabilirsiniz, fakat onu
başka yerde kullanırsanız söz dizimi hatasıyla karşılaşabilirsiniz.
Tip: Prosedürü silmek için DROP PROCEDURE procedureName ‘i kullanabilirsiniz.
 
PROSEDÜRÜ ÇAĞIRMAK
Kayıtlı prosedürü kullanmak için onu çağırabiliyor olmalısınız. Prosedürlerinden birini kullanmak
istediğiniz ve sonuçların size dönmesini istediğiniz, server’ı bilgilendirme araçlarına ihtiyacınız vardır.
Tam olarak server’ı böyle nasıl bilgilendirdiğiniz ve sonuçları nasıl aldığınız, içinde bulunduğunuz
koşullara bağlıdır.
Eğer bir sorgu aracındaysanız, araç kendi kendine kayıt kümesinin sonuçlarını alarak tutar. Sizin
sadece, server’a sizin için kayıtlı prosedürü icra etmesi için şu şekilde bir deyim yazmanız yeterlidir:

EXEC selFrmSchoolAdodc1

EXEC deyimi, server’dan, prosedürün icra edilmasini ve EXEC’in yayınladığı programa


sonuçların döndürülmesini ister.
Bununla beraber, sonuçları programa döndürmek istiyorsanız sonuçları tutacak bir araca
ihityacınız vardır. Eğer bir data control kullanıyorsanız data control kendi kendine, kayıt kümesini
toplamak için gerekli nesneyi ayarlayacaktır. Sadece EXEC deyimini onun RecordSource özelliğine
koymanız gerekir. Eğer ADO nesneleri veya benzer nesneler kullanıyorsanız komut nesneniz EXEC’i
yayınlayabilir ve sonuçları alabilir. EXEC deyimi komut nesnesinin CommandText özelliğinin içine
yazılır. Komut nesnesinin kayıt kümesi nesnesine ne döndürdüğünü belirlemeniz gerekir. Onun için
sonuçları kullanabilirsiniz. Aşağıdaki örnekte olduğu gibi komut nesnesini icra ettiğimiz zaman bu eylem
gerçekleşir.

SET rstRecordSet = cmdSQLStatement.Execute

 
SONUÇLARI KULLANMAK
Herhangi bir kayıt kümesini kullanabildiğiniz gibi aynen, kayıtlı prosedürün sonuçlarını da
kullanabilirsiniz. Bir kere kayıt kümesi döner, diğer herhangi bir kayıt kümesiyle aynı biçimde davranır.
Kayıt kümesi kullanmadaki bazı seçenekler şunlardır:
         Query Analyzer gibi araçlarla ona göz atmak
         Data control’ün Recordset özelliğine kayıt kümesini atamak
         Güncel kayıtla ilişkili değerleri, veriyi ekranda gösterecek bir grup kontrole atamak
         Kayıt kümesinde ileri veya geri harekete ve güncel kayıdı ekranda gösteren kontrolün
değerlerini güncellemeye izin veren düğmeler oluşturmak
         Kayıtları ekranda göstermeye izin vermek için, birt kayıt kümesini bir özellik gibi kabul eden
bir grid kontrolüne kayıt kümesini eklemek
Tip: Çoğu veritabanları için GO kullanmamıza gerek yoktur. GO’suz bir kayıtlı prosedür düzgün
çalışabilir. Bazı veritabanlarında, GO’yu prosedürü sizin için kesmesi için veritabanına izin vermede
kullanırsanız hata mesajı alırsınız.
Tip: Kayıtlı prosedürü sonlandırmak için GO’nun yerine RETURN de kullanabilirsiniz. SQL,
icra esnasında RETURN’e rastlarsa SQL kodunu icrayı durdurur. RETURN’den sonraki hiçbir deyim icra
edilmez. Bu davranıştan dolayı GO, RETURN’e göre gaha yumuşak sonlandırma olarak bilinir. GO,
veritabanına deyim yığınında icra edilecek kaç deyim olduğunu söyler, o deyimler onlarda gömülü
mantığa göre icra edilir ve ondan sonra prosedür sona erer. Bununla beraber RETURN, prosedürün
çağıran programa değer geri göndermesine neden olur. Eğer RETURN ile aynı satırda onu izleyen bir
değer yoksa 0 değeri çağıran programa gönderilir. Eğer çağıran programa geri değer gönderme
ihtiyacımız varsa GO yerine RETURN kullanmalısınız.
 
 

KAYITLI PROSEDÜRLERDE PROGRAMLAMA


Bu bölümde kayıtlı prosedürlere yetenekler eklemeyi işleyeceğiz. SQL, bazı güçlü, pratik
programlama yeteneklerine sahiptir. Bu yetenekler sayesinde, istemci tarafından çağrılıp server tarafında
çalışan maharetli veritabanı programları yazabilirsiniz.
Şöyle bir örenk düşünelim: Veritabanından günde 3 defa finansal bilgileri alma ihtiyacı olan bir
şirketle anlaşma yaptınız. Gündüz, şirket, önceki güne ait gece içindeki toplam satış miktarını görmek
istiyor. Günün ortasında, aynı toplamları o gün için hesaplanmasını istiyor. Şirketin kapanış saatinde de o
günkü toplam satış ile bir önceki günle karşılaştırılmasını görmek istiyor. Bunun için 3 tane kayıtlı
prosedür ve doğru zamanda uygun kayıtlı prosedürü çağıran bir istemci programı yazmanız gerekir.
Bunun gibi çözümlerle ilişkili problemleri düşünün: İstemci bilgisayarın açık ve uygun
zamanlarda çalışıyor olduğunu nasıl garanti edebilirsiniz? Çalıştığınız istemci bilgisayarda işin belirli
zamanlarda yapılacağından ne kadar emin olabilirsiniz? Eğer bir kullanıcı scheduler’ı kapatırsa ne olur?
İstemci bilgisayarın saatinin her zaman server’daki saatle doğru bir şekilde senkronize olduğundan emin
olabilir misiniz? Eğer istemciyle server arasındaki bağlantı beklenmedik bir şekilde koparsa ne olur?
İstemci / server etkileşimini düşündüğünüz zaman istemcinin neler için iyi, server’ın neler için iyi
olduğunu hatırlamanız gerekir. İstemciler kullanıcılarla etkileşim için, bilgiyi çekmek için ve kullanıcıya
o bilgiyi işlemesine izin vermek için uygundurlar. İstemci bilgisayarlar her zaman açık tutulmazlar.
Kullanıcıların programdan çıkma hakkı olduğu için istemcide programların sürekli çalışacağı garanti
edilemez. Kritik görevlerde istemcinin kullanılması makul değildir.
Server’lar kritik görev uygulamaları için daha kararlı çevre birimleridir. İlk olarak, server’lar
genellikle sürekli açık ve çalışır durumdadırlar. İkinci olarak, server’lar, server’ın sağlıklı olarak
çalışmasını sağlamak için eğitilmiş sistem yöneticilerinin denetimi altındadır. Bu yüzden scheduled
programlar rahatlıkla icra edilebilir. Üçüncü olarak, server’lar karışık prosedürleri icra etmek için tahsis
edilmiş daha çok sistem kaynağına sahiptirler. Bunlardan dolayı server’lar, verilen işlemi tutmada isemci
bilgisayarlardan daha hızlıdırlar.
 
DEĞİŞKENLERİ KULLANMAK
Az önce belirttiğimiz prosedür tiplerini oluşturmak için değişkenleri kullanabiliyor olmanız
gerekir. Onlara bir değer atayabilir veya onları sabit bir değere atayabilirsiniz. Biz örneğimizde, 3 zaman
için sabit değerlere ihtiyaç duyacağız. Bu sabit değerleri kullanarak, ilerde değiştirmemiz gereken bir
saati tek bir yerde değiştirmekle hepsinde değiştirmiş oluyoruz.
Değişkenleri kullanmak için onları ilk önce kayıtlı prosedürlerde deklare etmeniz gerekir.
Değişkenleri deklare etmek için âdet olan yer, kayıtlı prosedür kodunun en başıdır. Bir değişkeni deklare
etmenin yolu DECLARE anahtar sözcüğünü kullanmak ve ardından da değişken ismi ve değişken tipini
belirtmektir:

DECLARE @OverNight datetime

Tip: Değişken ismindeki @ işaretinin özel anlamı vardır. Tek @ işareti ile başlayan değişken
isimlerine sahip değişkenler kayıtlı prosedür için yereldir. İki tane at işareti (@@) ile başlayanlar ise
global’dir ve server’da çalışan bütün kayıtlı prosedürler için kullanılabilirdir.
Tek satırda birden çok da değişken deklare edebilirsiniz:

DECLARE @OverNight datetime, @MidDay datetime, @CloseOfBusiness datetime

Her bir değişken isminin değişken tipiyle beraber kullanıldığına dikkat edin. Ayrıca, varsayılan
değerler DECLARE deyimi kullanarak atanamazlar.

Varsayılan değerleri değişkenlere atamanın 2 yolu vardır. İlki olan SELECT deyimini şu şekilde
kullanabilirsiniz:
SELECT @OverNight=’4:00 am’
SELECT, belki de atama için en uygun yoldur. Çünkü bütün SQL veritabanlarında
belirtildiğinden emin olabilirsiniz. Daha sonraki standartlar, değişkenin değerini atamak için SET
deyimini sağlamıştır:

SET @OverNight=’4:00 am’

SET ile SELECT arasında çok küçük bir fark vardır. SET, bir sorgunun sonucu olan bir değeri
atamanıza izin verir:

SET @SalesTotal=(SELECT SUM(Qty) FROM Sales WHERE Title_ID=’BU1032’)

Bu SET deyiminde SELECT değikenine atanacak olan Qty sütunundaki değerleri toplar ve
sadece ve sadece bir değer döndürür. Fakat bunu SELECT ile de yapabiliriz:

SELECT @SalesTotal=(SELECT SUM(Qty) FROM Sales WHERE Title_ID=’BU1032’)

Bununla beraber SET,  cursor’lar ve değişkenlerle çalışmanıza izin verirken SELECT buna izin
vermez.
 
AKIŞ KONTROLÜNÜ KULLANMAK
Akış kontrol deyiminin amacı koşullar belirtmektir. SQL 2 tip akış kontrolü sağlar:
         IF … ELSE
         Spesifik veritabanı temelinde uygulanmış loop yapıları
Bütün veritabanları IF … ELSE ‘i destekler. Bazı veritabanları LOOP ve FOR deyimlerini
destekler. Diğerleri WHILE deyimini destekler.
IF, bir koşulu test etmenize ve koşulun doğruluğuna göre bir veya bir grup deyimi icra etmenize
izin verir. Temel yapı şöyledir:
IF condition

   BEGIN

      Statements

   END

ELSE

   BEGIN

      Statements

   END
Condition, True veya False olarak değerlendirilebilecek bir SQL ifadesidir. Statements, geçerli
SQL deyimleridir. BEGIN ve END, ard arda çalışması gereken deyim bloğunun sınırlarını belirler. Bu
bloğun arasındaki deyimler, ondan önce gelen şartın uygunluğu durumunda çalışırlar.

Kayıtlı prosedürümüzün ilk koşulunu ayarlamak için o andaki saatin gecenin sona erme saatine eşit veya
daha ilerde mi olduğuna bakmak gerekir. Eğer koşul doğru ise önceki 24 saat için SUM deyimini
koşacağız:
IF DATEPART(hh, GETDATE())<DATEPART(hh,@OverNight)

  AND DATEPART(dd, GETDATE())=DATEPART(dd,@OverNight)

  BEGIN

    SELECT SUM(Qty) FROM sales WHERE ord_date BETWEEN GETDATE()

    AND DATEADD(d, -1, GETDATE())

  END

Buradaki mantığa dikkat edelim. Eğer o andaki saat gecenin sona ermesinden küçük ise (ve gün
aynı ise) deyimi çalıştırıyor. @OverNight değişkenine yerleştirdiğiniz değer DATEPART() ile
çıkarılabilen bir saat değeri olmalıdır. 24 saat kullandığımızı farz edelim. (böylece sabah ile akşam ayırt
edilebilir) Ayrıca ord_date, tarih’in Aricinde zamanı da içermelidir. Son olarak, zamanın sona erme
vaktinden önce kayıtlı prosedüre başlamış olmak gerekir.
Geri kalan iki koşul da (CloseOfBusiness ile MidDay) de aynı yapıdadır.
SQL’de akış kontrolünde döngüler de kullanabilirsiniz. Oracle kullanıyorsanız koşulsuz
döngülere ve FOR LOOP’a aşinasınızdır. Diğer veritabanları bu döngü yapılarını desteklemezler. SQL
Server sadece WHILE döngüsünü destekler.
Size bir görüş vermesi açısından Oracle’da yazılmış bir döngü yapısına bakalım:

DECLARE LOOPCOUNTER NUMBER;


BEGIN

   LOOPCOUNTER := 0

   WHILE (LOOPCOUNTER<5) LOOP

      LOOPCOUNTER := LOOPCOUNTER+1;

 
  END LOOP;

END;

Dikkat ederseniz, SQL deyimlerinin sonuna noktalı virgül konuyor ve döngü deyimi
LOOPCOUNTER := 0 ile başlayıp END LOOP ile bitiyor. Ayrıca, özelleştirilmiş atama operatörü (:=)
kullanılıyor ve değişkenlerin önünde @ işareti bulunmuyor.
Aynı döngünün SQL Server 2000 ile yazılmış şekli şöyledir:

DECLARE @LOOPCOUNTER NUMERIC

SELECT @LOOPCOUNTER = 1

   WHILE (@LOOPCOUNTER<5) LOOP

   BEGIN

      SELECT @LOOPCOUNTER = @LOOPCOUNTER+1

   END

Bu iki döngü de aynı işi yaparlar. Döngü 4 defa çalışır ve arurma değeri 1 olduğu için her adımda
sayıcı 1 artar. Sayıcı 5’e ulaştığında döngüden çıkılır.
Örneğimize dönersek, prosedürümüzün gecenin sona erme zamanında 10 dakika içinde icra
edilmesini isteriz.
WHILE (DATEPART(mi, GETDATE())

   BETWEEN DATEPART(mi, DATEADD(mi,-10,@OverNight))

   AND DATEPART(mi, @OverNight)

   AND DATEPART(mm, @OverNight) = DATEPART(mm, GETDATE())

   AND DATEPART(dd, @OverNight) = DATEPART(dd, GETDATE()))

BEGIN

   Place IF statements here

   BREAK

END

Bu döngü sadece GETDATE() ile çekilen dakika değeri @OverNight ile çekilen dakika değeri ile
10 dakika öncesi arasındayken icra edilir (BETWEEN kullanımı, verilen 2 saat arasında saat seçmenize
imkan tanır). GETDATE()’in gün bölümünü de kontrol ettik ve @OverNight ile aynı olduğundan emin
olduk.
IF deyimlerinin olduğu yeri daha önce işlediğimiz için sadece etiketini koyduk. BREAK deyimi
IF deyimleri icra edildikten sonra koşulsuz olarak çıkmayı sağlar (örnekte sadece bir defa
hesapladığımıza dikket edin).
 
 
DEĞER DÖNDÜRMEK
Döndürülen değer, kayıtlı prosedürden çağıran prosedüre geri yollanan değerdir. Bu yolla sadece
tek değer döndürülebilir. Çağıran prosedüre geri değer döndürmek için RETURN deyimini kullanmanız
gerekir. Genel yapı şöyledir: RETURN [expression]
Eğer RETURN’ü sade (ifade kullanmadan) kullanırsak 0 değerini döndürür. Aksi halde,
RETURN kelimesini izleyen SQL ifadesinin değerini döndürürsünüz.
Tip: Çoğu veritabanları hata oluştuğunda hatanın numarasını tutan dahili global değişkenlere
sahiptirler. Örneğin SQL Server’da bu değişken @@error dur. Eğer hata oluşmazsa değişken 0 değerini
tutar. Değişkendeki değer, en son çalışan deyimle ilgili hata kodudur.
Kayıtlı prosedürden dönen değeri elde etmek için EXEC deyimini kullanmanız gerekir:

EXEC @SomeVariable = StoredProcedureName

 
HATA TANIMLAMAK
Hata oluşursa, onu, kayıtlı prosedürün return değerindenbağımsız olarak çağıran programa
döndürmek isteyebilirsiniz. Bu, eğer uygulamanızın hata tutma kodu hatayı alabilir ve raporlayabilirse
çok kolaydır.
Her bir SQL veritabanı döndürülen hatalar için bir mekanizma sağlar.
SQL Server’da hatayı döndürmek için RAISEERROR deyimini kullanırsınız. Yapısı şöyledir:
RAISEERROR ({msg_num or msg_str}, severity_num, state, [,argument1
[,argument2] ]) [WITH Options]

Bu yapıda, mesaj numarası veya mesaj stringi kullanmanız gerekir. Ayrıca büyüklük derecesini
belirten bir numara da yazmalısınızdır. Geçerli numaralar 0 ile 25 arasındadır. Genellikle kullanıcılar 0 ile
19 arasındaki değerleri kullanırlarken, sistem yöneticileri 20 ile 25 arasındakileri kullanmaktadırlar.
State, hata oluştuğunda sistemin durumunu belirten 1 ile 127 arasında keyfi bir sayıdır.
Hata numarası kullanarak hatayı tanımlamak için bu deyimi şu şekilde kullanırız:

RAISEERROR (50025,1,1)

SQL Server 1 ile 50000 arasındaki hata numaralarını kendisi için ayırır. 50000’den yukarı olanlar
kullanıcı tarafından tanımlı hatalardır. Hatanın büyüklük derecesinin 1 olması bilgi seviyesinde olduğunu
belirtir. Daha büyük hatalar için büyüklük derecesi artırılmalıdır.
Mesaj stringine sahip bir hata tanımlamak istiyorsanız şu yapıyı kullanabilirsiniz:

RAISEERROR (‘This is a user defined error.’, 1, 1)

Hata stringinizde ek bilgiler içermek için vekil parametreler kullanabilirsiniz. Örneğin hata
numarasını deyime yerleştirebilirsiniz.

RAISEERROR (‘This is a user defined error number %d.’, 1, 1, 50025)

Burada %d, en sonda belirtilen 50025 sayısının signed integer olduğu anlamındadır. (d veya I=
signed Integer, O= unsigned integer, P= Pointer, S=String, U= Unsigned Integer, x veya X= Unsigned
Hexadecimal)
Deyimin son kısmında seçenek anahtar sözcüğünü kullanabilirsiniz. Şu seçenekler var:
LOG: Veritabanı loguna hatayı yazar.
NOWAIT: İstemciye mesajı beklemeden yollar.
SETERROR: Sağladığınız mesaj numarası veya mesaj stringini @@error’un değerine atar.

PARAMETRELER KULLANMAK
Veriyi alıp alamadığımızı öğrenmek için (örnek olarak) strBuffer ile “MyDirectory” yi  byte byte
karşılaştırmamız gerekir. Alınanın doğru olduğunu anlamak için muhtemelen 11 karşılaştırma yapmanız
gerekecektir. GetCurrentDirectory() ile uygulanan yaklaşımla, eğer 0 byte geri dönmüşse fonksiyonun
başarısız olduğunu anlıyoruz.
Peki, strBuffer nedir? Teknik olarak, Visual Basic’te bir string değişkenidir. İşletim istemi
açısından, fiziksel bellekte bir alan belirten isimlendirilmiş depolama yeridir. Bu fiziksel bellek,
fonksiyonun çalışması esnasında, o andaki dizin’in isminin yazıldığı yerdir.  Ardından, programınız, o
andaki dizinin’in ne olduğunu öğrenmek istediği zaman bu depolama yerine erişebilir. Çıkış
parametresinin özü de, çağıran ve fonksiyon kodu tarafından erişilebilir olan depolama yeridir. Buraya
yerleştirilen her veriye, fonksiyon ve onu çağıran program tarafından erişilebilir.
SQL’de aynı tür rutinler vardır. Eğer parametreyi çıkış parametresi olarak belirtirseniz parametre,
depolama için tahsis edilmiş bellektir. Değer, o yere yerleştirilmiştir ve o yerin adresi çağıran program
tarafından elde edilebilirdir. Eğer işlem veritabanı server’ında çalışıyorsa veritabanı server’ında bellek
tahsis edilmiştir (istemcide çalışıyorsa istemcide tahsis edilmiştir). Veritabanı API’ları (ODBC API gibi)
çıkış parametreleri için bellek tahsis etme ve okuma yeteneklerine sahiptir. Ağ üzerinde bir makineden
diğerine veri hareketine ihityaç duyulursa bunu başarabilirler. Veritabanımızda istemci erişimi
programlaması için kullandığımız ActiveX Data Objects, parametre değerlerini alma ve depolama
yeteneğine sahiptir.
 
PARAMETRELER OLUŞTURMAK

Parametrelerin bütün avantajlarından yararlanmak için onları, parametreleri alacak olan kayıtlı
prosedürlerde ve kayıtlı prosedürleri çağıracak olan programlarda oluşturabiliyor olmanız gerekir. Kayıtlı
bir prosedürde bir parametre oluşturmak için, CREATE PROCEDURE deyiminde prosedür isminden
sonra ve AS’den önce bir değişkenler listesi vermeniz gerekiyor:
CREATE PROCEDURE myProc

@ParameterIN varchar(20),

@ParameterOUT int OUTPUT


AS

Insert SQL statements for the stored procedure here

CREATE PROCEDURE deyiminde değişken deklarasyonu için aynı bilgiler girilmektedir.


Burada, DECLARE anahtar sözcüğü kullanılmaz. Giriş parametreleri onları belirtmek için özel anahtar
sözcüklere ihtiyaç duymaz iken çıkış parametreleri parametrenin belirtilmesinde OUTPUT anahtar
sözcüğüne ihtiyaç duyar. Kayıtlı prosedürdeki parametreler, listede birbirlerinden virgüllerle ayrılır.

Program tarafında parametre oluşturmak için ADO emir nesnelerini kullanırsınız. Bu nesneler, kayıtlı
prosedüre verebilen ve kayıtlı prosedürden alabilen parametrelerin bir koleksiyonunu tutar. Bir kere emir
nesnesini oluşturduğunuz zaman parametreleri eklemek için aşağıdaki gibi deyimler kullanmanız gerekir:
Set prmInput = cmdCommand.CreateParameter(“MyInputParameter”, adVarChar,
adParamInput, 20, “ParameterValue”)

cmdCommand.Parameters.Append prmInput

Set prmOutput = cmdCommand.CreateParameter(“MyOutputParameter”,


adInteger, adParamOutput, 4)

cmdCommand.Parameters.Append prmOutput

Set prmReturn = cmdCommand.CreateParameter(“MyReturnParameter”,


adInteger, adParamReturnValue, 4)

cmdCommand.Parameters.Append prmReturn

Genelde Set deyimi, parantezler arasında belirtilen nitelikleri olan bir parametre oluşturmak için
emir nesnesi içindeki CreateParameter metodunu kullanır. Bu niteliklerin hepsi opsiyoneldir. Bunlar:
Ad:Sonradan onu çağırmak için kullanabileceğimiz bir parametre ismi veren bir stringtir.
Tip: Parametrenin veri tipi. ADO, kullanımda SQL veri tiplerine benzer veri tipleri için sabitler
tanımlamıştır. Bu sabitler şunlardır: adBigInt, adBinary, adBoolean, adBSTR (Unicode stringler için),
adChapter, adChar, adCurrency, adDBDate, adDBTimeStamp, adDecimal, adDouble, adEmpty, adError,
adFileTime, adGUID, adInteger, adLongVarBinary, adLongVarChar, adLongVarWChar, adNumeric,
adSingle, adSmallInt, adTinyInt, adUnsignedBigInt, adUnsignedInt, adUnsignedSmallInt,
adUnsignedTinyInt, adVarBinary, adVarChar, adVarNumeric, adVarWChar (null’la bitirilmiş Unicode
string için), adWChar
Yön: Parametrenin yönüdür. Mümkün değerler: adParamInput, adParamOutput,
adParamInputOutput, adParamReturnValue, adParamUnknown.
Boyut: Parametrenin kullandığı karakter sayısını veya byte’ı belirten integer’dır (integer değerle 4
byte yer tuttuğu için 4 değeri yazılırken string değerlerde karakter sayısıyla orantılıdır).
Değer: Parametrenin değeridir. Bu niteliği, oluşturma zamanında bir değeri ayarlamak için
kullanırsınız.
Tip: Parametre koleksiyonundaki parametrenizin veri tipi kayıtlı prosedürde kullandığınız veri
tipiyle aynı olmalıdır.
DEĞERLER VERME

SQL’de değerleri EXECUTE deyiminde, parametre değerlerini kayıtlı prosedürün isminden sonra doğru
sırada listeleyerek verebilirsiniz:
EXECUTE ProcedureName ‘InputValue1’, ‘InputValue2’

Parametreler listede virgüllerle ayrılarak yazılır. Parametre değikenlerinin isimlerini listede


açıkça da belirtebilirsiniz:

EXECUTE ProcedureName @Parameter1 = ‘InputValue1’, @Parameter2 = ‘InputValue2’

Kayıtlı prosedürde varsayılan parametre değerini kullanabilirsiniz. Böyle yapmak için CREATE
PROCEDURE deyiminde değerleri içermeniz gerekir:

CREATE PROCEDURE myProc

@ParameterIn varchar(20)=’ThisValue’

@ParameterOut int OUTPUT

AS

Insert SQL statements here


Varsayılan değerler tanımlı bir kayıtlı prosedür kullandığınızda, giriş parametresi olarak değer
vermezseniz kayıtlı prosedür belirtilen varsayılan değeri kullanır (Kayıtlı prosedürde varsayılan değer
verilmeyen bir parametreye dğer varmezseniz hata mesajı alırsınız). Varsayılan değer kullanmak
istediğinizi DEFAULT anahtar sözcüğünü kullanarak da belirtebilirsiniz:

EXECUTE ProcedureName DEFAULT, ‘InputValue2’

EXECUTE ProcedureName @Parameter1=’InputValue1’, @Parameter2=DEFAULT

İstemci programından parametreleri vermek için 2 yöntem vardır. İlkinde, parametreleri emir
nesnesinin CommandText özelliğini oluşturan string içinde toplarsınız. Bu metod, emir nesnesini
kullanarak giriş parametresinin bir koleksiyonunu oluşturmak istediğinizde kullanmanız gereken
metoddur. strInputValue değişkenini deklare ettiğinizi farz edersek şu yapıyı kullanarak kayıtlı prosedüre
değerleri verebilirsiniz:

cmdCommand.CommandText = “Exec myProc” & strInputValue

Birden çok değer vermek istediğinizde de aralara virgül konulur:


cmdCommand.CommandText = “Exec myProc” & strInputValue & “, ” & _

strParam2 & “, ” & strParam3

Parametreler CommandText stringinde, kayıtlı prosedürdeki parametre listesinde bulunduğu aynı


sırayla verilir.
Eğer oluşturulmuş bir giriş parametresi koleksiyonunuz varsa, parametreleri CommandText
stringi içinde birleştirmenize gerek olmaz. Parametreleri, kayıtlı prosedürde bulunduğu sırayla aynı sırada
oluşturduğunuzdan emin olun. Koleksiyona eklenen ilk giriş parametresi, kayıtlı prosedür için parametre
listesindeki ilk parametre olmalıdır (ikinci-ikinci…). Emir tipini adCommandText’e ayarlamayın. Onun
yerine, onu adCmdStoredProc’a ayarlayın. CommandText özelliğini de kayıtlı prosedürün ismine
ayarlayın. Emir nesnesi kayıtlı prosedürü yerleştirir ve onu icra eder, sizin için parametreleri içine girer.
ADO ve SQL, her iki kayıtlı prosedürü icra etme metodu için özel avantajlar sağlamaz. Eğer çıkış
parametreleri kullanmanız gerekiyorsa veya dönüş değeri almak istiyorsanız parametre koleksiyonu
kullanmalısınız. Programa bir kayıtlı prosedürden bilgi almanın en etkili yolu budur.
 
SONUÇLARI KULLANMAK
SQL ile sonuçları toplamak zor değildir. Bir dönüş değeri almak için, dönüş değeri alan
EXECUTE deyimine bir değişken eklemelisiniz:
EXECUTE @ReturnValue = MyStoredProcedure

Çıkış parametresini almak için parametre listesindeki parametreyi içerin ve OUTPUT anahtar kelimesini
kullanın:
EXECUTE @ReturnValue = MyStoredProcedure @OutputParameter =
@ReceivedValue OUTPUT

Prosedürün isminden sonra belirtilen ilk değer (@OutputParameter), kayıtlı prosedürde


tanımlanan parametrenin ismidir. Eşittir işaretinin sağındaki değişken ismi (@ReceivedValue), kayıtlı
prosedürden çıkış değerini alan değişkendir. Bu alıcı değişken, çağıran programda belirtilmiştir.
Dolayısıyla, kayıtlı prosedürde belirtilmiş değişkenin değerini kayıtlı prosedürün dışında belirtilmiş bir
değişkene vermiş oluyorsunuz.
Programdan çıkış değerlerini almak çok basittir, fakat dikkat etmediğinizde problemlere neden
olan önemli bir nokta vardır. Bir dönüş değeri veya bir çıkış parametresi almak için parametre
koleksiyonunu kullanın. Bir dönüş değeri parametresi ve bir çıkış parametresi belirtin. Sonra emri icra
edin. Bu işlem için temel kod şöyledir:

Set prmOutput = cmdCommand.CreateParameter(“MyOutputParameter”, adInteger,


adParamOutput, 4)

cmdCommand.Parameters.Append prmOutput

Set prmReturn = cmdCommand.CreateParameter(“MyReturnParameter”,


adInteger, adParamReturnValue, 4)

cmdCommand.Parameters.Append prmReturn

cmdCommand.CommandText = “MyStoredProcedure”

cmdCommand.CommandType = adStoredProcedure

Set rstRecordset = cmdCommand.Execute


Emrin icrası bittiğinde, parametre koleksiyonundaki dönüş parametresi ve çıkış parametresi
kayıtlı prosedürden alınan değerleri içerecektir. Önemli nokta ise, emirin icrasının bittiğini bilmektir.
Şeklen, kayıt kümesindeki en son kayıt istemci programınıza ağ bağlantısı boyunca sıraya konulduğu
zaman emir icrası biter. Bununla beraber ADO, en son kayıt dönmeden önce kullanmanız için kayıt
kümesi nesnesini serbest bırakır. Dolayısıyla kullanıcı bütün kayıtların ayrılmasını beklemeden
etkileşimli olarak sonuçları aldığı için istemci programının performansı artacaktır.
ADO, kayıt kümesinin tamamen döndürüldüğünü anlatan bir bayrak içermez. Bu yüzden, çıkış ve
dönüş parametrelerinin ne zaman serbest olacağını söyleyemezsiniz. Serbestliğini garanti etmek için kayıt
kümesini kapatmanız gerekir:

rstRecordset.Close

Parametrelere 2 yoldan birisiyle erişebilirsiniz. Eğer parametreleri isimlendirmişseniz onlara


isimleriyle erişebilirsiniz:

intReturn = cmdCommand(“MyReturnParameter”)

strOutput = cmdCommand(“MyOutputParameter”)

Eğer parametreleri isimlendirmediyseniz, onlara parametre koleksiyonundaki 0 tabanlı fihrist


numarasıyla erişebilirsiniz:

intReturn = cmdCommand(1)

strOutput = cmdCommand(0)

Tip: Parametreleri aldıktan sonra kayıt kümesine erişimi elde etme ihtiyacı duyarsanız kayıt
kümesini Open metodu kullanarak bir daha açabilirsiniz.
Not: ADO’da parametre değerleri, kayıt kümesi tamamıyla döndürülünce serbest bırakılır.
 
CURSOR’LARI KULLANMAK
Bazen kayıt kümesine denk başka bir SQL işleme türüne ihtiyaç duyabilirsiniz. SQL, kayıt
kümesini içine almaya izin veren cursor’ı size sunar. İsimlendirilmiş bir cursor’unuz oldu mu, satırları u-
cursor’dan alabilir ve o satır üzerinde çalışabilirsiniz. SQL cursor’ını ADO kayıt kümeleriyle
karşılaştırırsanız bazı benzerlikler bulacaksınızdır. Bir satırdan diğerine geçiş, kayıt kümesinde yaptığınız
gibidir. Satırdaki alan değerlerine erişebilir, satırdaki alan değerlerini güncelleyebilir veya bir satırı
silebilirsiniz. Cursor ile istemci tarafındaki kayıt kümesi arasındaki fark, cursor’ın işlemi veritabanı
server’ında icra etmesidir. Cursor’lar verinin ağ boyunca sıralanmasını gerektirmez. Bir ADO kayıt
kümesiyle yapacağınız işlerin hepsini, cursor kullanarak bir kayıtlı prosedür olarak belirtirseniz, aynı işi
server tarafında işlenen şekliyle yapmış olursunuz. Böylece server’ın işleme gücünden yararlanırsınız.
Bununla beraber cursor’ı dikkatli kullanmanız gerekir. Çoğu SQL programcıları cursor’ları
performans hırsızı olarak görmektedir, ki bu iddia doğrudur. Cursor’lar, bütün işlemler server tarafında
yapıldığı zaman en iyi sonucu verirler. Cursor kullanan kayıtlı prosedür, istemciyle bağlantı kurmadan,
tek başına işlemleri server tarafında tamamlamayabilir. Cursor’lar, istemciye döndüreceğiniz (özellikle
kayıt kümesi dönünceye kadar işleme devam edemeyen) bir kayıt kümesi oluşturmak için cursor
kullanmanız gerektiğinde yavaş kalırlar.
Not: Cursor’ların yavaş olmasının bir sebebi, cursor’ın kullanımını kolaylaştırmak için geçici
tablolar oluşturması ve tutmasıdır. Tipik olarak bir cursor, cursor’la ilgili satırları tutması için TempDB
veritabanında geçici bir tablo oluşturur.
Bir SELECT deyimi, SELECT deyimi ile belirtilen kayıt kümesinde bir cursor’a göre daha hızlı
çalışır. Seçilen satır; kayıtlar, kayıtlara pointer ve cursor’da kayıtlar arasında gezinmenize izin veren
benzer yapılar içerecek olan veri yapıları üretmenizi gerektirmez. Cursor kullanma ile kullanmama
arasında bir seçim olduğunda genellikle cursor kullanılmayan çözüm performans bakımından tercih edilir.
Bununla beraber, yapılacak işin kayıtlı prosedür olarak tanımlandığı tek istemci etkileşiminin
kayıtlı prosedürü çağırmak olduğu, prosedürün çalıştığı ve görevin tamamen sever tarafında gerçekleştiği
durumlar tam cursor’ın kullanımına göredir. Ayrıca cursor esneklik ve doğruluk sağlar.
 
CURSOR’I DEKLARE ETMEK
Cursor’ı kullanmak için önce deklare etmeniz gerekir. Cursor’ı deklare etmek, bir cursor’ı
kullanmayı niyet ettiğinizi veritabanına bildirme işlemidir.
Temel bir cursor deklarasyonu şu şekildedir:

DECLARE curAuthor CURSOR FOR SELECT * FROM authors

Bu deyimin temel elemanları: DECLARE anahtar kelimesi, cursor için bir isim, cursor’ı
oluşturuyor olduğunuzu belirten CURSOR FOR anahtar kelimeleri ve bir SELECT deyimidir.
Cursor’ın deklarasyonunda kullanacağınız ek seçenekler de vardır. Örneğin:

DECLARE curAuthor INSENSITIVE CURSOR FOR SELECT * FROM authors

INSENSITIVE anahtar sözcüğü, TempDB’deki bir tabloya döndürülen verinin bir kopyasına
cursor’ın yerleşmesine neden olur. Veri üzerindeki bütün işlemler verinin bu gizli kopyasında
gerçekleştirilir. Insensitive cursor’ı veriyi güncellemek için kullanamazsınız.

DECLARE curAuthor SCROLL CURSOR FOR SELECT * FROM authors


SCROLL anahtar sözcüğü veriyi ileri ve geriye doğru kaydırabileceğinizi belirtir. Bu seçenek
belirtilmezse yalnızca sonraki kayıda geçebilirsiniz (bu tip cursor forward-only cursor olarak bilinir).
SELECT deyiminden sonra, cursor’ı kullanmak için 2 değişik yaklaşım belirtebilirsiniz. Birisi
şöyledir:

DECLARE curAuthor CURSOR FOR SELECT * FROM authors FOR READ ONLY

Bu örnekte FOR ifadesini kullanmak, cursor’ı salt-okunur yapıyor. Veriyi okuyabilir fakat
değiştiremezsiniz. Eğer cursor’da alanları güncellemek isterseniz şu formu kullanmanız gerekir:

DECLARE curAuthor CURSOR FOR SELECT * FROM authors FOR UPDATE phone

FOR UPDATE ifadesi cursor’da virgüllerle ayrılmış listedeki sütunların cursor’da güncellenebilir
olduğunu belirtir.
CURSOR deklare edildikten sonra OPEN emri ile kullanılır.

OPEN curAuthor

OPEN deyimi, cursor’ın veri ile yaşaması için cursor’ın kayıt kümesini belirten sorguya neden
olur.
 
CURSOR’LARI KULLANMAK
Cursor’ları kullanmak için onları OPEN metoduyla açmanız gerekir. FETCH deyimini cursor’dan
satırları (işletebilmeniz için) belleğe almak için kullanabilirsiniz. Bütün cursor’lar şu deyime cevap
verecektir:

FETCH NEXT FROM curAuthor

Bu deyim, bir sonraki satırın işlemler için geçerli olmasına sebep olur. Bu, sonraki satırın belleğe
alındığı ve SQL’in ona ve onun bütün alanlarına bir pointer (işaretçi) tanıttığı anlamına gelir. Eğer belirli
sütunların değerlerini değikenlere yerleştirmek istiyorsanız, prosedürdeki alana işlemini yapacak olan
değişkenleri deklare etmeniz gerekir. Spesifik alanları spesifik değişkenlere şu deyimle alabilirsiniz:

FETCH NEXT FROM curAuthor INTO @au_id, @au_fname, @au_lname, @phone, @address,
@city, @state, @zip, @contract

FETCH, esneklikle cursor’ın mevkiini ayarlamanıza imkan tanıyan ek anahtar sözcük sağlar.
NEXT’te PRIOR kullanımı bir sonraki yerine bir önceki satırın alınmasını sağlar. FIRST, cursor’ın ilk
satıra; LAST da cursor’ın son satıra gitmesini sağlar. Aşağıdaki deyim ise cursor’ın başından itibaren 8
satırın alınmasını sağlar:
FETCH ABSOLUTE 8 FROM curAuthor

Eğer şu anda geçerli satırdan itibaren 8 satır almak istiyorsanız:

FETCH RELATIVE 8 FROM curAuthor

Mutlak ve göreceli konumlandırma, baştan itibaren son kayıda doğru kayıtlar almak için pozitif
sayılar ve sondan başa doğru kayıtlar almak için de negatif sayılar kullanmanıza izin verir. Ayrıca direkt
sayı değeri yerine değişkenler de kullanabilirsiniz.
Başka bir cursor’tan alma yöntemi daha vardır. CURSOR veri tipinde olacak bir değişken
tanımlayabilirsiniz. Bu değişkene bir cursor’ı atayabilir ve kayıtlı bir prosedüre parametre olarak o
cursor’ı geçirebilirsiniz. Bu mekanizmaya bir göz atalım:

DECLARE @MyCursor CURSOR

SET @MyCursor = curAuthor

Ayrıca, cursor değişkeninin değerini, ismin yerine cursor’ın tanımını koyarak da


ayarlayabilirsiniz:

SET @MyCursor = CURSOR FOR SELECT * FROM authors FOR UPDATE phone

Bir cursor’a parametre olarak kabul edilebilen bir kayıtlı prosedürünüz olduğunu farz edelim.
Aşağıdaki yapıyla cursor’ı atayabilirsiniz:

EXECUTE selAuthorList @MyCursor OUTPUT

Mevcut bir prosedüre bir cursor’ın geçirilmesinin ana nedeni cursor’ı bir çıkış parametresi olarak
veriyle doldurulmuş olarak döndürebilmeniz içindir. Kayıtlı prosedürde cursor’ı yaşatmak için SET
deyimini kullanırsınız. Mevcut prosedür döndüğünde, cursor değişkeni veri kümesine bir referans olarak
kalır. Bu, bir kayıtlı prosedürün çıkışı olarak bir grup satır elde etmek istediğimizde kullanışlı olur.
 
CUSOR’LARI GÜNCELLEMEK VE ARAŞTIRMAK
Cursor’da satırları değiştirmek ve silmek için UPDATE ve DELETE deyimlerini
kullanabilirsiniz:

DELETE authors WHERE CURRENT OF curAuthor


DELETE anahtar sözcüğünden sonra cursor’da referanslanan tablonun ismi yazılır. WHERE
CURRENT OF, bu anahtar sözcüklerden sonra ismi yazılan cursor’ın o andaki pozisyonuna DELETE
işleminin yapılacağını belirtir.
UPDATE deyimi de benzer şekildedir:

UPDATE authors SET au_lname=NULL WHERE CURRENT OF curAuthor

SQL, cursor araştırmada bir çift yapı sunar. İlki, global değişken olan @@FETCH_STATUS’tur.
Veritabanınız, bu değişkenden en son alınan için bir sonuç kodu yerleştirir (Eğer alma işlemi başarılı
olmuşsa 0, alma işlemi başarısız olmuş veya cursor’daki son kayıttan öteye gitmeye çalışıldıysa -1,
almaya çalıştığınız kayıt kayıp ise -2). Cursor’ı araştırma işlemine başlamak için ilk önce cursor’ı
tanımlayın ve onu açın. Ardından, ilk satırı alın:

DECLARE @au_id varchar(11), @au_fname varchar(20), æu_lname varchar(40)

DECLARE curAuthor CURSOR FOR SELECT au_id, au_fname, au_lname FROM


authors

OPEN curAuthor

FETCH NEXT FROM curAuthor INTO @au_id, @au_fname, @au_lname

Bu örnekte Pubs veritabanındaki authors tablosundan 3 sütun almaya karar verdiğimize dikkat
edin. Bu kod parçasının sonunda ilk sütunu aldık ve değerleri değişkenlere yükledik. Artık araştırma
işlemini yapmaya hazırız.
Araştırma işlemi için FETCH_STATUS değeri 0 ise cursor’da satırları incelemeye devam
edebiliriz (0 değilse cursor’la ilgili problemimiz olduğu anlaşılır ve araştırma durdurulması gerekir).

WHILE @@FETCH_STATUS=0

BEGIN

Place statements that seek a match here

END
Bu mantık, cursor’daki bütün satırları araştırmanız içindir. Soyadı “Stringer” olan yazarın kaydını
yerleştirmek için ise şu mantık kullanılır:
 

WHILE @@FETCH_STATUS=0

BEGIN

   IF @au_lname = ‘Stringer’

      RETURN 99

   FETCH NEXT FROM curAuthor INTO @au_id, @au_fname, @au_lname

END

Bu örnekte, işlemin başarılı olduğunu belirtmek için bir değer döndürdük. SQL’de ekrana çıktı
sağlamak için PRINT deyimini de kullanabilirsiniz. PRINT sadece basılacak olan stringi argüman olarak
alır:

PRINT ‘Message’

Cursor ile daha karışık işler de yapabilirsiniz. Örneğin, çıkış parametresi olarak kayıtlı prosedüre
geçirilen bir cursor’ı açmak ve yazarların kayıtlarını içine almak gibi. @PassedCursor’ın kayıtlı
prosedüre bir parametre olarak tanımlandığını farz edersek kod şu şekli alır:
FETCH NEXT FROM curAuthor INTO @au_id, @au_fname, @au_lname

FETCH NEXT FROM @PassedCursor

WHILE @@FETCH_STATUS=0

BEGIN

   IF @au_lname = ‘Stringer’

      BEGIN

         FETCH RELATIVE 0 FROM @PassedCursor

         RETURN 99

      END

   FETCH NEXT FROM curAuthor INTO @au_id, @au_fname, @au_lname

   FETCH NEXT FROM @PassedCursor

END

Burada @PassedCursor şu şekilde tanımlanmıştır:

SET @PassedCursor = CURSOR FOR SELECT * FROM authors

 
CURSOR’LARI KAPATMAK VE SERBEST BIRAKMAK

Cursor’la işiniz bittiği zaman onu kapatmanız gerekir:


CLOSE curAuthor

Böylece onu kapatmış oldunuz, ama henüz onu yok etmiş olmadınız. İstediğinizde onu yeniden
açabilirsiniz. Cursor’ı veritabanınızdan tamamen kaldırmak için onu deaalocate etmelisiniz:

DEALLOCATE curAuthor

Eğer deallocate etmeyi başaramazsanız, cursor’ın içinde tanımlandığı prosedür sonlandığı zaman
cursor deallocate edilir.
Not: Bazı veritabanları (SQL Server gibi) global cursor deklare etmenize imkan tanır. Bunlar
veritabanı sisteminde çalışan bütün prosedürler için kullanılabilirdir. Eğer global bir cursor oluşturursanız
onu deallocate etmeyi unutmamanız gerekir (Oturumunuz kapatılmadıkça deallocate olmaz).

UNION’LAR OLUŞTURMAK
Bazen sonuçlar istediğiniz gibi dönmeyebilir. Örneğin Pubs veritabanında yazarlar authors
tablosunda, memurlar employee tablosunda tutulmuştur. Burada, bütün yazarların ve memurların bir
listesini almak isteyebilirsiniz. Bu listeyi almak çok kolay değildir. Mesela şu yapıyı kullanamayız:

SELECT fname, lname FROM authors, employee

Bu deyim hata üretir. İlk olarak, FROM’dan sonra birden çok tabloyu listeleyemezsiniz. Tabloları
birleştirmeniz gerekir. Fakat bu iki tablo, birleştirilmenin gerçekleşmesi için ortak alana sahip değiller.
İkinci olarak, ad ve soyad her iki tabloda da aynı adla belirtilmemiştir.  Employee tablosunda fname ve
lname olarak belirtilmişken authors tablosunda au_fname, au_lname olarak belirtilmiştir.
İstediğimiz kayıt kümesini oluşturmak için 2 SELECT deyimi kullanabiliriz:

SELECT fname, lname FROM employee

SELECT au_fname, au_lname FROM authors

Bu çözümle 2 liste oluşturmuş olduk. Eğer onların birleştirilmiş ve alfabetik olarak sıralanmış
olmasını istersek çok iş yapmamız gerekecektir (onları basıp, basılanı kesip, isimleri sıralayıp listeye geri
yapıştırmalı veya 2 kayıt kümesini almak ve birleştirmek için bir program yazmalıyız).
Bu işi yapmak için birden çok SQL deyimini bağlamaya izin veren bir operatör vardır. Bu
operatör UNION operatörüdür.
 
UNION OPERATÖRÜNÜ KULLANMAK
UNION operatörünü kullanmak çok basittir. İlk önce kayıtları seçecek olan SQL deyimlerini ayrı
bir yerde yazın ve onların veriyi doğru olarak çektiğinden emin olun. Sonra, UNION operatörünü
kullanarak iki deyimi birleştirin:
SELECT fname, lname FROM employee

UNION

SELECT au_fname, au_lname FROM authors

Bu deyim çalışınca iki kayıt kümesi çıkışta tek bir kayıt kümesi olarak belirir. Sütun isimleri ilk
deyimde gözüktüğü gibidir (fname, lname).

Eğer veriyi sıralamak isterseniz UNION’daki son sorguya ORDER BY ifadesini yerleştirmeniz gerekir:
SELECT fname, lname FROM employee

UNION

SELECT au_fname, au_lname FROM authors

ORDER BY lname

Tip: ORDER BY’ı union’larla kullanırken bu ifadeyi sadece son sorguya koymanız gerekir.
Başka bir yere koymak hata çıkmasına sebep olur.
 
UNION SINIRLAMALARI
UNION’lar bütün veritabanlarında veya aynı veritabanının geçmiş sürümlerinde aynı şekilde
uygulanamayabilirler. Örneğin SQL Server 7.0 bir view yaptığınız zaman UNION operatörü
kullanmanıza izin vermezken, SQL Server 2000 buna izin verir. Veritabanınızın her zaman UNION
operatörünü desteklemediğini görebilirsiniz.
Eğer UNION kullanıyorsanız şu sınırlamaları aklınızda tutmanız gerekir:
         Union’daki sütunlar, ilk sorguda kullanılan sütunlarla ilişkili olmalıdır.
         Değişik sorgularda referanslanan sütunların veri tiplerinin aynı olması gerekmez. Örneğin
fname için veri tipi varchar(50), au_fname için veri tipi char(10) olabilir. Veri tipleri karşılıklı
olarak uygun tipe çevrilir. (İkinci sorgudaki değişkenin tipi ilk sorgudaki değişkenin tipine
çevrilir, gerekirse kırpılır)
         Her bir sorguda aynı sırayla uygun sütunları referanslamalısınız. UNION, hangi sütunun
hangi sütunla eşleşeceğini yerlerine göre belirler. İlk sorgudaki ilk sütun ikinci sorgudaki ilk
sütunla, ilk sorgudaki ikinci sütun ikinci sorgudaki ikinci sütunla… eşleşir.
         Uygun düşen sütunlar değişik tipteyse onların veri tipi dönüşümünün gerçekleşebileceğinden
emin olmanız gerekir. Eğer veritabanınız o iki tip arasındaki dönüşümü desteklemezse
sorgularla bu dönüşümü desteklemeniz gerekir.
         Union’daki bütün sorgular SELECT ile FROM arasında aynı sayıda ifade içermelidir.
(“ifade” ile kastedilen, sütun ismi, aggregate fonksiyonlar, alt sorgular… SELECT deyiminin
yapısına göre kullanabileceğiniz bütün elemanlardır.)
         Eğer union’ın sonuçlarının bir tabloda kayıtlı olmasını isterseniz union’daki ilk sorguya
INTO ifadesini yerleştirmeniz gerekir.
         UNION operatörünü INSERT deyiminin yükleminde kullanabilirsiniz.
         ODER BY ve COMPUTE sadece union’ın son sorgusunda kullanılabilir.
         GROUP BY ve HAVING ifadeleri sadece kullanılmış olan sorguda uygulanabilirler ve
union’daki herhangi bir sorguda bulunabilirler.
         UNION olmayan sorgulardan faklı olarak, varsayılan dönüş seçeneği ALL yerine
DISTINCT’tir. Bu gerçekten şaşırtıcıdır. Diğer sorgularımızda bütün sütunlar dönerdi ve bazı
satırlar tekrarlanırdı, fakat UNION bu şekilde çalışmaz. Sorgunuzda ALL anahtar sözcüğünü
kullanmadıkça (UNION ALL şeklinde) tekrarlayan satırlardan sadece birisi dönecektir.
UNION’la ikiden çok tablodan da veriler çekilebilir:

SELECT CompanyName AS Name, Address, City, Country FROM Customers

UNION

SELECT CompanyName, Address, City, Country FROM Suppliers

UNION

SELECT FirstName+’ ‘+LastName, Address, City,  Country FROM Employees

Not: Veritabanında global bir nesne olarak kalıcı mevcudiyeti olan bir tabloda veriyi geçici
olarak tuttuğunuz zaman, çakışmanın olması muhtemeldir. Union’ların çok kullanıcılı ayarlarında bu
çakışma potansiyeli çok daha fazladır. Böyle bir çakışmanın olmamasının garanti edilmesinin en basit
yolu yerel geçici tablolardır. Sade kilitleme (locking) stratejileri bu gibi çakışmaları önlemeye yetmez.
Not: Union’larla çalışırken geçici tabloları yerel olarak oluşturursanız veriyi kullanmak ve
tabloyu silmede bir sorunla karşılaşmazsınız. Burada dikkat edilecek konu zamanlamadır. Union’daki
veriyi dondurmuş oluyorsunuz ve bu donmuş (değişiklikleri göstermeyen) veriyle çalışıyorsunuz. Ve
bunu yenilemeniz gerekir. Eğer yenilemeyi unutursanız zamanı geçmiş verilerle uğraşmış olabilirsiniz.
Eğer veritabanınız bir view’da union’ı destekliyorsa, onla çalıştığınız müddetçe veriyi güncelleme işini
idare etmesini view’a verebilirsiniz. Bununla beraber bütün veritabanları bir view’ı belirten sorguda union
operatörünü kullanmayı desteklemez.
 
 

KİLİTLER VE ÇAKIŞMA
Çakışma bütün veritabanı sistemleri için çok önemli bir konudur. Bu, hepsi de aynı anda aynı
nesneyle etkileşime çalışan iki ya da daha fazla kullanıcıyı belirtir. Bu etkileşimin niteliği her kullanıcı
için ayrı olabilir. Nesnelerin kontrolü için yarışı engellemenin ideal yolu, sadece tüm kullanıcıların soruda
ne yaptıklarına ve olayların ne kadar önemli olduğuna bağlı olarak değişir.
Veritabanıyla çakışmalı ilişkinin temelinde kilitleme işlemi vardır. Kilitler, o nesne üzerinde
çakışan iki işlemi önleme mekanizmasıdır. Bu, eğer başka birisi ilk olarak o nesneye erişmişse sizin o
nesne üzerinde çalışamayacağınız demektir. Ne yapıp ne yapamayacağınız başka birisinin ne yaptığına
bağlıdır. Bu ayrıca neyin yapıldığının belirtilmesidir. Böylece sistem ikinci işlemin ilk işlemle uyumlu
olup olmadığını bilir. Örneğin 1,2,10,100,1000 veya daha başka sayıda kullanıcı bağlantısını tutabilen
sistemler genellikle bütün hepsinde, aynı veri kısmını aynı anda sadece salt-okunur temelinde paylaşıma
açarlar. Şeffaf bir dükkanınız olduğunu düşünün. Bir çok kişinin aynı anda aynı şeye baktığını ve
almadığını varsayalım. Eğer birden fazla kişi aynı anda mal almaya kalkarlarsa karkaşa çıkabilir.
Dükkancı genellikle ilk önce kimin ele alınacağına karar verir.
SQL Server Lock Manager da bu “dükkancıdır”. SQL Server dükkanına girdiğiniz zaman lock
manager size amacınızın ne olduğunu sorar. Eğer sadece göz atmak derseniz, lock manager içeri
girmenize izin verir. Eğer satın almak (update veya delete) isterseniz lock manager orada herhangi birinin
olup olmadığını kontrol eder. Varsa, biraz beklemeniz gerekecektir. Sizin girmenize izin verilince de
başkaları bekleyecektir.
Mümkün çakışma problemleri ve her birini önleyecek ayırma seviyelerini inceleyelim:
 
KİLİTLERLE HANGİ ÇEŞİT PROBLEMLER ÖNLENEBİLİR?
4 tane büyük problem vardır: *Bozuk okumalar  *Tekrarlanamayan okumalar  *Aldanışlar 
*Kayıp güncellemeler.
Her biri ayrı problemler kümesini belirtir ve transaction isolation level’ın uygun ayarlarını içeren
çözümlerle de ele alınır.
Bozuk Okumalar: Bozuk okumalar, transaction henüz tamamlanmayan başka bir transaction’ın
bir parçası olan bir kayıdı okuduğu zaman ortaya çıkar. Eğer ilk transaction normal bir şekilde
tamamlanırsa muhtemelen problem olmaz. Fakat, eğer transaction geri alınırsa ne olur? Veritabanı
açısından hiçbir şeyin yapılmadığına dair bir bilgi alırsınız. Bir örnek inceleyelim:

Trans 1 Trans 2 Gerçek İşlenmemiş Trans2’nin


Komutu Komutu Veritabanı Veritabanı Gösterdiği
Değeri
Değeri

BEGIN TRAN   3    

UPDATE col = BEGIN TRAN 3 5  


5

SELECT SELECT @var = 3 5 5


anything col

ROLLBACK UPDATE 3   5
anything SET

  SET whatever      
= @var

Burada Transaction 2, geçerli olmayan bir değeri kullanmış oluyor. Eğer geri dönmeyi ve bu
sayının nereden geldiğini bulmaya çalışırsanız, hiçbir iz kalmadan ve büyük bir başağrısı ile bitirirsiniz.
Ama bu senaryo, SQL Server’ın varsayılan transaction isolation level’ını (READ COMMITTED)
kullandığınız zaman başınıza gelmez.
 
Tekrarlanamayan Okumalar: Tekrarlanamayan okuma,  bir transactiondaki kayıdı iki kez
okumaya çalıştığınızda ve bu aralıktaki veriyi ayrı bir transaction değiştirmeye çalıştığında meydana
gelir. Örnek verirsek:

Transaction 1 Transaction 2 @var Tablodaki Değer

BEGIN TRAN   NULL 125

SELECT @Var = value BEGIN TRAN 125 125


FROM table

IF @Var>=100 UPDATE value, SET   75


value=value-50

Update VALUE, SET END TRAN 125 75


value=value-100

Silmek için kilidi   125 -25 (No CHECK)


beklemeyi bitir ve 547 Error(CHECK)
devam et
Transcation 1, değerin geçerli olduğundan emin olmak için taranmıştır ve bu transaction çalışır.
Problem ise Transaction 1 güncelleme yapmadan önce Transaction 2’nin güncelleme yapması ve bundan
Transaction 1’in haberi olmamasıdır. Eğer tabloda negatif değeri önlemek için CHECK sınırlaması yoksa
gerçekten -25 değerine ayarlanacaktır.
Bu problemi iki yolla engelleyebiliriz:
* CHECK oluşturmak ve 547 hatası için kontrol etmek
* Isolation Level’ı REPEATABLE READ veya SERIALIZABLE’a ayarlamak.
 
Aldanışlar: Aldanış diyerek, yayınladığımız bir UPDATE veya DELETE deyimiyle
etkilenmemiş gizemli bir şekilde oluşmuş kayıtlardan bahsediyoruz. Bir örnek verirsek: Bir fast-food
dükkanında çalıştığınızı farz edin. Ve, devlet tarafından belirlenen asgari ücretle çalışan elemanlarınız
var. Devlet asgari ücreti saat başına $5.00 ‘dan $5.50 ‘a çıkartıyor. Bunun sizin için problem olmadığını
düşünüyor ve oldukça basit bir deyim yazıyorsunuz:

UPDATE Employees

SET HourlyRate = 5.50

WHERE HourlyRate < 5.50

ALTER TABLE Employees

   ADD ckWage CHECK (HourlyRate >= 5.50)

GO

Ve karşınıza şu hata çıkıyor:


Server: Msg 547, Level 16, State 1, Line 1
ALTER TABLE statement conflicted with COLUMN CHECK constraint ‘ckWage’. The conflict
occured in database ‘FastFood’, table ‘Employees’, column ‘HourlyRate’.
Bu yüzden hemen bir SELECT deyimi yazıyor ve $5.50’dan aşağı olanları listeliyorsunuz ve bir
tane kayıt buluyorsunuz. Fakat, siz daha önce UPDATE yapmıştınız ve gayet güzel çalışmamış mıydı?
Demek ki UPDATE’inizin çalıştığı zaman içinde başka birisi INSERT deyimiyle yeni kayıt eklemiş. Bu
problem için tek çare, trancation isolation level’ı SERIALIZABLE’a ayarlamaktır.
 
Kayıp Güncellemeler: Bu problemler, bir güncellemenin veritabanına başarıyla yazıldığı fakat
yanlışlıkla başka bir transaction’ın üzerine yazıldığı zaman ortaya çıkar. Bir örnek verirsek:
Kurumunuzda kredi analizcisisiniz. X adındaki kullanıcı size telefon açıyor ve $5000’lık limitine
ulaştığını ve artırmak istediğini belirtiyor. Siz de onun kullanıcı bilgilerine bakıyor ve parasını sürekli
olarak zamanında yatırdığını görüyor ve limitini artırmaya karar veriyorsunuz. Bu arada kredi
departmanındaki başka birisi de X kişisinin adresini değiştirmeye çalışıyor. Siz limiti $7500’a
çıkarıyorsunuz. Bu arada diğer kişi de adresini düzeltiyor ve ekranında henüz güncellenmemiş olan
$5000’lık şekliyle kayıdı güncelliyor. Böylece sizin yaptığınız limit artırma geçersiz oluyor.
Çözüm, başka bir bağlantının sizin veriyi okuyup güncelleyeceğiniz arada kayıdı güncellemiş
olduğunu her nasılsa tanımaya bağlıdır. Bu tanıma kullandığınız erişim metodlarına göre değişir:
ADO kullanıyorsanız, ADO’da bunun için ek hiçbir şey yapmanıza gerek yoktur. ADO,
güncellenmiş olan bir kayıdı güncellemenize izin vermez.
Diğer Erişim Metodlarını kullanıyorsanız en yaygın metod, timestamp veri tipini kullanmaktır.
Hatırlarsanız timestamp server’ın çalışmasına bağlı bir sayıdır ve verilen her veritabanından ayrı olacağı
garantidir. Verinizin geri kalan kısmındaki timestamp’i okuyun ve güncellemeden önce o değerin
değiştirilmediğinden emin olun. Eğer değiştirilmişse başka birisi o kayıtta bir güncelleme yaptı demektir
ve güncellemenizi iptal etmeniz gerekir.
 
KİLİTLENEBİLİR KAYNAKLAR
SQL Server için 6 çeşit kilitlenebilir kaynak vardır:
Veritabanı: Bütün veritabanı kilitlidir. Bu, genellikle veritabanı şeması değiştirilirken yapılır.
Tablo: Bütün tablo kilitlidir. O tablo ile ilgili nesnelere bağlı bütün verileri içerir (bütün satırlar,
kayıtlar, index’ler…)
Extent: Bütün extent kilitlidir. Hatırlarsanız extent sekiz sayfadan oluşur. Bu yüzden extent kilidi
extent’i, sekiz veri veya index sayfasını ve bu sekiz sayfadaki bütün veri sütunlarını kontrol eder.
Sayfa: Bu sayfadaki bütün veriler veya index key’leri kilitlidir.
Key: Index’teki bir kısım veya bir dizi key’ler üzerindeki kilittir. Aynı index sayfasındaki diğer
key’ler etkilenmemiş olabilir.
Satır veya Satır Belirteci (RID): Kilit teknik olarak satır belirtecine konmasına rağmen aslında
bütün sütunlar kilitlenir.
 
KİLİT ŞEKİLLERİ
Kilitlemek için değişik kaynaklar olduğu gibi çeşitli kilit şekilleri de vardır. Tek şekiller
diğerlerinden ayrıdır (beraber çalışamazlar). Bazı şekiller aslında diğer şekilleri değiştirmekten başka
hiçbir şey yapmaz. Şekillerin birbirleriyle çalışması onların uyumluluğuna bağlıdır. Aşağıdaki tablo
uyumlulukları göstermektedir:
  IS S U IX SIX X

Intent Shared (IS) Evet Evet Evet Evet Evet Hayır

Shared (S) Evet Evet Evet Hayır Hayır Hayır

Update (U) Evet Evet Hayır Hayır Hayır Hayır

Intent Exclusive (IX) Evet Hayır Hayır Evet Hayır Hayır

Shared with Intent Evet Hayır Hayır Hayır Hayır Hayır


Exclusive (SIX)

Exclusive (X) Hayır Hayır Hayır Hayır Hayır Hayır

Ayrıca:
* Schema stability lock (Sch-S), schema modification lock (Sch-M) haricindeki bütün kilitlerle
uyumludur.
* Sch-M bütün kilitlerle uyumsuzdur.
* Bulk update (BU) sadece schema stability ve diğer bulk update kilitleriyle uyumludur.
Shared: Bu şekil, en temel kilitleme şeklidir. Paylaşılmış bir kilit sadece veriyi okumanız
gerektiğinde (hiçbir şey değiştiremezsiniz) kullanılır. Paylaşılmış bir kilit diğer paylaşılmış kilitlerle
uyumludur.
Paylaşılmış kilitler diğer kilitlere oranın dışında olduğunuzu anlatır. Anlamsız olsa da henüz onu
gözardı edemezsiniz.
Update: Update kilitleri, shared kilitleri ve exclusive kilitleri arasında bir melez yapıdır. Update
kilitleri sadece shared kilitleriyle uyumludur ve shared kilitlerine eğilimlidirler. Yani daha önceden
üzerinde bir update kilidi olan kaynağa update kilidi yerleştiremezsiniz.
Bir update kilidi, yer tutmanın özel bir çeşididir. Bir UPDATE yapmak için sadece hangi satırları
güncelleyeceğinizi hesaplayacağınız WHERE cümleciğinizi (bir tane olduğunu farz edersek) tasdik
etmeye ihtiyacınız vardır. Yani, gerçekten fiziksel güncelleme yapıncaya kadar sadece bir shared kilidine
ihtiyacınız vardır. Fiziksel güncelleme sırasında, bir exclusive kilide ihtiyacınız olacak.
Update kilitleri, tam olarak neyin güncelleneceğini hesaplamak için verinin başlangıç taramasını
yaptıktan sonra bir exclusive kilidi olacak bir shared kilidine sahip olduğunuzu gösterir. Bu, bir
güncelleme için 2 ayrı aşamanın olduğu gerçeğini onaylar: İlki, WHERE cümleciği kriteriyle neyin
buluşacağını neyin güncelleneceğini hesaplayacağınız aşaması (Bu, bir update kilidine sahip bir update
sorgusunun parçasıdır). İkincisi, update kilidinin bir exclusive kilidine gerçekten dönüşüp
dönüşmeyeceğine karar verme aşamasıdır. Diğer durumda, kilit bir shared kilidine dönüşür. Bu,
deadlock’ın bir çeşidine karşı bir engel oluşturması açısından çok iyidir. Deadlock bir kilit çeşidi değildir,
bir çelişkinin oluştuğu bir durumdur. Bir kilit, başka bir kilit kaynağı tuttuğu için bir şey yapamaz.
(Problem, kaynağın ilk transaction’da temizlemek (clear) için kilit bekliyor olarak saplantıda olmasıdır)
Update kilitleri olmazsa bu problem beklenmedik bir anda ortaya çıkabilir. 2 update sorgusu
shared mod’da koşuluyor olacak. A sorgusu sorgusunu tamamlar ve fiziksel güncelleme için hazırdır. Bir
exclusive kilidine dönüşmek ister, fakat olamaz. Çünkü B sorgusu sorgusunu bitiriyordur. B sorgusu o
zaman, (fiziksel güncelleme yapma ihtiyacı haricinde) sorgusunu bitirdi. Bunu yapmak için, B sorgusu bir
exclusive kilidine dönüşmelidir. Fakat dönüşemez. Çünkü A sorgusu halen bekliyordur. Bu bir çıkmaz
veya bir deadlock oluşturur.
Bunun yerine bir update kilidi diğer update kilitlerini kurulmuş olmadan önler. 2. transaction bir
update kilidini başarmaya çalıştığı zaman, onlar bir bekleme durumuna koyulacaklar, kilit kabul
edilmeyecek. Eğer ilk kilit, kilit timeout’una ulaşmadan önce temizlenirse, o zaman kilit yeni istekler için
kabul edilecek ve işlem devam edilebilecektir.Eğer öyle olmazsa bir hata üretilecektir.
Exclusive: Exclusive kilitleri tam olarak isminden anlaşıldıkları gibidirler. Exclusive kilitleri
diğer hiçbir kilitle uyumlu değildirler. Eğer diğer kilitler mevcutsa ulaşılamazlar. Exclusive kilidi hâlâ
aktif olduğunda kaynak üzerinde oluşturulması için yeni bir kilit çeşidine izin vermezler. Bu, 2 kişinin
aynı anda güncellemesini, silmesini… önler.
Intent: Intent kilidi, gerçek bir yer tutacağıdır. Bu, nesne hiyerarşisi konusunun başarılması
anlamına gelmektedir. Bir satırda bir kilit kurulmuş olduğu bir durumu düşünün, fakat birisi bir sayfada
bir kilit kurmak istiyor veya bir tabloyu değiştirmek istiyor.
Intent kilidi olmadan, yüksek seviyeli nesne sizin daha düşük seviyede bir kilide sahip
olduğunuzu asla bilemez. Intent kilitleri performansı arttırır. (SQL Server, bir işlemin tüm tabloyu
güvenli olarak kilitleyip kilitleyemeyeceğini hesaplamak için sadece tablo seviyesinde intent lock’larını
incelemeye ihtiyaç duyar ve tablodaki her satır veya sayfa kilidini kontrol etmez)
Intent kilitlerinin 3 farklı çeşidi vardır:
Intent Shared: Bir shared kilidi,  hiyerarşide bazı düşük noktalarda sağlanır. Örneğin bir sayfada
satır seviyesinde shared lock uygulanmış olsun. Bu çeşit kilit sadece tablolara ve sayfalara uygulanır.
Intent Exclusive: Düşük seviye öğesinde yer alacak bir exclusive kilidi hariç Intent Shared
gibidir.
Shared with Intent Exclusive: Bir shared lock, düşük nesne hiyerarşisini kurar. Fakat intent veriyi
değiştirmek içindir. Böylece bazı noktalarda o, bir intent exclusive olacaktır.
Schema: 2 çeşittir:
Sch-M: Bir plan değişikliği nesneye uygulanır. Sch-M kilidi süresince bu nesneye karşı hiçbir
sorgu, CREATE, ALTER, DROP deyimleri çalıştırılamaz.
Sch-S: Bu kilidin tek amacı, nesnede aktif olan CREATE, ALTER, DROP ve diğer sorgulara
Sch-M kilidi sağlamaktır. Diğer bütün kilitlerle uyumludur.
Bulk Load: Bu kilit, tablo kilidinin değişik bir biçimidir. Tek önemli fark, tablo kilidinin
cause/source ‘una işarettir (BCP aracı veya DTS tabloya veriyi bulk copy yapar).
 
ÖZEL BİR KİLİT TİPİ BELİRTMEK
Bazen sorgunuzda veya bütün transaction’ınızda kilitlemeyle ilgili daha fazla kontrol
isteyebilirsiniz. Bunu “optimizer hint” leri kullanarak yapabilirsiniz. Optimizer hint, SQL Server’a bir
kilidi özel bir seviyeye yükseltmesini açıkça anlatmanın bir yoludur.

Hint Açıklama

SERIALIZABLE / Transaction’daki bir deyimle bir kilit kuruldu mu, o kilit transaction bitmeden
HOLDLOCK (ROLLBACK veya COMMIT ile) o kilit serbest bırakılmaz. Eğer eklenen kayıt, kilidi
kuran sorgudaki WHERE ifadesindeki kriteri de sağlıyorsa eklemeler de önlenmiştir.
Bu en yüksek ayırma seviyesidir ve verinin tam kararlılığını garanti eder.

READUNCOMMITTED Hiçbir kilit sağlamaz (shared lock bile) ve diğer kilitleri kabul etmez. Çok hızlı
/ NOLOCK olmasına rağmen bozuk okumalar gibi problemlere sebep olabilmektedir.

READCOMMITTED Varsayılandır. Bütün kilitleri kabul eder. Ayırma seviyesindeki READ COMMITTED
ile aynı şekilde çalışır.

REPEATABLEREAD Transaction’daki bir deyim tarafından bir kilit kuruldu mu, o kilit transaction bitinceye
kadar serbest bırakılmaz. Bununla beraber, yeni veri eklenebilir.

READPAST Silmek (clear) için bir kilidi beklemektense bütün kilitli satırları atlar. Atlama satır
kilitleriyle sınırlandırılmıştır (hâlâ sayfa, extent ve tablo kilitleri için bekler) ve
yalnızca SELECT deyimiyle kullanılabilir.

ROWLOCK SQL Server 6.5 için, ekelemeler için sayfa kilitleri yerine düşük seviye kilitlemeyi
kullanacağını belirtir. 7.0 için ise, optimizer sahip olacak olsa bile kilidin ilk seviyesini
satır seviyesinde olmaya zorlar, aksi halde daha az parçalı kilitleme stratejisi seçilir.
Eğer kilitlerin sayısı sistemin kilit üst sınırına erişmişse kilidin daha az parçalı
seviyeye yükselmesini önlemez.

TABLOCK Tüm tabloyu kilide zorlar. Gerçekten, bilinen tablonun tarama durumunu hızlandırır,
fakat, eğer diğer kullanıcılar tablodaki veriyi değiştirmek isterlerse büyük rekabet
problemleri oluşturur.

TABLOCKX TABLOCK’a benzerdir fakat exclusive lock oluşturur (Tablonun dışındaki bütün diğer
kullanıcıları deyim veya TRANSACTION ISOLATION LEVEL’ın nasıl
ayarlandığına bağlı olarak transaction süresince kilitler).

UPDLOCK Shared lock yerine update lock kullanılır. Bütün kullanıcıların shared lock edinmesine
izin verir. Fakat, deyim veya transaction bitinceye kadar hiçbir veri değişikliği
yapılmamasını sağlar.
Bunları kullanmak çok basittir (tablo isminden veya alias’tan sonra eklenir). Yapı şöyledir:
FROM <table name> [AS <alias>] [[WITH] (<hint>)]

Örnekler verirsek:
SELECT * FROM Orders AS ord WITH (TABLOCKX)

SELECT * FROM Orders WITH (TABLOCKX)

SELECT * FROM Orders (TABLOCKX)

Bu örneklerin hepsi de aynı işi yaparlar. Birden çok tablolu bir örnek incelersek:

SELECT * FROM Orders AS ord WITH (TABLOCKX) JOIN [Order Details] AS od ON


ord.OrderID = od.OrderID

Burada Orders tablosu exclusive kilide zorlanır.Fakat, Order Details tablosuna hiçbir çeşit özel
kilit yerleştirilmez (SQL Server lock manager hâlâ bu tablonun tam kontrolünü elinde tutmaktadır).

ALT SORGU OLUŞTURMAK


Bazen tek bir sorgu yeterli olmayabilir.
Bir alt sorgu, sorgu içindeki bir sorgudur. Bir SELECT deyiminde, başka bir tablodan bir veya iki
parça almak için başka bir SELECT deyimi içerebilirsiniz. İlişkili tablolardaki bazı tipteki bilgileri JOIN
kullanarak çekebilirsiniz. Bununla beraber bazı zamanlar join’ler, yönetmek için çok karışık hale gelebilir
veya join yapmak için çekmek istediğiniz bölgeler arasında paylaşım olmayabilir. Bu durumlarda alt
sorgular daha etkili olmaktadır.

Örneğin, Pubs veritabanındaki her bir başlığı, fiyatını, ve bazı satış karşılaştırma verisini listeleyen bir
rapor oluşturmak, her başlık için ortalama fiyatı göstermek isteyebilirsiniz. Bunu yapmak için aşağıdaki
kodu yazmanız yanlış olur:
SELECT title, price, AVG(price) FROM titles

Veritabanınız title ve price’ın aggregate fonksiyon veya GROUP BY ifadesi içermemesinden


dolayı hata verir. Aşağıdaki örnekte de istediğiniz ortalamayı elde edememiş olursunuz:
SELECT title, price, AVG(price) AS AveragePrice FROM titles GROUP BY title
price

GROUP BY deyimi, ortalama fiyatın başlık grubuna göre hesaplanmasına neden olur. Sonuç
olarak price ile average price’ın her ikisi de aynı değere sahip olmuş olur.
Bu problemleri gidermek için bir alt sorgu (subquery) kullanmalısınız:

SELECT title, price, (SELECT AVG (price) FROM titles) AS AveragePrice FROM
titles

Bu sorguyu çalıştırdığınız zaman doğru ortalama değerini elde edersiniz. Burada alt sorgu,
yalnızca grup için hesaplanan aggregate fonksiyonlarını ana sorguda kullanmaya zorlayan GROUP BY
deyimini içerme ihtiyacını ortadan kaldırır.
Alt sorguların, SQL sorguları yazmada önemli bir yeri vardır. Akılda tutmanız gereken tek kural
ise alt sorguların parantez içinde yazılacağıdır. Sonuçlar bir kayıt kümesinde ortaya çıkacağı zaman,
sonuçlara AS kullanarak iyi bir isim vermelisiniz.
 
BİR ÖĞE SEÇMEK
Asıl sorguda içerilmesi için öğe alan alt sorgular kesin kurallara uymalıdır. Alt sorguların
parantez içinde yazılması gerekir (böylece SQL çözümleyici, ona ayrı varlık olarak davranır). Ayrıca
parantezlerin içinde, herhangi bir verilmiş sade sorguya uygulanacak kurallara da uymalıdır. Eğer
gruplarsanız sorgu da gruplamaya uymalıdır. Tipik olarak seçilmiş sonuçları isimlendirmek için AS
kullanırsınız. Böylece sonuçları programınızda işleyebilirsiniz. Bununla beraber bu gibi alt sorgular tek
bir değer döndürmelidir. Aksi halde hata mesajı alırsınız. Tam merkezil (superordinate) bir sorguya dahil
etmek için bir öğe alıyorken, SELECT deyiminiz sadece ve sadece bir değer döndürmelidir. SELECT
deyiminiz birden çok değer döndürebileceği durumlarda, sonuçları tek bir değere sınırlamak için
DISTINCT veya TOP anahtar kelimelerini kullanmalısınız. Sorgu kendi kendini tıpki sütun ismiymiş gibi
tam merkezil sorgu tarafından seçilen öğeler listesine yerleştirir.
Eğer, alt sorgunun içindekileri ana sorgudaki bazı değerlere ilişkilendirmek istiyorsanız alt
sorguda WHERE ifadesini kullanmanız gerekir. WHERE ifadesindeki bir koşul, alt sorgudaki bir sütun
değerini ana sorgudaki bir sütun değerine eşitlemelidir. Aşağıdaki örnek, öğrencilerin listesi ile onların
kullandıkları, portfolio tablosundaki evrak çantalarının sayısını döndürür:

SELECT First_Name, Last_Name, (SELECT COUNT(Portfolio_ID) FROM Portfolio WHERE


Portfolio.Student_ID = Student.Student_ID) AS PortfolioCount FROM Student

 
KOŞULLARI AYARLAMAK
Koşulları ayarlamak için alt sorguları WHERE veya HAVING ifadesi içinde de kullanabilirsiniz.
Bu durumda 3 seçenek vardır. İfadenin, değer döndüren bir ilişkiye sahip sabit bir değer mi yoksa ana
sorgudaki bir sütun mu olduğunu görmek için kontrol edeceği, yalnızca ve yalnızca tek değer dönüren alt
sorgu kullanabilirsiniz. Ana sorgudaki sütun değerleriyle ilişkili alt sorgudan dönen değeri kontrol etmek
için ilişkisel operatör kullanabilirsiniz:

SELECT First_Name, Last_Name FROM Student WHERE (SELECT COUNT (Portfolio_ID)


FROM Portfolio WHERE Portfolio.Student_ID=Student.Student_ID)>0

Bu sorgu, evrak çantası bulunan öğrencilerin isimlerini döndürür.  Alt sorgu, her bir öğrenci için
evrak çantası sayısını döndürür. Sonuç olarak alt sorgu ana sorguyla ilişkilidir, bu yüzden kayıt
kümesindeki her bir kayıt, sadece ve sadece bir öğrenciyle ilişkili olarak döndürülür.
Koşulu belirlemek için IN anahtar sözcüğünü de kullanabilirsiniz. Örnek olarak, aktif evrak
çantasına sahip öğrencilerin bir listesini almayı düşünün. Burada dikkat etmeniz gereken, sistemde evrak
çantasına sahip olan, ana sorgu tarafından döndürülen öğrencilerden emin olmanız gerektiğidir. Portfolio
tablosundaki bütün Student_ID’lerin bir listesini döndürmek için alt sorgu kullanabilirsiniz. Bu kayıt
kümesini aşağıdaki biçimde, uygun koşulu ayarlamak için kullanabilirsiniz:

SELECT First_Name, Last_Name FROM Student WHERE Student.Student_ID IN (SELECT


Student_ID FROM Portfolio)

Bu sorgunun HAVING ifadesi kullanılmış şekli şöyle olur:

SELECT First_Name, Last_Name FROM Student GROUP BY Last_Name, First_Name,


Student_ID HAVING Student.Student_ID IN (Student_ID FROM Portfolio)

Burada, aggregate fonksiyonumuz olmadığı için sorgulamada kullandığımız bütün sütınları


GROUP BY ifadesinin içine koymalıyız. Buradan çıkaracağımız sonuç ise alt sorguların WHERE ile
çalıştığı kadar HAVING ile de iyi çalıştığıdır.
Ayrıca, koşulları ayarlamak için, alt sorguları EXISTS ile de kullanabilirsiniz. EXISTS ile
çalıştığınız zaman aklınıza alt sorgunun hiçbir kayıt kümesi döndürüp döndürmediği sorusu gelir.
Kayıtların sayısı değil, sadece kayıdın var olup olmadığı önemlidir. Dolayısıyla bu bölümdeki ilk örnek
şu şekle çevrildi:

SELECT First_Name, Last_Name FROM Student WHERE EXISTS (SELECT Portfolio_ID


FROM Portfolio WHERE Portfolio.Student_ID = Student.Student_ID)
Net sonuç, Portfolio_ID’lerin  sayısını araştırdığımız ve 0’dan büyüklüğünü kontrol ettiğimiz ilk
sorguyla aynıdır. Burada biz sadece Portfolio_ID’nin var olup olmadığını sorguladık. Eğer varsa,
öğrencinin ismini ana sorguyla döndürülen listeye ekledik.
Not: Alt sorguyla döndürülen her değerle ilişkiye sahip bir sütun mu yoksa bir sabit mi oluğunu
kontrol etmek için ANY anahtar sözcüğünü bir alt sorguyla kullanabilirsiniz. Alt sorguyla döndürülen
herhangi bir tek değerle ilişkiye sahip bir sütun mu ya da bir sabit mi oluğunu kontrol etmek için ise ALL
anahtar kelimesini bir alt sorguyla kullanabilirsiniz.
 
JOIN’LERİN YERİNİ ALMAK
Alt sorgular karışık join’lerin yerine geçebilir. (EXIST veya IN kullanılarak)
Son örneği ele alırsak, bunu join kullanarak da şu şekilde gerçekleştirebiliriz:

SELECT First_Name, Last_Name FROM Student INNER JOIN Portfolio ON


Portfolio.Student_ID = Sudent.Student_ID

Bu, daha sade gözükmektedir. Fakat, JOIN mantığının içinde sorgunun icrasının nasıl
gerçekleştirildiğine dikkat edin. Student_ID’nin her iki tabloda da bulunması gerektiğinden aktif evrak
çantasına sahip olan öğrencileri listemize ekleyen doğal bir filtre elde etmiş olduk.
JOIN’lerle (özellikle içi içe kullanılan) alt sorgular arasında bir tercih yapmanız gerekirse
(JOIN’lerin mantığı daha karışık olduğu için) alt sorguları seçmeniz daha uygundur.
Tip: Bazen (özellikle saniyede bir çok işlem yapan büyük veri tabanlarında) join’i kullanmakla
alt sorguları kullanma arasındaki performans farkını bilmek istersiniz. Alt sorgular, join’lere göre
index’leri kullanmada daha az etkilidir. Eğer performans önemliyse en hızlı seçeneği seçmeniz gerekir.
 
SİLME, GÜNCELLEME ve EKLEME
Alt sorgular diğer SQL deyimleriyle de kullanılabilir. Örneğin bir DELETE deyiminde alt
sorguyu, hangi kayıtların silineceğini belirlemek için WHERE ifadesinin içinde kullanabilirsiniz:

DELETE FROM Student WHERE NOT EXISTS (SELECT Portfolio_ID FROM Portfolio WHERE
Portfolio.Student_ID = Student.Student_ID)

Bir UPDATE deyimi de WHERE ifadesinde alt sorgu kullanabilir:

UPDATE Student SET First_Name = UPPER (First_Name), Last_Name = UPPER


(Last_Name) WHERE NOT EXISTS (SELECT Portfolio_ID FROM Portfolio WHERE
Portfolio.Student_ID = Student.Student_ID)

Not: Ayrıca, sütunu güncelleştirmek için bir veri elde etmede UPDATE içinde bir alt sorgu
kullanabilirsiniz. Alt sorgu, ayarlanacak (SET) sütunların listesinde eşittir işaretinin sağına yazılabilir.
INSERT deyimi biraz farklı çalışır. Alt sorguyu isterseniz koşulu belirtmek için WHERE
ifadesinde kullanabilirsiniz. Alt sorgunun daha tipik kullanımı ise, eklenecek değerlerin bir listesini
sağlamadaki kullanımıdır:

INSERT INTO Temptable (First_Name, Last_Name, PortfolioCount) SELECT


First_Name, Last_Name, (SELECT COUNT (Portfolio_ID) FROM Portfolio WHERE
Portfolio.Student_ID = Student.Student_ID) AS PortfolioCount FROM Student

Burada, her bir öğrenci için evrak çantası sayısını elde etmek için JOIN ve GROUP BY da
kullanabiliriz:

INSERT INTO Temptable (First_Name, Last_Name, PortfolioCount) SELECT


First_Name, Last_Name, COUNT (Portfolio_ID) FROM Student INNER JOIN Portfolio
ON Portfolio.Student_ID = Student.Student_Id GROUP BY First_Name, Last_Name

Bunun gibi durumlarda, sorgu arkasındaki mantığı size en doğal şekilde ifade eden sorgu çeşidini
kullanmalısınız.
Not: Alt sorgudaki bir gruplamanın ana sorgudaki gruplamaya tesir etmesini önlemek için alt
sorgudaki değerlerin, ana sorgudaki kritik gruplamayı belirlemediğinden emin olmanız gerekir.
 
 

VIEW OLUŞTURMAK
Şimdiye kadar veritabanı tablosunu bilgi depolamada birincil ünite olarak kabul etmiştik.
Tablolar, bireysel veri öğeleri için saklama yerini belirten satır ve sütunlara sahiptirler. Tabloda saklanan
veriyi gözden geçirmek isterseniz tabloyu sorgularsınız. Birden çok tablodaki verileri görmek isterseniz
sorgularınızda join kullanırsınız.
Yalnız tablolara güvenmek uygun metoddur. Bununla beraber tabloya göz atması gereken
kullanıcılarınız olduğunda, az miktarda endişelenebilirsiniz. Uzun zaman bir iş üzerinde çalışan bütün
veritabanı yöneticilerinin, (birisinin tabloya bakarken hata yapmasından dolayı) hiç olmazsa en az bir tane
yedek banda başvurma hikayesi vardır. Böyle hatalara karşı korumanız, veritablosuna direk erişildiğinde
kullanmak için sınırlı haklara, güncelleme ve silme haklarına sahip özel bir login’i kullanıcılara
sağlamaktır. Bu yaklaşımın en belirgin sınırlaması ise çoğu kullanıcıların doğru kimlik ile logout ve
logback yapmadaki tembelliklerinden dolayı tabloya direk erişimin sınırlanmasıdır.
 Bir view bir tabloya benzer. Fakat veritabanında fiziksel bir mevcudiyeti yoktur. Bir view, bir
kullanıcının ulaşma isteği gerektiğinde oluşturulur. Sonuç olarak bir kullanıcı view’a erişince o andaki
verilere göre tablo yapısını görür. View’lar, oluşturma, yaşatma ve işi bitince silme gereksinimi olmayan
geçici tablolar gibidir. View, tablo yapısının temelini oluşturduğu için view’da güvenliği
ayarlayabilirsiniz. Veriye, (gözden geçirmek için) salt okunur erişimi garanti altına almak istiyorsanız,
view’ı salt okunur olarak belirtebilirsiniz. Burada kimin kullandığı önemli değildir, view, doğasında salt
okunur olarak kalır. Özel login kullanılacağından endişelenmenize gerek yoktur.
Not: View’lar, tablolar gibi veritabanında kalıcı fiziksel mevcudiyeti olmadığı için genellikle
sanal tablolar olarak adlandırılır.
 
VIEW’IN KULLANIMINI DÜZENLEMEK
View’lar bazı sınırlamalara sahiptir. İlk olarak, onlara ulaşıldığında isteğe bağlı olarak
oluşturulurlar. View oluşturma işlemi, view’a erişen sorgu için sorgu icra planına birleştirilmiş olmalıdır.
Sonuç olarak, benzer sorguların koşulmasını karşılaştırdığımız zaman, view’a erişen sorgu icra planının
biraz daha fazla yapacağı iş vardır. İkinci olarak, her ne zaman bir view bir view’a erişse artırma için
performans gecikmesi beklersiniz. Query optimizer en iyi icra planını kurmada çok iyi olsa bile plan,
view#2’nin sonuçları görüntülemesinden önce view#1’in görüntülemesi gerektiği gerçeği ile
bağdaşmalıdır. 2. view’da veri döndürülebilmesi için ilk view’da sorgunun icrasındaki bağımlılık bir dizi
olay kümesini icra planına dahil eder. Bazı örneklerde gecikmeler bir dizi problem oluşturur.
View’lar arası bağımlılıklar, query optimizer’ın çözmesi için ilginç problemler oluşturur. Kendi
kendilerine bağımlılık kalıtsal bir problem değildir. Bununla beraber, bağımlılıklar belirli öz yinelemeyi
icra planına soktuğu zaman gecikmeler olur. Şunu anlamalısınız ki: her veritabanının query optimizer’ı
bir programcı olarak size kara kutu niteliğindedir. Önceden ne olacağını kestirmek için iyi
donatılmadığınızdan sadece view’ları ve sorguları dikkatlice planlayabilir ve onların performansını
dikkatlice test edebilirsiniz.
Bunun gibi muhtemel problemlerle view’lar tehlikeli gözükebilir. Yine de view’lar gerçekte çok
kullanışlıdırlar. İhtiyacınız olan sadece onların gizli tehlikelerinin farkında olmak ve onları gereği gibi
nasıl yönetileceğini bilmektir. İlk olarak avantajları düşünelim:
         Temel sorgu join içerebileceği için birden çok tablodan veri gösterebilirsiniz.
         Kolayca veriye salt-okunur erişim sağlayabilirsiniz.
         Bir view kullanarak tablodaki değerlere göz atabilirsiniz. Ardından da, o değerlerin başka bir
sorguyla ilgili olup olmadığını doğrulamak için IN kullanabilirsiniz.
         View kullanarak değerlere göz atabilirsiniz ve bir sorgu içinde o değerleri içermek için view
ile bir join de kullanabilirsiniz.
View’ları kullanırken birkaç pratik önlemi almanız gerekir. View’ların tablo ile veritabanı
kullanıcısı arasına bir ara katman soktuğunu hatırlamanız gerekir. Bu ara katman tablolarınız için koruma
sağlar, bununla beraber performanstan da yoksun bırakır. Bu yüzden şu uyarıları dikkate almanız gerekir:
         Mümkün olduğu kadar basit bir view belirten sorguyu tutun. Eğer sorgunun çalışması uzun
zaman alıyorsa, view’ınız kullanılmadan önce gerçekleşmek için uzun zaman alacaktır.
         View’ları kullanan view’ları kullanan view’lardan kaçının. Maalesef, view’ları referanslayan
view’lar genellikle çok kullanışlıdırlar.
         View’ları test ve optimize edin. Yavaş işleyen bir view’ı tespit etmeden, acele ederek
oluşturduğunuz zaman oluşabilecek büyük süprizlerden kaçının.
         View’ları bir tablodaki, (performans düştüğünde view’ın sonuçlarını tanımlayabilen) küçük
alanlarla birleştirin.
 
 
VIEW OLUŞTURMAK
View oluşturmak için CREATE VIEW deyimini kullanmanız gerekir. Yapısı şöyledir: CREATE
VIEW tabloadi [(sütun1, sütun2, …)] AS sorgu [WITH CHECK OPTION]
Bu deyim büyük ölçüde INSERT INTO’ya benzemektedir. VIEW anahtar sözcüğüden sonra
view’ınıza bir isim verirsiniz. Sonuç tablosunda kullanmak için sütun isimleri listesi içerebilirsiniz. Eğer
isimler içermezseniz, temel tablodaki sütun isimleri kullanılacaktır. Query ise, çoktan oluşturduğunuz ve
test ettiğiniz SELECT deyimidir.
Geri kalan yapı elemanları sadece güncellenebilir olmasını istediğiniz view’lara uygulanır.
View’ınızın güncellenebilir olması için veritabanınızın güvenlik düzenini kullanarak ona güncelleme izni
vermeniz gerekir. SQL, birden çok tabloda çapraz güncellemeye izin vermediği için, view sadece ve
sadece bir tabloya dayandırılmalıdır. (Bazı veritabanları izin verebilir) View’ınızın güncellenebilir
olduğunu farz ediniz. WITH CHECK OPTION, view’ı belirten sorgudaki WHERE koşullarını eşlemek
için güncellemeyi zorlar.
Aşağıdaki yolla bir view belirtebiliriz:

CREATE VIEW StudentPortfolio AS SELECT * FROM portfolio WHERE StudentID IS NOT


NULL

StudentPortfolio view’ı, değer içeren StudentID sütununa sahip kayıtların bir listesini içerir. Eğer
view’ı güncellenebilir yaparsak view’da şu güncellemeyi de yapabiliriz:

UPDATE StudentPortfolio SET StudentID=NULL

Bu güncelleme çalışır ve view’dan bütün kayıtları siler. (Kayıtların hiç biri artık view’ı belirten
WHERE koşuluna uygun olmayacaktır.) Biz daha çok güncelleme izni vermeyi tercih ederiz, fakat
view’dan bir kayıt silecek olan güncellemenin çalışmamasını isteriz. Aşağıdaki yöntemle bir view
belirttiğimizi düşünelim:

CREATE VIEW StudentPortfolio AS SELECT * FROM Portfolio WHERE StudentID IS NOT


NULL WITH CHECK OPTION

Güncellemeye çalıştığınız zaman SQL güncellemeyi kontrol eder ve WHERE koşulları


bozulmuşsa (view’dan kayıt silmeye çalışırsanız) hata mesajı verir. WITH CHECK OPTION kullanarak
view’ı güncelleyerek view’dan hiçbir kayıt silemeyeceğimizden emin oluyoruz. Ayrıca, view’da
olamayacak bir satır ekleyemezsiniz ve view’ın ölçüsüne uymayan bir yolla kayıdı güncelleyemezsiniz.
Tip: DROP VIEW view_adi yapısını kullanarak view’ı silebilirsiniz.
Tip: View’da kullanılan yapı elemanlarında kullandığınız, veritabanına göre değişen bazı
kısıtlamalar vardır. Genellikle CASE ile UNION’lara izin verilmez. Ayrıca JOIN kullanımında da limitle
karşılaşabilirsiniz.
SQL Server’ın Northwind veritabanına uygulanabilecek daha karışık bir örnek incelersek:

CREATE VIEW YesterdaysOrders_vw AS SELECT cu.CompanyName, o.OrderID,


o.OrderDate, od.ProductID, p.ProductName, od.Quantity, od.UnitPrice,
od.Quantity*od.UnitPrice AS ExtendedPrice FROM Customers AS cu INNER JOIN
Orders AS o ON cu.CustomersID = o.CustomersID INNER JOIN [Order Details] AS od
ON o.OrderID = od.OrderID INNER JOIN Products AS p ON od.ProductID =
p.ProductID WHERE CONVERT (varchar(12), o.OrderDate, 101) = CONVERT
(varchar(12), DATEADD (day, -1, GETDATE(), 101))

Bu örnekte birden çok tablodan veriler JOIN’lerle birleştirilerek (ve adlandırılarak) alınıyor ve WHERE
koşuluyla sipariş tarihi dün olan kayıtlar seçiliyor.
 
VIEW’LARI ŞİFRELEME
View’ınızı şifrelemek için bütün yapmanız gereken WITH ENCRYPTION seçeneğini
kullanmaktır. Burada dikkat etmeniz gereken husus, WITH ENCRYPTION ifadesini view’ın isminden
sonra, AS ifadesinden önce kullanmak ve WITH ENCRYPTION ile OPTION anahtar sözcüğünü
kullanmamaktır.
ALTER VIEW deyimiyle, erişim hakları dışında var olan view’ın bütün ayarlarını
değiştirebilirsiniz. Bu, şifrelemenin zaten değiştirildiği anlamına gelir. Eğer ALTER VIEW yapılırken
şifrelenmesini de istiyorsanız ALTER VIEW’da WITH ENCRYPTION ifadesini kullanmanız gerekir.

ALTER VIEW CustomerOrders_vw WITH ENCRYPTION AS SELECT cu.CompanyName,


o.OrderDate . . .

WITH ENCRYPTION seçeneğini kullanmadan önce kaynak kodunuzu bir yerde saklamanız
gerekir. Bir kere şifrelendikten sonra daha geri almazsınız. Eğer bir yerde saklamamışsanız ve
değiştirmeniz gerekirse en başından yeniden yazmak zorunda kalırsınız.

You might also like