← Kursa Dön
📄 Text · 30 min

UPDATE — WHERE, Birden Fazla Sütun, Subquery

Giriş — Veri Değişir, Güncelleme Kaçınılmaz

Müşteri adresini değiştirdi, ürün fiyatı güncellendi, sipariş durumu "kargoya verildi" oldu, kullanıcı şifresini sıfırladı... Gerçek dünyada veri sürekli değişir. UPDATE komutu, tablolardaki mevcut verileri güncellemenin yoludur.

Bu komutu öğrenirken bir kuralı asla unutma: WHERE olmadan UPDATE yapmak, tüm satırları günceller. Bu, SQL dünyasının en tehlikeli hatalarından biridir ve production'da büyük felaketlere yol açabilir.

🎯 Analoji: Bir sınıfta öğretmensin ve yoklama kâğıdında bir öğrencinin ismini düzeltmen gerekiyor. "Ahmet Yılmaz" yerine "Ahmet Kaya" yazacaksın. Ama dikkatli olmazsan, tüm öğrencilerin soyadını "Kaya" yaparsın. İşte WHERE koşulu, "sadece bu öğrenciyi değiştir" demenin yolu.


Temel Sözdizimi

UPDATE tablo_adi
SET sutun1 = yeni_deger1,
    sutun2 = yeni_deger2,
    ...
WHERE kosul;

Üç ana bileşen:

  1. Tablo adı — Hangi tablodaki veriler güncellenecek?

  2. SET — Hangi sütunlar, hangi değerlerle güncellenecek?

  3. WHERE — Hangi satırlar güncellenecek? (Bu olmadan TÜM satırlar güncellenir!)


Tek Sütun Güncelleme

Basit Güncelleme

-- Ahmet'in şehrini güncelle (customer_id = 1)
UPDATE customers 
SET city = 'Bursa' 
WHERE customer_id = 1;
-- Query OK, 1 row affected
-- Rows matched: 1  Changed: 1  Warnings: 0

Çıktıdaki bilgiler:

  • Rows matched: 1 — WHERE koşuluna 1 satır uydu

  • Changed: 1 — 1 satır gerçekten değişti

  • Eğer zaten 'Bursa' olsaydı: Changed: 0 (değişiklik yok, hata da yok)

Koşullu Güncelleme

-- 1000 TL üstündeki ürünlere %10 zam yap
UPDATE products 
SET price = price * 1.10 
WHERE price > 1000;
-- Fiyatı 1000'den büyük olan tüm ürünlerin fiyatı %10 artar

-- Belirli bir kategorideki ürünlerin stokunu sıfırla
UPDATE products 
SET stock_quantity = 0 
WHERE category_id = 3;

Birden Fazla Sütun Güncelleme

Tek bir UPDATE komutuyla birden fazla sütunu güncelleyebilirsin:

-- Müşterinin hem şehrini hem telefonunu güncelle
UPDATE customers 
SET city = 'İzmir',
    phone = '05559999999'
WHERE customer_id = 2;

-- Siparişin durumunu ve toplam tutarını güncelle
UPDATE orders 
SET status = 'shipped',
    shipping_address = 'İzmir, Karşıyaka, 35000',
    updated_at = NOW()
WHERE order_id = 4;

-- Ürün bilgilerini toplu güncelle
UPDATE products 
SET price = 52999.99,
    stock_quantity = 20,
    is_active = TRUE
WHERE product_id = 1;

💡 İpucu: Birden fazla sütunu ayrı ayrı güncellemek yerine tek bir UPDATE'te toplamak hem daha okunabilir hem daha performanslıdır. Veritabanı tek seferde tek bir yazma operasyonu yapar.


WHERE Koşulları ile Güncelleme

WHERE'da kullanabileceğin tüm operatörler UPDATE'te de geçerlidir:

-- Belirli şehirlerdeki müşterileri güncelle
UPDATE customers 
SET is_active = FALSE 
WHERE city IN ('Bursa', 'Antalya');

-- Tarih aralığındaki siparişleri güncelle
UPDATE orders 
SET status = 'delivered' 
WHERE order_date < '2024-02-01' AND status = 'shipped';

-- LIKE ile güncelleme
UPDATE customers 
SET city = 'İstanbul' 
WHERE email LIKE '%@gmail.com';

-- NULL olanları güncelle
UPDATE customers 
SET phone = 'Belirtilmedi' 
WHERE phone IS NULL;

-- BETWEEN ile güncelleme
UPDATE products 
SET is_active = FALSE 
WHERE stock_quantity BETWEEN 0 AND 5;

Hesaplamalı Güncelleme (Mevcut Değere Göre)

-- Tüm ürünlere %5 zam yap (mevcut fiyata göre)
UPDATE products 
SET price = price * 1.05
WHERE is_active = TRUE;

-- Stoktan düş
UPDATE products 
SET stock_quantity = stock_quantity - 3 
WHERE product_id = 5;

-- Stokta olanların stokunu artır
UPDATE products 
SET stock_quantity = stock_quantity + 100 
WHERE stock_quantity > 0 AND category_id = 1;

⚠️ Dikkat: stock_quantity = stock_quantity - 3 negatife düşebilir! Eğer stoğu 2 olan ürünü 3 düşürürsen -1 olur. Bunu önlemek için:

-- Güvenli stok düşürme
UPDATE products 
SET stock_quantity = stock_quantity - 3 
WHERE product_id = 5 AND stock_quantity >= 3;

-- Eğer satır güncellenmezse (stock < 3), 0 rows affected döner
-- Uygulama katmanında bu durumu kontrol edebilirsin

Veya GREATEST fonksiyonuyla:

UPDATE products 
SET stock_quantity = GREATEST(stock_quantity - 3, 0) 
WHERE product_id = 5;
-- Stok asla 0'ın altına düşmez

Subquery ile UPDATE

Bir tablodaki veriyi, başka bir tablo veya sorgudan gelen değerle güncelleyebilirsin:

Tek Değer Döndüren Subquery (Scalar Subquery)

-- En pahalı ürünün fiyatını al ve başka bir ürünün fiyatını ona eşitle
UPDATE products 
SET price = (SELECT MAX(price) FROM products WHERE category_id = 2)
WHERE product_id = 4;

-- Bir müşterinin sipariş sayısını güncelle
-- (Normalde böyle bir sütun tutulmaz ama örnek olarak)
ALTER TABLE customers ADD COLUMN order_count INT DEFAULT 0;

UPDATE customers 
SET order_count = (
    SELECT COUNT(*) 
    FROM orders 
    WHERE orders.customer_id = customers.customer_id
)
WHERE customer_id = 1;

-- Siparişin toplam tutarını order_items'tan hesaplayarak güncelle
UPDATE orders 
SET total_amount = (
    SELECT SUM(quantity * unit_price * (1 - discount_percent / 100)) 
    FROM order_items 
    WHERE order_items.order_id = orders.order_id
)
WHERE order_id = 1;

JOIN ile UPDATE (MySQL'e Özel)

MySQL'de UPDATE komutuyla doğrudan JOIN kullanabilirsin:

-- Stoku 0 olan ürünlerin siparişlerini iptal et
UPDATE orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
SET o.status = 'cancelled'
WHERE p.stock_quantity = 0 AND o.status = 'pending';

-- Kategori adına göre ürün fiyatlarını güncelle
UPDATE products p
JOIN categories c ON p.category_id = c.category_id
SET p.price = p.price * 0.90  -- %10 indirim
WHERE c.category_name = 'Kitap';

📝 PostgreSQL Notu: PostgreSQL'de JOIN ile UPDATE syntax'ı farklıdır: ``sql -- PostgreSQL syntax UPDATE products SET price = price * 0.90 FROM categories WHERE products.category_id = categories.category_id AND categories.category_name = 'Kitap'; ``


WHERE Olmadan UPDATE — Tehlike!

-- ⚠️ TEHLİKE! WHERE yok — TÜM müşterilerin şehri değişir!
UPDATE customers SET city = 'Ankara';
-- Query OK, 10 rows affected — 10 müşterinin HEPSİ artık Ankara'da!

-- ⚠️ TEHLİKE! Tüm ürünlerin fiyatı 0 olur!
UPDATE products SET price = 0;
-- Tüm ürünler bedava oldu... 💀

Bunları önlemenin yolları:

1. Safe Updates Mode

-- Safe mode'u aç
SET SQL_SAFE_UPDATES = 1;

-- Artık WHERE'siz veya WHERE'de index olmayan UPDATE'ler engellenir
UPDATE customers SET city = 'Ankara';
-- ERROR 1175: You are using safe update mode and you tried to update a table
-- without a WHERE that uses a KEY column.

2. LIMIT ile Güvenlik

-- En fazla 1 satır güncelle
UPDATE customers 
SET city = 'Ankara' 
WHERE first_name = 'Ahmet' 
LIMIT 1;

3. Önce SELECT ile Kontrol

-- Önce hangi satırların etkileneceğini gör
SELECT customer_id, first_name, city 
FROM customers 
WHERE city = 'İstanbul';
-- 4 satır etkilenecek — tamam mı?

-- Sonra güncelle
UPDATE customers 
SET is_active = FALSE 
WHERE city = 'İstanbul';

💡 İpucu: Production'da UPDATE çalıştırmadan önce her zaman aynı WHERE koşuluyla bir SELECT yap. Kaç satırın etkileneceğini gör, doğru satırlar mı kontrol et, sonra UPDATE'i çalıştır. Bu alışkanlık hayat kurtarır.


CASE WHEN ile Koşullu Güncelleme

Farklı koşullara göre farklı değerler atamak istediğinde:

-- Fiyat aralığına göre indirim uygula
UPDATE products 
SET price = CASE
    WHEN price > 50000 THEN price * 0.85    -- %15 indirim
    WHEN price > 10000 THEN price * 0.90    -- %10 indirim
    WHEN price > 1000 THEN price * 0.95     -- %5 indirim
    ELSE price                               -- Değişiklik yok
END
WHERE is_active = TRUE;

-- Sipariş durumunu tarihe göre toplu güncelle
UPDATE orders 
SET status = CASE
    WHEN order_date < '2024-01-15' AND status = 'shipped' THEN 'delivered'
    WHEN order_date < '2024-02-01' AND status = 'processing' THEN 'shipped'
    ELSE status
END
WHERE status NOT IN ('delivered', 'cancelled');

CASE WHEN ile birden fazla koşullu güncellemeyi tek bir UPDATE'te yapabilirsin — her koşul için ayrı UPDATE yazmana gerek kalmaz.


UPDATE ve Transaction

Kritik güncelleme işlemlerinde transaction kullan:

START TRANSACTION;

-- Fiyat güncelleme
UPDATE products SET price = 49999.99 WHERE product_id = 1;

-- Kontrol et
SELECT product_id, product_name, price FROM products WHERE product_id = 1;
-- 49999.99 — doğru mu?

-- Doğruysa kaydet
COMMIT;

-- Yanlışsa geri al
-- ROLLBACK;

Gerçek Dünya Örneği — Sipariş Durumu Yönetimi

-- Senaryo: Kargo şirketi toplu teslimat bildirimi gönderdi
-- 2024-01-20'den önce kargolanan siparişler teslim edildi

START TRANSACTION;

-- Önce kontrol et
SELECT order_id, customer_id, order_date, status 
FROM orders 
WHERE status = 'shipped' AND order_date < '2024-02-01';
-- 1 satır: order_id = 4

-- Güncelle
UPDATE orders 
SET status = 'delivered',
    updated_at = NOW()
WHERE status = 'shipped' AND order_date < '2024-02-01';

-- Doğru mu kontrol et
SELECT order_id, status, updated_at FROM orders WHERE order_id = 4;

COMMIT;

Performans İpuçları

  1. WHERE'da indexli sütun kullan: WHERE customer_id = 5 hızlıdır çünkü customer_id primary key (indexli). WHERE city = 'İstanbul' index yoksa tüm tabloyu tarar.

  2. Toplu güncelle: 10.000 satırı tek tek güncellemek yerine tek bir UPDATE ile toplu güncelle.

  3. Gereksiz güncelleme yapma: Değer zaten aynıysa UPDATE çalıştırma gereksizdir. MySQL fiziksel olarak yazmaz ama yine de bazı işlem maliyeti var.

-- Gereksiz güncellemeyi önle
UPDATE customers 
SET city = 'Ankara' 
WHERE customer_id = 2 AND city != 'Ankara';
-- Zaten Ankara ise hiçbir şey yapmaz

Sıkça Yapılan Hatalar

  1. WHERE unutmak: En büyük hata. UPDATE products SET price = 0 yazdığında tüm ürünlerin fiyatı sıfır olur. Production'da felaket. Safe updates mode'u aç.

  2. = yerine == yazmak: Python alışkanlığıyla WHERE customer_id == 1 yazmak MySQL'de hata vermez ama beklenmedik sonuç verebilir. SQL'de karşılaştırma operatörü = (tek eşittir).

  3. Subquery'de aynı tabloyu güncellemek: MySQL, güncellenen tabloyu doğrudan subquery'de kullanmana izin vermez:

-- ❌ HATA — aynı tablo hem güncelleniyor hem subquery'de
UPDATE products 
SET price = (SELECT AVG(price) FROM products);
-- ERROR: You can't specify target table 'products' for update in FROM clause

-- ✅ Çözüm — iç içe subquery ile
UPDATE products 
SET price = (SELECT avg_price FROM (SELECT AVG(price) AS avg_price FROM products) AS tmp)
WHERE product_id = 1;
  1. Transaction kullanmamak: Birbirine bağlı güncelleme işlemlerinde transaction kullanmamak veri tutarsızlığına yol açar.

  2. Güncelleme öncesi kontrol yapmamak: UPDATE'ten önce SELECT ile kaç satırın etkileneceğini kontrol etmemek sürprizlere açıktır.


Özet

  • UPDATE tablo SET sütun = değer WHERE koşul mevcut veriyi günceller

  • WHERE koşulu olmazsa tüm satırlar güncellenir — en tehlikeli SQL hatası

  • Birden fazla sütun tek UPDATE'te güncellenebilir: SET a = 1, b = 2

  • Hesaplamalı güncelleme mümkün: SET price = price * 1.10

  • Subquery ile başka tablodan değer alarak güncelleme yapılabilir

  • MySQL'de UPDATE ... JOIN syntax'ı desteklenir

  • CASE WHEN ile koşullu güncelleme yapılabilir

  • Production'da UPDATE öncesi mutlaka SELECT ile kontrol et

  • SQL_SAFE_UPDATES = 1 ile WHERE'siz güncellemeler engellenir

  • Kritik güncellemelerde transaction kullan