MariaDB ve MySQL’de UPDATE ile Mevcut Kayıtları Güncelleme
Veritabanı yönetiminde en sık kullanılan işlemlerden biri mevcut kayıtları güncellemektir. Yeni bir kayıt eklemek kadar kolay görünse de, UPDATE sorgularını yanlış yazmak ciddi veri kayıplarına yol açabilir. Özellikle WHERE koşulunu unutmak, tablodaki tüm satırları değiştirir ve bu durum production ortamında felaket anlamına gelir. Bu yazıda MariaDB ve MySQL üzerinde UPDATE sorgusunu derinlemesine inceleyecek, gerçek dünya senaryolarıyla pekiştireceğiz.
UPDATE Sorgusunun Temel Yapısı
UPDATE sorgusunun sözdizimi oldukça basittir ancak dikkat edilmesi gereken birkaç kritik nokta vardır.
UPDATE tablo_adi
SET sutun1 = deger1, sutun2 = deger2, ...
WHERE kosul;
Buradaki en önemli kural: WHERE koşulunu her zaman yazın. WHERE olmadan çalıştırılan bir UPDATE, tablodaki tüm satırları günceller. Bunu önlemek için MariaDB’de sql_safe_updates modunu aktif edebilirsiniz, ilerleyen bölümlerde bunu da göstereceğiz.
Basit bir örnekle başlayalım. Müşteri tablosunda bir kullanıcının e-posta adresini güncelleyelim:
UPDATE musteriler
SET email = '[email protected]'
WHERE musteri_id = 42;
Bu sorgu, musteri_id değeri 42 olan tek satırı günceller. Query OK, 1 row affected çıktısını görüyorsanız işlem başarılıdır. Eğer 0 rows affected görüyorsanız, WHERE koşulunuza uyan kayıt yok demektir, bu bir hata değildir ama dikkat etmeniz gereken bir durumdur.
Birden Fazla Sütunu Aynı Anda Güncelleme
Gerçek dünya senaryolarında genellikle tek bir sütunu değil, birden fazla sütunu aynı anda güncellemek gerekir. SET ifadesinde sütunları virgülle ayırarak bunu yapabilirsiniz.
UPDATE calisanlar
SET
ad = 'Ahmet',
soyad = 'Yilmaz',
telefon = '0532-111-2233',
guncelleme_tarihi = NOW()
WHERE calisan_id = 105;
Burada NOW() fonksiyonunu kullandığımıza dikkat edin. Güncelleme tarihini manuel girmek yerine veritabanının kendi zaman damgasını kullanmak, tutarlılık açısından çok daha güvenilirdir. Bu pattern’i sık kullanıyorsanız, tablonuza updated_at sütunu ekleyip her UPDATE sorgusunda updated_at = NOW() yazmayı alışkanlık haline getirin.
WHERE Koşullarıyla Çalışmak
UPDATE sorgularında WHERE koşulları SELECT sorgularındaki kadar güçlüdür. AND, OR, IN, BETWEEN, LIKE gibi operatörlerin hepsini kullanabilirsiniz.
Birden fazla koşul kombinasyonu:
UPDATE urunler
SET stok_miktari = 0, aktif = 0
WHERE kategori_id = 5 AND son_satis_tarihi < '2023-01-01';
Bu sorgu, kategori 5’e ait ve 2023 yılından önce son satışı gerçekleşmiş ürünlerin stok miktarını sıfırlayıp pasife çeker. E-ticaret sistemlerinde sezonluk ürünleri toplu kapatmak için bu tür sorgular çok işe yarar.
IN operatörüyle belirli ID listesini güncellemek:
UPDATE siparisler
SET durum = 'iptal_edildi', iptal_nedeni = 'Stok yok'
WHERE siparis_id IN (1001, 1045, 1067, 1089)
AND durum = 'beklemede';
LIKE operatörüyle desen eşleştirme:
UPDATE kullanicilar
SET email = REPLACE(email, '@eski-domain.com', '@yeni-domain.com')
WHERE email LIKE '%@eski-domain.com';
Bu son örnek özellikle dikkat çekici. Şirket domain’i değiştiğinde tüm kullanıcıların e-posta adreslerini tek sorguda güncelleyebilirsiniz. REPLACE() fonksiyonu, string içindeki bir deseni bulup değiştirir.
Matematiksel İşlemlerle Güncelleme
UPDATE sorgularında mevcut değer üzerinde matematiksel işlem yapabilirsiniz. Bu özellikle stok yönetimi, puan sistemleri ve fiyat güncellemeleri gibi senaryolarda çok kullanışlıdır.
-- Fiyatları yüzde 15 artır
UPDATE urunler
SET fiyat = fiyat * 1.15
WHERE kategori_id = 3 AND aktif = 1;
-- Stok miktarından düş
UPDATE stok
SET miktar = miktar - 5
WHERE urun_id = 201 AND depo_id = 1;
-- Kullanıcı puanına ekle
UPDATE kullanici_puanlar
SET puan = puan + 100, toplam_islem = toplam_islem + 1
WHERE kullanici_id = 55;
Stok güncellemelerinde kritik bir nokta: negatif stok durumunu engellemek. Bunun için WHERE koşuluna ek kontrol ekleyin:
UPDATE stok
SET miktar = miktar - 10
WHERE urun_id = 201
AND depo_id = 1
AND miktar >= 10;
Bu şekilde mevcut stok 10’dan azsa güncelleme hiç yapılmaz. 0 rows affected döndüğünde uygulama katmanında bu durumu yakalayıp kullanıcıya “Yeterli stok yok” mesajı verebilirsiniz.
JOIN ile UPDATE Kullanımı
Bu, birçok geliştiricinin ve sysadmin’in ilk etapta bilmediği ama öğrenince hayatını kolaylaştıran bir özelliktir. MariaDB ve MySQL’de başka bir tablodaki veriye göre güncelleme yapabilirsiniz.
Senaryo: Siparişler tablosundaki müşteri adını, müşteriler tablosundaki güncel bilgilerle senkronize etmek istiyorsunuz.
UPDATE siparisler s
JOIN musteriler m ON s.musteri_id = m.id
SET
s.musteri_adi = m.ad,
s.musteri_soyadi = m.soyad,
s.musteri_email = m.email
WHERE s.durum = 'beklemede';
Daha karmaşık bir örnek, üç tabloyu birleştirerek güncelleme:
UPDATE urunler u
JOIN kategoriler k ON u.kategori_id = k.id
JOIN vergi_oranlari v ON k.vergi_grubu = v.grup_kodu
SET u.vergi_orani = v.oran, u.guncelleme_tarihi = NOW()
WHERE k.aktif = 1 AND u.aktif = 1;
Bu sorgu, aktif kategorilere ait aktif ürünlerin vergi oranlarını ilgili tablodaki güncel değerlerle günceller. Vergi düzenlemesi olduğunda tek bir sorguyla binlerce ürünü güncelleyebilirsiniz.
Subquery (Alt Sorgu) ile UPDATE
JOIN kullanmak mümkün olmadığı durumlarda veya daha okunaklı bir sorgu tercih ettiğinizde alt sorgulardan yararlanabilirsiniz.
UPDATE calisanlar
SET maas = maas * 1.10
WHERE departman_id IN (
SELECT id FROM departmanlar
WHERE performans_puani > 85
);
Önemli bir uyarı: MySQL ve MariaDB’de aynı tabloyu hem güncelleme hem de alt sorgu olarak kullanamazsınız. Bunu geçici tablo ya da JOIN ile çözmeniz gerekir.
Yanlış (hata verir):
-- Bu çalışmaz!
UPDATE urunler
SET fiyat = fiyat * 1.05
WHERE id IN (
SELECT id FROM urunler WHERE stok_miktari > 100
);
Doğru çözüm:
UPDATE urunler u1
JOIN (
SELECT id FROM urunler WHERE stok_miktari > 100
) u2 ON u1.id = u2.id
SET u1.fiyat = u1.fiyat * 1.05;
LIMIT ile UPDATE
Büyük tablolarda toplu güncelleme işlemi yaparken tüm sorguyu tek seferde çalıştırmak, lock sürelerini uzatır ve performans sorunlarına yol açar. Bu durumda LIMIT kullanarak işlemi parçalara bölebilirsiniz.
UPDATE log_kayitlari
SET arsivlendi = 1
WHERE tarih < '2022-01-01' AND arsivlendi = 0
LIMIT 1000;
Bu sorguyu bir shell scriptiyle döngüye alarak aşamalı güncelleme yapabilirsiniz:
#!/bin/bash
# Toplu güncellemeyi parçalara bölerek yap
BATCH_SIZE=1000
AFFECTED=1
while [ $AFFECTED -gt 0 ]; do
RESULT=$(mysql -u root -p'sifreniz' veritabani_adi -e "
UPDATE log_kayitlari
SET arsivlendi = 1
WHERE tarih < '2022-01-01' AND arsivlendi = 0
LIMIT $BATCH_SIZE;
SELECT ROW_COUNT();
" 2>/dev/null | tail -1)
AFFECTED=${RESULT:-0}
echo "$(date): $AFFECTED satir guncellendi"
if [ $AFFECTED -gt 0 ]; then
sleep 0.5 # Sunucuya nefes aldır
fi
done
echo "Tum kayitlar guncellendi."
Bu yaklaşım özellikle production ortamlarında kritik önem taşır. 10 milyon satırlık bir tabloyu tek sorguda güncellemek yerine, 1000’erli gruplar halinde işleyerek hem lock süresini kısaltırsınız hem de diğer sorguların etkilenmesini önlersiniz.
CASE ile Koşullu Güncelleme
Tek bir UPDATE sorgusunda farklı koşullara göre farklı değerler atamak istiyorsanız CASE ifadesi kullanabilirsiniz.
UPDATE siparisler
SET kargo_ucreti = CASE
WHEN toplam_tutar >= 500 THEN 0
WHEN toplam_tutar >= 200 THEN 15
WHEN toplam_tutar >= 100 THEN 25
ELSE 35
END
WHERE durum = 'onaylandi' AND kargo_ucreti IS NULL;
Bu sorgu, sipariş tutarına göre farklı kargo ücretleri belirler. Alternatif olarak birden fazla UPDATE çalıştırabilirdiniz ama CASE ile tek sorguda halletmek hem daha hızlı hem de daha tutarlıdır.
Başka bir örnek, kullanıcı segmentasyonu:
UPDATE kullanicilar
SET segment = CASE
WHEN toplam_harcama > 10000 THEN 'VIP'
WHEN toplam_harcama > 3000 THEN 'premium'
WHEN toplam_harcama > 500 THEN 'standart'
ELSE 'yeni'
END
WHERE son_aktivite_tarihi >= DATE_SUB(NOW(), INTERVAL 1 YEAR);
Safe Update Modu ve Güvenlik Önlemleri
Özellikle geliştirme ortamında yanlışlıkla WHERE koşulsuz UPDATE çalıştırmayı önlemek için sql_safe_updates modunu aktif edin:
-- Sadece bu oturum için
SET sql_safe_updates = 1;
-- Kalıcı olarak my.cnf veya mariadb.cnf içine ekleyin:
-- sql_safe_updates = 1
Bu mod aktifken, PRIMARY KEY veya INDEX içermeyen WHERE koşullu UPDATE sorgularını çalıştıramazsınız. Bu bazen kısıtlayıcı gelebilir ama production ortamında gerçek bir güvence sağlar.
Güncelleme yapmadan önce her zaman SELECT ile kaç satırın etkileneceğini kontrol edin:
-- Önce kontrol et
SELECT COUNT(*) FROM musteriler
WHERE sehir = 'Ankara' AND aktif = 1;
-- Sonucu beğendiysen güncelle
UPDATE musteriler
SET bolge = 'IC_ANADOLU'
WHERE sehir = 'Ankara' AND aktif = 1;
Bu alışkanlığı kazanmak, production ortamında pek çok hatanın önüne geçer.
Transaction ile Güvenli Güncelleme
Kritik güncellemeleri her zaman transaction içinde yapın. Bu sayede bir hata oluşursa değişikliği geri alabilirsiniz.
START TRANSACTION;
-- Banka transferi örneği
UPDATE hesaplar SET bakiye = bakiye - 1000 WHERE hesap_id = 101;
UPDATE hesaplar SET bakiye = bakiye + 1000 WHERE hesap_id = 202;
-- Her şey yolundaysa onayla
COMMIT;
-- Sorun çıkarsa geri al
-- ROLLBACK;
Shell script içinde transaction kullanımı:
mysql -u root -p'sifreniz' veritabani_adi << 'EOF'
START TRANSACTION;
UPDATE siparisler
SET durum = 'kargoya_verildi', kargo_tarihi = NOW()
WHERE siparis_id = 5501;
UPDATE stok
SET rezerve_miktar = rezerve_miktar - 1
WHERE urun_id = (SELECT urun_id FROM siparisler WHERE siparis_id = 5501);
COMMIT;
EOF
ROW_COUNT ile Etkilenen Satır Sayısını Kontrol Etme
UPDATE sorgusundan sonra kaç satırın etkilendiğini kontrol etmek, uygulama mantığı için önemlidir.
UPDATE kullanicilar
SET sifre_hash = 'yeni_hash_degeri', son_degisiklik = NOW()
WHERE kullanici_adi = 'mehmet_kaya';
SELECT ROW_COUNT();
ROW_COUNT() fonksiyonu, son sorgunun etkilediği satır sayısını döndürür. Eğer 0 dönüyorsa, güncellenmek istenen kayıt ya yoktur ya da zaten aynı değeri taşımaktadır. MariaDB bir nüans ekler burada: eğer mevcut değer yeni değerle aynıysa, sorgu “başarılı” olsa da ROW_COUNT() 0 döner çünkü gerçek anlamda hiçbir şey değişmemiştir.
Performans İpuçları
UPDATE sorgularını optimize etmek, özellikle büyük tablolarda önem kazanır.
- WHERE koşulundaki sütunlar indeksli olmalı: Indekssiz bir sütun üzerinden WHERE koşulu yazarsanız, MariaDB tüm tabloyu tarar. Bu hem yavaş çalışır hem de uzun süre lock tutar.
- Büyük toplu güncellemeleri LIMIT ile parçalayın: Yukarıda gösterdiğimiz script yaklaşımını kullanın.
- Çok sütun güncellemek yerine sadece değişenleri güncelleyin: Gereksiz sütun güncellemeleri undo log boyutunu artırır.
- InnoDB’de UPDATE sonrası ANALYZE TABLE çalıştırmayı unutmayın: Büyük toplu güncellemelerden sonra istatistikler eskiyebilir.
-- Büyük güncelleme sonrası istatistikleri yenile
ANALYZE TABLE urunler;
OPTIMIZE TABLE urunler;
- Uzun süren UPDATE’leri EXPLAIN ile analiz edin:
EXPLAIN UPDATE siparisler
SET durum = 'teslim_edildi'
WHERE musteri_id = 150 AND durum = 'kargoda';
EXPLAIN çıktısında type: ALL görüyorsanız, full table scan yapılıyor demektir. Bu durumda ilgili sütuna index eklemeyi değerlendirin.
Gerçek Dünya Senaryosu: Sezonluk Fiyat Güncelleme
Bir e-ticaret platformunda çalışıyorsunuz ve yıl sonu kampanyası için belirli kategorilerdeki ürün fiyatlarını güncellemeniz gerekiyor. Bunu güvenli biçimde yapalım:
#!/bin/bash
# Sezonluk fiyat guncelleme scripti
DB_USER="kampanya_user"
DB_PASS="guclu_sifre"
DB_NAME="eticaret_db"
LOG_FILE="/var/log/mysql/kampanya_guncelleme.log"
echo "$(date): Kampanya fiyat guncellemesi basliyor" >> $LOG_FILE
# Önce yedek al
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME urunler >
/backup/urunler_kampanya_oncesi_$(date +%Y%m%d_%H%M%S).sql
# Güncellemeden önce kaç satır etkileneceğini logla
ETKILENECEK=$(mysql -u $DB_USER -p$DB_PASS $DB_NAME -sN -e "
SELECT COUNT(*) FROM urunler u
JOIN kategoriler k ON u.kategori_id = k.id
WHERE k.kampanya_dahil = 1 AND u.aktif = 1 AND u.kampanya_fiyati IS NULL;
")
echo "$(date): $ETKILENECEK urun etkilenecek" >> $LOG_FILE
# Güncellemeyi transaction içinde yap
mysql -u $DB_USER -p$DB_PASS $DB_NAME << 'EOF'
START TRANSACTION;
UPDATE urunler u
JOIN kategoriler k ON u.kategori_id = k.id
SET
u.kampanya_fiyati = ROUND(u.normal_fiyat * 0.80, 2),
u.kampanya_baslangic = '2024-12-25',
u.kampanya_bitis = '2025-01-05',
u.guncelleme_tarihi = NOW()
WHERE k.kampanya_dahil = 1
AND u.aktif = 1
AND u.kampanya_fiyati IS NULL;
COMMIT;
EOF
SONUC=$?
if [ $SONUC -eq 0 ]; then
echo "$(date): Guncelleme basarili tamamlandi" >> $LOG_FILE
else
echo "$(date): HATA! Guncelleme basarisiz, kod: $SONUC" >> $LOG_FILE
fi
Bu script önce yedek alıyor, etkilenecek satır sayısını loglayıp ardından transaction içinde güncellemeyi yapıyor. Production ortamında bu tür bir güvenlik katmanı şarttır.
Sonuç
UPDATE sorgusu basit görünse de, yanlış kullanıldığında telafisi zor sonuçlar doğurabilir. Bu yazıda ele aldığımız temel kuralları özetlemek gerekirse: WHERE koşulunu her zaman yazın, büyük güncellemeleri LIMIT ile parçalara bölün, kritik işlemleri transaction içinde gerçekleştirin ve güncelleme öncesinde SELECT ile kaç satırın etkileneceğini doğrulayın.
JOIN ve CASE kullanımını öğrenmek, çok sayıda UPDATE sorgusunu tek bir verimli sorguya indirmenizi sağlar. Safe update modu ve ROW_COUNT kontrolü gibi küçük alışkanlıklar ise sizi beklenmedik sürprizlerden korur. MariaDB ve MySQL’in UPDATE özelliklerini doğru kullandığınızda, hem veritabanı performansınız artar hem de sistem yönetimi süreçleriniz daha güvenilir hale gelir.
