Veritabanlarında normalizasyon sistemin ileriye dönük kullanımında oluşabilecek güncelleme ve yeniliklerde hasar görmeden uyum sağlamasına yarayan ve veri analizi yaparken çok fazla varyasyon çıkartabilmenize yarar.
Çok uzatmadan Normalize Kurallar ile Örnek bir Restoran veritabanı yapalım.
musteriler
-musteri_no (Integer(11) ve Unique Auto Increment olmak zorundadır -ki müşterimizi id'sinden bulalım.)
-isim (varchar-255 uygundur)
-soyisim(varchar-255 uygundur)
-created_at (datetime uygundur default= current date time)
-updated_at (datetime uygundur default= current date time)
musteri_adresleri
-adres_no (Integer(11) ve Unique Auto Increment olmak zorundadır)
-musteri_no (integer(11) = musteriler.musteri_no)
-adres (text - nullable)
-ilce (text)
-sehir (text)
-telefon (text - nullable)
-eposta (text - nullable)
-created_at (datetime uygundur default= current date time)
-updated_at (datetime uygundur default= current date time)
siparisler
-siparis_no (integer(11) ve Unique Auto Increment)
-musteri_no (integer(11) = musteriler.musteri_no)
-musteri_isim ( NEDEN BİR DAHA YAZIYORUZ? Çünkü müşteri bir dönem sonra bu ismi değiştirebilir 2012 tarihinde ismi Ahmetmiş sonra Ayşe olarak güncellemiş bunu musteri_no statik olduğu için oradan bulabiliriz. )
-musteri_soyisim (varchar-255)
-musteri_adres_no (integer(11) = musteri_adresleri.adres_no)
-musteri_adres_adres
-musteri_adres_ilce
-musteri_adres_sehir
-musteri_telefon
-musteri_eposta
-siparis_durumu (enum = İptal,Teslim Edildi vs..)
-created_at (datetime uygundur default= current date time)
-updated_at (datetime uygundur default= current date time)
siparisler_urunler
-siparis_no
-urun_no (integer(11) = urunler.urun_no)
-urun_adi (text)
-urun_fiyat (decimal,10,2 - default: 0.00)
-urun_siparis_adeti ( integer(11) - default:0 )
-created_at (datetime uygundur default= current date time)
-updated_at (datetime uygundur default= current date time)
Sipariş ürünlerini JOIN ile bağlanarak urun_fiyat * urun_siparis_adeti ile bulmak normailzedir. siparis_toplam değeri tutmak (adet * fiyat) normalize değerlerine aykırıdır.
urunler
-urun_no (integer(11) ve unique Auto Increment)
-isim (text)
-fiyat (decimal, 10,2 - Fiyat bazını 0.00 olarak yordamladık 10,6 yaparsanız fiyat: 0.000000 olarak yorumlanabilir.)
-created_at (datetime uygundur default= current date time)
-updated_at (datetime uygundur default= current date time)
Bu şekilde bir veri tabanı ile normalize standartta bir sistem çıkartabilirsiniz. PHP ile DB build eden arkadaşlarıma bu işi çok kolaylaştıracak olan Eloquent tavsiye etmekteyim.
--Konu hakkında daha detaylı açıklama ekleyeceğim yakında.
Veritabanı Normalize Kuralı Nedir ve Nasıl Yapılır?
7
●221
- 25-08-2020, 16:21:16Forumda bu tarz paylaşımlar görmek çok hoşuma gidiyor. Teşekkürler
- 25-08-2020, 17:02:37Elinize sağlık.
Fakat siparişler tablosundaki müşteri isim biraz mantıksız ve mükerrer/gereksiz geldi.
Müşterinin ismi değişir ise, o yeni bir müşteri kartı daha doğru olmaz mı. Aynı şekilde soyisim gibi bilgileri, zaten müşteri kartından çekebiliyor iken, ekstra olarak bu kolonların olması çok doğru bir tasarım gibi gelmedi.
Onun yerine, Siparişler tablosunda "Müşteri ünvanı" diye text bir alan koyulabilir. Müşteri tablosundan adı soyadını otomatik alır, kullanıcı ihtiyaca göre (tek tük) string alanı düzeltebilir. Eğer müşterinin adı, soyadı vb bilgiler değişti ise de, müşteri kartından ad soyad güncellenir. Eski adına ve soyadına ise de, siparişler tablosundaki geçmiş kayıtlardaki müşteri ünvanı text olandan da görülebilir.
Bu söylediklerim, müşterinin adresi için de geçerli. Identity alanlar, başka bir tabloya ilişkili olarak çekilip, ek olarak string bir değer ile de set edilebilmesi daha doğru gibi.
Siparişler tablosundaki, Adres_ID'yi set edip, İl, Semt vb bilgiler bu ID'deki değerleri otomatik getirmesi, kullanıcının ihtiyaca göre string bir şekilde il, şehir'i değiştirme durumu yapılması daha doğru olmaz mı.
Yani özet olarak, farklı bir tabloda tutulan bilgiyi, farklı bir tabloya mükerrer bir şekilde yazmak yerine, ilişkiyi tutup, ek kolonlar ile de son kullanıcı ihtiyacına göre ilgili ekranın düzenlenebilmesinin sağlanılması daha doğru bir tasarım değil midir.
Ve ek olarak devam edeyim.
Siparis_urunler diye bir tablo düşünmüşsün. Bunun yerine olması gereken şudur diye yorumluyorum.
Öncelikle ürünler diye ayrı bir tablon olmalı. Ürünün kodu, adı, ölçü birimi vb bilgiler ile ürün kartları açılmalı.
Sipariş satırı diye de bir tablon daha olmalı. Bir siparişe ait birden fazla ürün olabilir, yani birden fazla kalemli satırlar. ki Siparis tablosu ile foreign_key ilişkisi içersinde olmalı. (Siparis_ID)
Sipariş satırı tablonda da URUN_ID olmalı ki, ürün kartındaki bilgileri otomatik getirebilsin gibi.
Ürünü sipariş satırına seçtirip, kullanıcı sadece fiyatını yazabilir. (Ki daha da devam edersem fiyat listesi tablosuna ihtiyaç olur ama konu uzar gider.) - 25-08-2020, 17:17:16ResimBox adlı üyeden alıntı: mesajı görüntüle
Müşterinin ismi değişir ise, o yeni bir müşteri kartı daha doğru olmaz mı. Aynı şekilde soyisim gibi bilgileri, zaten müşteri kartından çekebiliyor iken, ekstra olarak bu kolonların olması çok doğru bir tasarım gibi gelmedi.
- Müşteri bir patron olabilir ve şubelerine yemek sipariş ediyor olabilir.
Siparişler tablosundaki, Adres_ID'yi set edip, İl, Semt vb bilgiler bu ID'deki değerleri otomatik getirmesi, kullanıcının ihtiyaca göre string bir şekilde il, şehir'i değiştirme durumu yapılması daha doğru olmaz mı.
-müşteri tek sipariş için no:5 yerine no:6 yı talep edebilir bir seferlik güncelleme tüm kaydını güncellemeye sebep olur. kayıt güncellenir ise eğer 5 yıl önce bu siparişi nereye götürdüğümüzü nereden bileceğiz?
Mükerrer kayıt değil daha çok zamana göre kayıt diye geçiyor
--- Öncelikle ürünler diye ayrı bir tablon olmalı. Ürünün kodu, adı, ölçü birimi vb bilgiler ile ürün kartları açılmalı.
--- Evet zaten olacak vaktim olmadığı için en altına devam edeceğim yazdım hocam
- ki ekledim şimdi
Sipariş satırı diye de bir tablon daha olmalı. Bir siparişe ait birden fazla ürün olabilir, yani birden fazla kalemli satırlar. ki Siparis tablosu ile foreign_key ilişkisi içersinde olmalı. (Siparis_ID)
--- zaten siparis_urunleri tablosu var bunun iççin
Sipariş satırı tablonda da URUN_ID olmalı ki, ürün kartındaki bilgileri otomatik getirebilsin gibi.
--- sipariş satırında değil siparis_urunlerinde mevcut ürün id si
Ürünü sipariş satırına seçtirip, kullanıcı sadece fiyatını yazabilir. (Ki daha da devam edersem fiyat listesi tablosuna ihtiyaç olur ama konu uzar gider.)
--- o şekilde zaten
teşekkürler yorumun için - 25-08-2020, 17:25:24Ek string alanlar ile sorularının cevaplarına çözüm sağlanmış oluyor aslında. Şubelerine gönderiyor ise de, bu müşteriye zaten ek şube adresleri kayıt edilir. Siparişler tablosundaki bahsettiğim Adres_ID ile de, müşterinin şube bilgilerindeki diğer adresler set edilebilir. Ve dediğim gibi string alan ile de set edilir ise, adres tablosuna müdahale etmeden, yalnızca ilgili siparişe ait güncelleme yapılabilir diye bahsetmek istedim. Ama tam anlatamadım büyük ihtimal, önemi yok.
Veritabanı tasarımı çok keyifli ama bir o kadar da kişiden kişiye değişebildiği gibi, sektörel ihtiyaçlar doğrultusunda da değişiklik gösterebiliyor.
Böyle bir konu açtığın için ben teşekkür ederim. - 25-08-2020, 17:57:11Bahsetmeye çalıştığım yapı şu şekildeydi, belki birilerinin işine yarar. (Dediğim gibi bu tür konular çok keyifli.)
CREATE TABLE [dbo].[Musteri]( [Musteri_ID] INT IDENTITY(1,1) NOT NULL, [Musteri_No] VARCHAR(50) NULL, [Musteri_Adi] VARCHAR(100) NOT NULL, [Musteri_Soyadi] VARCHAR(100) NULL, [Eklenme_Zamani] DATETIME NOT NULL, [Guncelleme_Zamani] DATETIME NOT NULL ) CREATE TABLE [dbo].[Adres]( [Adres_ID] INT IDENTITY(1,1) NOT NULL, [Musteri_ID] INT NOT NULL, [Adres] VARCHAR(200) NULL, [Sehir] VARCHAR(50) NULL, [Ilce] VARCHAR(50) NULL, [Telefon] VARCHAR(50) NULL, [EPosta] VARCHAR(50) NULL, [Eklenme_Zamani] DATETIME NOT NULL, [Guncelleme_Zamani] DATETIME NOT NULL ) CREATE TABLE [dbo].[Siparis]( [Siparis_ID] INT IDENTITY(1,1) NOT NULL, [Durum] INT NOT NULL, --0 : Hazırlanıyor / 1 : Teslim edildi / 2 : iptal vb... [Siparis_NO] VARCHAR(50) NULL, --Numeratörden otomatik sipariş no alır. Sip.0000001 gibi. [Musteri_ID] INT NOT NULL, [Musteri_Unvani] VARCHAR(200) NULL, --Müşterinin Adı Soyadı set edilir. [Teslimat_Adresi_ID] INT, --Adres tablosundaki, Adres_ID den ilişkiyi kurar. [Adres] VARCHAR(200) NULL, --Adres_ID'den seçilen değer otomatik set edilir, kullanıcı değiştirebilir. [Sehir] VARCHAR(50) NULL, --Adres_ID'den seçilen değer otomatik set edilir, kullanıcı değiştirebilir. [Ilce] VARCHAR(50) NULL, --Adres_ID'den seçilen değer otomatik set edilir, kullanıcı değiştirebilir. [Telefon] VARCHAR(50) NULL, --Adres_ID'den seçilen değer otomatik set edilir, kullanıcı değiştirebilir. [EPosta] VARCHAR(50) NULL, --Adres_ID'den seçilen değer otomatik set edilir, kullanıcı değiştirebilir. [Eklenme_Zamani] DATETIME NOT NULL, [Guncelleme_Zamani] DATETIME NOT NULL ) CREATE TABLE [dbo].[Birim]( [Birim_ID] INT IDENTITY(1,1) NOT NULL, [Birim_Kodu] VARCHAR(100) NULL, --Ad. Lt, Kg gibi kısaltmalar için kullanılabilir. [Birim_Adi] VARCHAR(200) NOT NULL --Adet, Litre, Kilogram gibi daha uzun açıklaması yazılır. ) CREATE TABLE [dbo].[Urun]( [Urun_ID] INT IDENTITY(1,1) NOT NULL, [Urun_Kodu] VARCHAR(100) NULL, --Numeratörden otomatik kod atanabilir. Ürün.000001 gibi. [Urun_Adi] VARCHAR(200) NOT NULL, --Boş olmamalı. [Birim_ID] VARCHAR(200) NOT NULL, --Boş olmamalı. Birim tablosundaki Birim_ID ile ilişkili. [Satis_Birim_Fiyati] FLOAT NOT NULL, --Sabit bir fiyattan satilan bir ürün ise. (Her müşteriye.) ) CREATE TABLE [dbo].[Siparis_Satiri]( [Siparis_Satiri_ID] INT IDENTITY(1,1) NOT NULL, [Siparis_ID] INT NOT NULL, --Sipariş tablosundaki, Siparis_ID alanı ile ilişkili. [Urun_ID] INT NOT NULL, --Urunler Tablosundaki, Urun_ID ile ilişkili. [Urun_Aciklamasi] VARCHAR(200) NULL, --Ürün adının haricinde ekstra ürün ile ilgili eklenmek istenen not. [Miktar] FLOAT NOT NULL, --Boş olmamalı. [Birim_ID] INT NOT NULL, --Ürün kartındaki Birim_ID çekilebilir. İsteğe göre diğer birimler seçilebilir. [Birim_Fiyat] FLOAT NOT NULL, --Boş olmamalı. Boş ise 0'a set edilmeli. [Tutar] FLOAT NOT NULL, --Miktar x Birim Fiyat. ) ALTER TABLE [dbo].[Siparis_Satiri] WITH NOCHECK ADD FOREIGN KEY([Siparis_ID]) REFERENCES [dbo].[Siparis] ([Siparis_ID]) ALTER TABLE [dbo].[Adres] WITH NOCHECK ADD FOREIGN KEY([Musteri_ID]) REFERENCES [dbo].[Musteri] ([Musteri_ID]) ALTER TABLE [dbo].[Siparis] WITH NOCHECK ADD FOREIGN KEY([Musteri_ID]) REFERENCES [dbo].[Musteri] ([Musteri_ID]) ALTER TABLE [dbo].[Siparis] WITH NOCHECK ADD FOREIGN KEY([Teslimat_Adresi_ID]) REFERENCES [dbo].[Adres] ([Adres_ID]) ALTER TABLE [dbo].[Urun] WITH NOCHECK ADD FOREIGN KEY([Birim_ID]) REFERENCES [dbo].[Birim] ([Birim_ID]) ALTER TABLE [dbo].[Siparis_Satiri] WITH NOCHECK ADD FOREIGN KEY([Siparis_ID]) REFERENCES [dbo].[Siparis] ([Siparis_ID]) ALTER TABLE [dbo].[Siparis_Satiri] WITH NOCHECK ADD FOREIGN KEY([Birim_ID]) REFERENCES [dbo].[Birim] ([Birim_ID])

mümkünse video anlatım da yaparsanız çok iyi olacaktır