Okumayı öğrendik, şimdi yazma sırası. Veri eklemek, güncellemek, silmek — ve bunları güvenli yapmak. Transaction’lar olmadan production’da SQL yazmayın.
1. INSERT — Veri Ekleme
Tek Satır
INSERT INTO Employees (Name, Department, Salary)
VALUES ('Ali Yılmaz', 'IT', 8500.00);
Sütun isimlerini her zaman yaz. Tablo yapısı değişirse sorgun bozulmaz.
Birden Fazla Satır (Tek Sorguda)
INSERT INTO Employees (Name, Department, Salary)
VALUES
('Ayşe Demir', 'HR', 6200),
('Mehmet Kaya', 'Marketing', 7000),
('Fatma Şahin', 'IT', 9500);
SELECT’ten INSERT
Başka tablodan veri kopyala:
INSERT INTO EmployeesArchive (Name, Department, Salary, ArchivedAt)
SELECT Name, Department, Salary, GETDATE()
FROM Employees
WHERE Active = 0;
IDENTITY Değeri Geri Almak
OUTPUT ile:
INSERT INTO Employees (Name, Department)
OUTPUT INSERTED.Id, INSERTED.Name
VALUES ('Cem Öztürk', 'Sales');
Veya:
INSERT INTO Employees (Name) VALUES ('Cem');
SELECT SCOPE_IDENTITY(); -- son INSERT edilen IDENTITY değeri
2. UPDATE — Kayıt Güncelleme
UPDATE Employees
SET Salary = Salary * 1.10 -- %10 zam
WHERE Department = 'IT';
Birden Fazla Sütun
UPDATE Employees
SET
Salary = 9000,
Department = 'Senior IT',
UpdatedAt = GETDATE()
WHERE Id = 5;
JOIN ile UPDATE
UPDATE e
SET e.Salary = e.Salary + 500
FROM Employees AS e
INNER JOIN Departments AS d ON e.DepartmentId = d.Id
WHERE d.Budget > 100000;
-- Bütçesi yüksek departmanlardakilere zam
UYARI:
WHEREolmadan UPDATE = tüm satırları günceller. Hayatınızın en pahalı dersini almak istemiyorsanız her zaman önceSELECTile aynıWHEREile test edin.
3. DELETE — Kayıt Silme
DELETE FROM Employees
WHERE Active = 0
AND LastLoginDate < DATEADD(YEAR, -2, GETDATE());
TRUNCATE — Hızlı Tam Temizleme
TRUNCATE TABLE Employees;
DELETE vs TRUNCATE
| Özellik | DELETE | TRUNCATE |
|---|---|---|
| WHERE | Var | Yok (hep tüm tablo) |
| Hız | Yavaş | Çok hızlı |
| Log | Her satır loglanır | Minimal |
| IDENTITY | Reset etmez | Sıfırlar |
| Trigger | Çalışır | Çalışmaz |
| Foreign Key referansı varsa | Çalışır (cascade’e göre) | Hata verir |
Boş tabloya çekmek için → TRUNCATE. Şartlı silmek için → DELETE.
4. MERGE — Upsert (Insert + Update)
“Varsa güncelle, yoksa ekle” tek komutta:
MERGE INTO Inventory AS target
USING (VALUES (101, 'Widget', 50)) AS source (Id, Name, Quantity)
ON target.Id = source.Id
WHEN MATCHED THEN
UPDATE SET
Quantity = target.Quantity + source.Quantity
WHEN NOT MATCHED THEN
INSERT (Id, Name, Quantity)
VALUES (source.Id, source.Name, source.Quantity);
MERGE güçlü ama karmaşık. Basit upsert için bazen
IF EXISTS ... UPDATE ELSE INSERTdaha okunaklı olabilir.
5. Transaction Yönetimi
Veritabanı ACID prensibinin kalbi. Birden fazla işlemi tek bir atomic operasyon olarak yapmak için:
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 1000 WHERE Id = 1;
UPDATE Accounts SET Balance = Balance + 1000 WHERE Id = 2;
COMMIT TRANSACTION;
-- ya da: ROLLBACK TRANSACTION; (geri al)
Eğer ortada bir hata olursa hiçbir değişiklik kalıcı olmaz.
TRY / CATCH ile Güvenli Transaction
Production pattern:
BEGIN TRY
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 1000 WHERE Id = 1;
IF (SELECT Balance FROM Accounts WHERE Id = 1) < 0
THROW 50001, 'Yetersiz bakiye', 1;
UPDATE Accounts SET Balance = Balance + 1000 WHERE Id = 2;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- Hatayı yeniden fırlat veya logla
DECLARE @ErrorMsg NVARCHAR(4000) = ERROR_MESSAGE();
THROW;
END CATCH
@@TRANCOUNT aktif transaction sayısını verir. ERROR_MESSAGE(),
ERROR_NUMBER(), ERROR_LINE() hata detayları için.
Savepoint
İç içe işlemlerde kısmi rollback:
BEGIN TRANSACTION;
INSERT INTO Orders ... ;
SAVE TRANSACTION OrderSaved;
INSERT INTO OrderItems ... ;
IF (kontrol başarısız)
ROLLBACK TRANSACTION OrderSaved; -- sadece OrderItems geri alınır
ELSE
COMMIT TRANSACTION;
6. Constraint’ler
Constraint’ler, veriye kısıtlama koyar — veritabanı seviyesinde doğrulama.
PRIMARY KEY
CREATE TABLE Users (
Id INT IDENTITY(1,1) PRIMARY KEY,
...
);
Veya named constraint:
CREATE TABLE Users (
Id INT IDENTITY(1,1),
CONSTRAINT PK_Users PRIMARY KEY (Id)
);
FOREIGN KEY
Bir tablonun başka tabloyu referans vermesi:
CREATE TABLE Orders (
Id INT IDENTITY(1,1) PRIMARY KEY,
UserId INT NOT NULL,
Amount DECIMAL(10, 2),
CONSTRAINT FK_Orders_Users
FOREIGN KEY (UserId) REFERENCES Users(Id)
ON DELETE CASCADE -- User silinince Order'lar da silinir
ON UPDATE NO ACTION
);
Cascade seçenekleri:
CASCADE— Parent silinince child da silinir/güncellenirSET NULL— Child’in FK’sı NULL’a çekilirSET DEFAULT— Default değere çekilirNO ACTION— Engellenir (en güvenli)
UNIQUE
CREATE TABLE Users (
Email NVARCHAR(150) UNIQUE,
...
);
NULL kabul eder ama tek bir NULL — birden fazla NULL hata verir (SQL Server’da varsayılan).
CHECK
Özel doğrulama kuralı:
CREATE TABLE Products (
Price DECIMAL(10, 2) CHECK (Price >= 0),
Stock INT CHECK (Stock >= 0),
Status CHAR(1) CHECK (Status IN ('A', 'P', 'D'))
);
DEFAULT
CREATE TABLE Users (
CreatedAt DATETIME DEFAULT GETDATE(),
Status NVARCHAR(20) DEFAULT 'active'
);
Sonradan Constraint Eklemek
ALTER TABLE Users
ADD CONSTRAINT UQ_Email UNIQUE (Email);
ALTER TABLE Users
DROP CONSTRAINT UQ_Email;
7. Index Temelleri
Index, arama hızını artıran veri yapısı. Kitap arkasındaki dizin gibi.
Index Oluşturma
CREATE INDEX IX_Users_Email ON Users(Email);
Clustered vs Non-Clustered
- Clustered Index — Veri fiziksel olarak bu sırada saklanır. Tek bir tane olabilir. PRIMARY KEY varsayılan olarak clustered’tır.
- Non-Clustered Index — Ayrı yapı, veriye işaret eder. Birden fazla olabilir.
Composite Index
Birden fazla sütun üzerinde:
CREATE INDEX IX_Orders_UserDate
ON Orders(UserId, OrderDate DESC);
Sıralama önemli:
WHERE UserId = Xçalışır (index kullanır),WHERE OrderDate = Ytek başına bu index’i kullanmaz.
Ne Zaman Index Eklemeli?
- ✅ Sık
WHEREile filtrelenen sütunlar - ✅
JOINkoşullarında kullanılan sütunlar (FK) - ✅
ORDER BYsütunları - ❌ Çok az farklı değer içeren sütunlar (örn.
Gender) - ❌ Sık güncellenen tablolar (write performansını düşürür)
Pratik Sorular
Soru 1: Yeni bir kullanıcı ekle ve oluşturulan Id’sini geri al.
INSERT INTO Users (Name, Email)
OUTPUT INSERTED.Id, INSERTED.Email
VALUES ('Ejder', 'ejder@x.com');
Soru 2: IT departmanındaki tüm çalışanlara %15 zam yap. Önce kaç satırı etkileyeceğini test et.
-- Test
SELECT COUNT(*) FROM Employees WHERE Department = 'IT';
-- Update
UPDATE Employees
SET Salary = Salary * 1.15
WHERE Department = 'IT';
Soru 3: İki hesap arasında para transferi (transaction ile).
BEGIN TRY
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - @Amount WHERE Id = @FromId;
IF (SELECT Balance FROM Accounts WHERE Id = @FromId) < 0
THROW 50001, 'Yetersiz bakiye', 1;
UPDATE Accounts SET Balance = Balance + @Amount WHERE Id = @ToId;
INSERT INTO Transfers (FromId, ToId, Amount, TransferDate)
VALUES (@FromId, @ToId, @Amount, GETDATE());
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
THROW;
END CATCH
Soru 4: Products tablosunda fiyat 0’dan büyük olmalı kuralını ekle.
ALTER TABLE Products
ADD CONSTRAINT CHK_Price_Positive CHECK (Price > 0);
Soru 5: Sık aranan OrderDate ve CustomerId sütunlarına index ekle.
CREATE INDEX IX_Orders_CustomerDate
ON Orders(CustomerId, OrderDate DESC);
Günün Özeti
Bugün veriyi nasıl güvenli yazacağımızı öğrendik:
- INSERT (tek, toplu, SELECT’ten, OUTPUT ile)
- UPDATE ve JOIN ile UPDATE
- DELETE vs TRUNCATE farkları
- MERGE ile upsert
- Transaction’lar —
BEGIN / COMMIT / ROLLBACKve TRY-CATCH - Constraint’ler (PRIMARY, FOREIGN, UNIQUE, CHECK, DEFAULT)
- Index temelleri ve ne zaman kullanılacağı
Yarın Gün 5’te son durağımız: stored procedure’ler, function’lar, window functions ve performans optimizasyonu.