Konuyu Paylaş : facebook gplus twitter

Konuyu Oyla:
  • Derecelendirme: 5/5 - 2 oy
  • 1
  • 2
  • 3
  • 4
  • 5
MS-SQL Server Procedure Tavsiyeleriniz
#1
Merhaba,
Web hosting üzerinde kullandığım bir MS-SQL veritabanım var. Farklı proje türlerinde (Web, VCL, FMX vb.) kullanıcı kontrol amaçlı, ortak kullanmak istediğim bir  prosedür oluşturdum. 

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
 @KulaniciAdi NVARCHAR(20),
 @Sifre NVARCHAR(20)
 AS
BEGIN
 SET NOCOUNT ON;
 
 DECLARE @PersonelId INT
 
 SELECT @PersonelId = PERSONEL.ID FROM PERSONEL
 WHERE (KULLANICI_ADI = @KulaniciAdi) AND (SIFRE = @Sifre) 
 
 IF @PersonelId IS NOT NULL
 BEGIN
   UPDATE PERSONEL
   SET SON_GIRIS = GETDATE()
   WHERE ID = @PersonelId
   SELECT @PersonelId ID -- Kullanıcı ID'sini al - Giriş başaralı
   END
 ELSE
   SELECT - 1 -- Giriş başarısız
END

Yukarıdaki kullanım prensibinde güvenlik açısından bir sorun olur mu?
Daha sade yazılabilir mi veya farklı bir tavsiye/öneri sunabilir misiniz?
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#2
Merhaba,

Fesih Bey çok platformlu projelerde, projenin geleceği açısından ve gerek sağlığı, gerekse güvenliği açısından arada bir web servis/api katmanı oluşturmak daha konforlu olacaktır.

Veritabanına doğrudan erişim çoğu zaman "pipe", "pool" ve "connection" denetimlerinin programın ötesine geçmesine neden olabiliyor.

Ek olarak, yapılacak sistematik ve çeşitli detaylarda birden fazla tablodan veri okuma-veri yazma-veri güncelleme işlemleri yapacak tarzda işlemler için kodcunun veritabanı ve tablo yapısına hakim olma gerekliliğini ortadan kaldırarak, hem güvenli hemde hızlı geliştirmeye olanak sunuyor. Böylece "kullanıcı kontrolü" gibi çok komplike olmayan kodlarda da tek sefere mahsus kontrol yapısını kodlayıp, sunum katmanlarında (TForm) doğrudan entegrasyon ile tüm işi ws/api katmanına yıkabilirsiniz. Bu tarz "n adet aplikasyonlu" işlerde tercih ettiğim yöntem budur.

İyi günler hocam.
{ talk is cheap show me the code. }
Cevapla
#3
(21-07-2017, Saat: 15:16)Abdullah Ilgaz Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlMerhaba,

Fesih Bey çok platformlu projelerde, projenin geleceği açısından ve gerek sağlığı, gerekse güvenliği açısından arada bir web servis/api katmanı oluşturmak daha konforlu olacaktır.

Veritabanına doğrudan erişim çoğu zaman "pipe", "pool" ve "connection" denetimlerinin programın ötesine geçmesine neden olabiliyor.

Ek olarak, yapılacak sistematik ve çeşitli detaylarda birden fazla tablodan veri okuma-veri yazma-veri güncelleme işlemleri yapacak tarzda işlemler için kodcunun veritabanı ve tablo yapısına hakim olma gerekliliğini ortadan kaldırarak, hem güvenli hemde hızlı geliştirmeye olanak sunuyor. Böylece "kullanıcı kontrolü" gibi çok komplike olmayan kodlarda da tek sefere mahsus kontrol yapısını kodlayıp, sunum katmanlarında (TForm) doğrudan entegrasyon ile tüm işi ws/api katmanına yıkabilirsiniz. Bu tarz "n adet aplikasyonlu" işlerde tercih ettiğim yöntem budur.

İyi günler hocam.

Haklısınız (Elbetteki doğrudan bağlantı olmaması gerekiyor). O halde soruyu hafif değiştirerek (falsolu Smile ) sorayım.  
Web service/API katmanı tarafından kullanılacak, veri tabanı katmanında, Kullanıcı Doğrulama işlemleri için SQL kullanım tarzı nasıl olmalı?
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#4
(21-07-2017, Saat: 15:04)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlMerhaba,
Web hosting üzerinde kullandığım bir MS-SQL veritabanım var. Farklı proje türlerinde (Web, VCL, FMX vb.) kullanıcı kontrol amaçlı, ortak kullanmak istediğim bir  prosedür oluşturdum. 

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
 @KulaniciAdi NVARCHAR(20),
 @Sifre NVARCHAR(20)
 AS
BEGIN
 SET NOCOUNT ON;
 
 DECLARE @PersonelId INT
 
 SELECT @PersonelId = PERSONEL.ID FROM PERSONEL
 WHERE (KULLANICI_ADI = @KulaniciAdi) AND (SIFRE = @Sifre) 
 
 IF @PersonelId IS NOT NULL
 BEGIN
   UPDATE PERSONEL
   SET SON_GIRIS = GETDATE()
   WHERE ID = @PersonelId
   SELECT @PersonelId ID -- Kullanıcı ID'sini al - Giriş başaralı
   END
 ELSE
   SELECT - 1 -- Giriş başarısız
END

Yukarıdaki kullanım prensibinde güvenlik açısından bir sorun olur mu?
Daha sade yazılabilir mi veya farklı bir tavsiye/öneri sunabilir misiniz?

Gördüğüm kadarı ile PERSONEL tablosundaki KULLANICI_ADI alanı unique index. Aksi durumda, aynı kullanıcı adı ve şifresine sahip birden fazla kayıt olur ise bu durum sıkıntı yaratabilir. Aynı zamanda, tablo üzerinde bir güncelleme işlemi başlattığınız için, bir transaction başlatmanız ve BEGIN TRY/BEGIN CATCH ile oluşabilecek hataları da kontrol etmek isteyebilirsiniz. Örneğin:

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
  @KulaniciAdi NVARCHAR(20),
   @Sifre NVARCHAR(20)
AS
BEGIN

  SET NOCOUNT ON;

  DECLARE
    @PersonelID INT

  -- Kullanıcı Adı'nın unique index olma durumu söz konusu olmalı.
  SELECT @PersonelID = ISNULL(
                                                 (
                                                   SELECT TOP 1
                                                     ID
                                                   FROM dbo.PERSONEL
                                                   WHERE
                                                     KULLANICI_ADI = @KulaniciAdi AND 
                                                     SIFRE = @Sifre
                                                   ), -1
                                                 ) 

  IF @PersonelID <> -1
  BEGIN
    BEGIN TRANSACTION;

    BEGIN TRY
      UPDATE dbo.PERSONEL
      SET SON_GIRIS = GETDATE()
      WHERE
        ID = @PersonelID 
    END TRY
    BEGIN CATCH
      IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

      RAISERROR('Belirtilen kullanıcı (%s) sistemde kayıtlı değil.!', 16, 1, @KullaniciAdi)

      RETURN -1
    END CATCH

    IF @@TRANCOUNT > 0 COMMIT TRANSACTION;
  END

  SELECT @PersonelID AS ID
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#5
(21-07-2017, Saat: 15:32)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlHaklısınız (Elbetteki doğrudan bağlantı olmaması gerekiyor). O halde soruyu hafif değiştirerek (falsolu Smile ) sorayım.  
Web service/API katmanı tarafından kullanılacak, veri tabanı katmanında, Kullanıcı Doğrulama işlemleri için SQL kullanım tarzı nasıl olmalı?

Hocam bu tarz kontrol sorgularında genellikle yaygın olarak View kullanıyorum. .Net web service altyapısında ado ile çalışan bir connection dll dosyası oluşturdum. Tüm obje modellemelerini, sınıfları burada yapıyorum. Böylece tek tip sınıf-obje-metod mantığı ile her platformda aynı yapı çalışıyor.

Akış şeması şu şekilde oluyor hocam;

Kontrol: SQL -> Web Service <> Sunum Platformu
Veri basma: SQL <- Web Service < Sunum Platformu
Veri çekme: SQL -> Web Service > Sunum Platformu



Save
{ talk is cheap show me the code. }
Cevapla
#6
(21-07-2017, Saat: 15:38)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
(21-07-2017, Saat: 15:04)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlMerhaba,
Web hosting üzerinde kullandığım bir MS-SQL veritabanım var. Farklı proje türlerinde (Web, VCL, FMX vb.) kullanıcı kontrol amaçlı, ortak kullanmak istediğim bir  prosedür oluşturdum. 

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
 @KulaniciAdi NVARCHAR(20),
 @Sifre NVARCHAR(20)
 AS
BEGIN
 SET NOCOUNT ON;
 
 DECLARE @PersonelId INT
 
 SELECT @PersonelId = PERSONEL.ID FROM PERSONEL
 WHERE (KULLANICI_ADI = @KulaniciAdi) AND (SIFRE = @Sifre) 
 
 IF @PersonelId IS NOT NULL
 BEGIN
   UPDATE PERSONEL
   SET SON_GIRIS = GETDATE()
   WHERE ID = @PersonelId
   SELECT @PersonelId ID -- Kullanıcı ID'sini al - Giriş başaralı
   END
 ELSE
   SELECT - 1 -- Giriş başarısız
END

Yukarıdaki kullanım prensibinde güvenlik açısından bir sorun olur mu?
Daha sade yazılabilir mi veya farklı bir tavsiye/öneri sunabilir misiniz?

Gördüğüm kadarı ile PERSONEL tablosundaki KULLANICI_ADI alanı unique index. Aksi durumda, aynı kullanıcı adı ve şifresine sahip birden fazla kayıt olur ise bu durum sıkıntı yaratabilir. Aynı zamanda, tablo üzerinde bir güncelleme işlemi başlattığınız için, bir transaction başlatmanız ve BEGIN TRY/BEGIN CATCH ile oluşabilecek hataları da kontrol etmek isteyebilirsiniz. Örneğin:

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
  @KulaniciAdi NVARCHAR(20),
   @Sifre NVARCHAR(20)
AS
BEGIN

  SET NOCOUNT ON;

  DECLARE
    @PersonelID INT

  -- Kullanıcı Adı'nın unique index olma durumu söz konusu olmalı.
  SELECT @PersonelID = ISNULL(
                                                 (
                                                   SELECT TOP 1
                                                     ID
                                                   FROM dbo.PERSONEL
                                                   WHERE
                                                     KULLANICI_ADI = @KulaniciAdi AND 
                                                     SIFRE = @Sifre
                                                   ), -1
                                                 ) 

  IF @PersonelID <> -1
  BEGIN
    BEGIN TRANSACTION;

    BEGIN TRY
      UPDATE dbo.PERSONEL
      SET SON_GIRIS = GETDATE()
      WHERE
        ID = @PersonelID 
    END TRY
    BEGIN CATCH
      IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

      RAISERROR('Belirtilen kullanıcı (%s) sistemde kayıtlı değil.!', 16, 1, @KullaniciAdi)

      RETURN -1
    END CATCH

    IF @@TRANCOUNT > 0 COMMIT TRANSACTION;
  END

  SELECT @PersonelID AS ID

Tavsiyeleriniz için teşekkür ederim hocam. 
KULLANICI_ADI ve SIFRE alanlarının aynı olma ihtimali biraz düşük, fakat yazılımcı olarak; milyonda bir ihtimal bile olsa bu durum göz ardı edilmemelidir. Bu noktada, her iki alanın Unique olmaması (aynı kullanıcı adı ve şifreye sahip birden fazla kullanıcı) durumunda, metod nasıl olmalı? 
Yada mevcut sistemlerde (NVİ, e-Devlet vb.) olduğu gibi  unique olması açısından TC Kimlik Numarası mı kullanılmalı? 
SON_GIRIS alanının farklı bir tablo üzerinde bir PersonelId ile yer alması durumunda, yine de transaction başlatmak gerekiyor mu?
While true do; Hayat döngüsü, kısır değildir! Yapılan bir yanlış, o döngünün dışına çıkmanızı sağlayacaktır.
WWW
Cevapla
#7
(21-07-2017, Saat: 15:54)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
(21-07-2017, Saat: 15:38)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlGördüğüm kadarı ile PERSONEL tablosundaki KULLANICI_ADI alanı unique index. Aksi durumda, aynı kullanıcı adı ve şifresine sahip birden fazla kayıt olur ise bu durum sıkıntı yaratabilir. Aynı zamanda, tablo üzerinde bir güncelleme işlemi başlattığınız için, bir transaction başlatmanız ve BEGIN TRY/BEGIN CATCH ile oluşabilecek hataları da kontrol etmek isteyebilirsiniz. Örneğin:

CREATE PROCEDURE [dbo].[Kullanici_Dogrula]
  @KulaniciAdi NVARCHAR(20),
   @Sifre NVARCHAR(20)
AS
BEGIN

  SET NOCOUNT ON;

  DECLARE
    @PersonelID INT

  -- Kullanıcı Adı'nın unique index olma durumu söz konusu olmalı.
  SELECT @PersonelID = ISNULL(
                                                 (
                                                   SELECT TOP 1
                                                     ID
                                                   FROM dbo.PERSONEL
                                                   WHERE
                                                     KULLANICI_ADI = @KulaniciAdi AND 
                                                     SIFRE = @Sifre
                                                   ), -1
                                                 ) 

  IF @PersonelID <> -1
  BEGIN
    BEGIN TRANSACTION;

    BEGIN TRY
      UPDATE dbo.PERSONEL
      SET SON_GIRIS = GETDATE()
      WHERE
        ID = @PersonelID 
    END TRY
    BEGIN CATCH
      IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

      RAISERROR('Belirtilen kullanıcı (%s) sistemde kayıtlı değil.!', 16, 1, @KullaniciAdi)

      RETURN -1
    END CATCH

    IF @@TRANCOUNT > 0 COMMIT TRANSACTION;
  END

  SELECT @PersonelID AS ID

Tavsiyeleriniz için teşekkür ederim hocam. 
KULLANICI_ADI ve SIFRE alanlarının aynı olma ihtimali biraz düşük, fakat yazılımcı olarak; milyonda bir ihtimal bile olsa bu durum göz ardı edilmemelidir. Bu noktada, her iki alanın Unique olmaması (aynı kullanıcı adı ve şifreye sahip birden fazla kullanıcı) durumunda, metod nasıl olmalı? 
Yada mevcut sistemlerde (NVİ, e-Devlet vb.) olduğu gibi  unique olması açısından TC Kimlik Numarası mı kullanılmalı? 
SON_GIRIS alanının farklı bir tablo üzerinde bir PersonelId ile yer alması durumunda, yine de transaction başlatmak gerekiyor mu?

 Rica ederim üstad. Benim tablo tasarımı sırasında düsturum, her bir tablo içinde ID isminde otomatik artan değer alan bir primary key'e sahip alan ve tablo içinde bir unique index. Tablonuzda unique index olmaya müsait bir alan var ise muhakkak bir unique index verin. Ancak yok ise, örneğinizden yola çıkarak aynı kullanıcı adı ve şifresine sahip birden fazla kayıt gelebilir. Bu durumda hangi kullanıcının son giriş tarihini güncelleyeceğinizi bilemezsiniz. Hata durumu oluşmasın (subquery returned more than one value) diye TOP 1 kıstası koyduk.

Ama mantıksak olarak doğru bir durum değil tabii. Personel tablonuzun durumuna bakarak, KullanıcıAdı alanının unique olması gerektiğini düşünüyorum. Çeşitli nedenlerden ötürü unique olamıyor ise, tablonuzda unique olacak bir alan üzerinden sorgulama yapmanızı öneriyorum.

 Transaction ve Begin Try/Begin Catch bloklarına gelince. Bu bir tercih meselesi esasen. Ben kullanıcıların karşısına çıkacak hata mesajlarının (SQL Server'ın diline bağlı olarak) ancak programcıların anlayabileceği hata mesajları olmasını sevmiyorum. Kullanıcının anlayabileceği hata mesajları ile karşılaşması gerektiğine inanıyorum. Bu nedenle benzer bir yapı kullanıyorum. Bu tarz bir yapı aynı zamanda, oluşan hata mesajlarını da log'layabilmeme yardımcı oluyor.

 Tabii transaction'un opsiyonel olma durumundan zaruri olma durumuna geçtiği bazı özel şartlarda olabilir. Örneğin:

UPDATE Tablonuz
SET
Alanınız = ISNULL(@NullableParameteniz, Alanınız)
WHERE
UniqueIndexiniz = UniqueIndexDegeriniz

UPDATE Tablonuz2
SET
Alanınız2 = ISNULL(@NullableParameteniz2, Alanınız2)
WHERE
UniqueIndexiniz2 = UniqueIndexDegeriniz2

DELETE Tablonuz3
WHERE
UniqueIndexiniz3 = UniqueIndexDegeriniz3

gibi birden fazla tablonun etkilendiği durum söz konusu ise kesinlikle bir transaction başlatmanızı öneririm. Aksi taktirde veri tabanı bütünlüğünü bozabilirsiniz. Birinci tablonuzu hatası bir şekilde güncelleyebilir ama ikinci tablonuzun güncellenmesi sırasında bir hata ile karşılaşabilirsiniz. Bir transaction başlatmadığınız için birinci tablonuz güncel değeri ile kalmış olur. Ya da bir ve ikinci tablolarınızı hatası bir şekilde güncelleyebilir ama üçüncü tablonuzdan kayıt siler iken bir hata ile karşılaşabilirsiniz. Bu durumda, birinci ve ikinci tablonuzda olmaması gereken güncellemeler kalıcı hale gelmiş olur. Yukarıdaki korumasız sorgu aşağıdaki gibi kodlanır ise veri bütünlüğünü de sağlamış olursunuz.(Ya hep/ya hiç, ya herro/ya merro)

BEGIN TRANSACTION;

  BEGIN TRY
    UPDATE Tablonuz
    SET
      Alanınız = ISNULL(@NullableParameteniz, Alanınız)
    WHERE
      UniqueIndexiniz = UniqueIndexDegeriniz

   UPDATE Tablonuz2
    SET
     Alanınız2 = ISNULL(@NullableParameteniz2, Alanınız2)
   WHERE
     UniqueIndexiniz2 = UniqueIndexDegeriniz2

   DELETE Tablonuz3
    WHERE
     UniqueIndexiniz3 = UniqueIndexDegeriniz3
  END TRY
  BEGIN CATCH
    IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

    RAISERRROR('Hata mesajınız', 16, 1)

    RETURN -1
  END CATCH

  IF @@TRANCOUNT > 0 COMMIT TRANSACTION;

Bu sayede, işlemleriniz ya tam manası ile olumlu olur; ya da hiç bir şey olmamış gibi olur. Ayrıca Begin Catch/End Catch blogları arasında oluşan hataları loglamak ister iseniz, aşağıdaki gibi bir procedure kullanabilirsiniz:

CREATE PROCEDURE [dbo].[sysSaveErrors]
     @Message NVARCHAR(500) = NULL
AS
DECLARE
   @ErrorNumber INT

IF ERROR_NUMBER() IS NULL RETURN;

-- Triggerların içinde kaç tane begin transaction olursa olsun; bir adet rollback transaction tüm transactionları rollback eder.
-- Örneğin 3 adet Begin Transaction'ın ardından global @@TRANCOUNT değeri 3 iken, bir adet rollback çağrımı @@TRANCOUNT değerini
 -- otomatikman sıfıra çeker. Bu değerin sıfır olması durumunda SQL Server 3609 hata mesajını verir.!
IF (SELECT ERROR_NUMBER()) IN (3609, 3616) RETURN; 

IF XACT_STATE() = -1 -- Açık kalmış bir transaction var ve açık bir transaction varken
BEGIN -- SQL Server Kayıt girişi/güncelleme gibi işlemlere müsaade etmez.
   RAISERROR('Açık kalmış bir transaction var. Lütfen önce ROLLBACK yapın.', 16,1);
  RETURN;
END

SET NOCOUNT ON;

SET @ErrorNumber = ERROR_NUMBER()

BEGIN TRY
   INSERT INTO Errors(
                                 ErrorNumber,
                                 ErrorLine,
                                 ErrorMessage,
                                 ErrorProcedure,
                                  ErrorSeverity,
                                 ErrorState
                               )
SELECT
   ERROR_NUMBER(),
  ERROR_LINE(),
  CASE
    WHEN ISNULL(@Message, '') = '' THEN ''
    ELSE @Message + CHAR(13) + CHAR(10)
  END + ERROR_MESSAGE(),
  ERROR_PROCEDURE(),
  ERROR_SEVERITY(),
  ERROR_STATE();
END TRY
BEGIN CATCH
END CATCH

SET NOCOUNT OFF;

DECLARE 
  @ErrorMessage NVARCHAR(4000)

SET @ErrorMessage = 
   CASE
     WHEN ISNULL(@Message, '') = '' THEN ''
     ELSE @Message + CHAR(13) + CHAR(10)
   END + ERROR_MESSAGE()

IF EXISTS(
   SELECT *
   FROM master.sys.messages
   WHERE
     message_id = @ErrorNumber AND 
     @ErrorNumber >= 50000
) 
 RAISERROR(@ErrorNumber , 16,1)
ELSE 
RAISERROR(@ErrorMessage, 16,1);

Not: Maalesef kod editörlerimiz tab tuşlarını göz ardı ediyor. Dolayısı ile örnek olması bağlamında burada paylaştığımız kodlar biraz dağınık görünüyor. Biraz düzenlemeye çalıştım ama, ancak bu kadar olabildi. :-)
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#8
(21-07-2017, Saat: 16:21)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye Ol
(21-07-2017, Saat: 15:54)Fesih ARSLAN Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlTavsiyeleriniz için teşekkür ederim hocam. 
KULLANICI_ADI ve SIFRE alanlarının aynı olma ihtimali biraz düşük, fakat yazılımcı olarak; milyonda bir ihtimal bile olsa bu durum göz ardı edilmemelidir. Bu noktada, her iki alanın Unique olmaması (aynı kullanıcı adı ve şifreye sahip birden fazla kullanıcı) durumunda, metod nasıl olmalı? 
Yada mevcut sistemlerde (NVİ, e-Devlet vb.) olduğu gibi  unique olması açısından TC Kimlik Numarası mı kullanılmalı? 
SON_GIRIS alanının farklı bir tablo üzerinde bir PersonelId ile yer alması durumunda, yine de transaction başlatmak gerekiyor mu?

 Rica ederim üstad. Benim tablo tasarımı sırasında düsturum, her bir tablo içinde ID isminde otomatik artan değer alan bir primary key'e sahip alan ve tablo içinde bir unique index. Tablonuzda unique index olmaya müsait bir alan var ise muhakkak bir unique index verin. Ancak yok ise, örneğinizden yola çıkarak aynı kullanıcı adı ve şifresine sahip birden fazla kayıt gelebilir. Bu durumda hangi kullanıcının son giriş tarihini güncelleyeceğinizi bilemezsiniz. Hata durumu oluşmasın (subquery returned more than one value) diye TOP 1 kıstası koyduk.

Ama mantıksak olarak doğru bir durum değil tabii. Personel tablonuzun durumuna bakarak, KullanıcıAdı alanının unique olması gerektiğini düşünüyorum. Çeşitli nedenlerden ötürü unique olamıyor ise, tablonuzda unique olacak bir alan üzerinden sorgulama yapmanızı öneriyorum.

 Transaction ve Begin Try/Begin Catch bloklarına gelince. Bu bir tercih meselesi esasen. Ben kullanıcıların karşısına çıkacak hata mesajlarının (SQL Server'ın diline bağlı olarak) ancak programcıların anlayabileceği hata mesajları olmasını sevmiyorum. Kullanıcının anlayabileceği hata mesajları ile karşılaşması gerektiğine inanıyorum. Bu nedenle benzer bir yapı kullanıyorum. Bu tarz bir yapı aynı zamanda, oluşan hata mesajlarını da log'layabilmeme yardımcı oluyor.

 Tabii transaction'un opsiyonel olma durumundan zaruri olma durumuna geçtiği bazı özel şartlarda olabilir. Örneğin:

UPDATE Tablonuz
SET
Alanınız = ISNULL(@NullableParameteniz, Alanınız)
WHERE
UniqueIndexiniz = UniqueIndexDegeriniz

UPDATE Tablonuz2
SET
Alanınız2 = ISNULL(@NullableParameteniz2, Alanınız2)
WHERE
UniqueIndexiniz2 = UniqueIndexDegeriniz2

DELETE Tablonuz3
WHERE
UniqueIndexiniz3 = UniqueIndexDegeriniz3

gibi birden fazla tablonun etkilendiği durum söz konusu ise kesinlikle bir transaction başlatmanızı öneririm. Aksi taktirde veri tabanı bütünlüğünü bozabilirsiniz. Birinci tablonuzu hatası bir şekilde güncelleyebilir ama ikinci tablonuzun güncellenmesi sırasında bir hata ile karşılaşabilirsiniz. Bir transaction başlatmadığınız için birinci tablonuz güncel değeri ile kalmış olur. Ya da bir ve ikinci tablolarınızı hatası bir şekilde güncelleyebilir ama üçüncü tablonuzdan kayıt siler iken bir hata ile karşılaşabilirsiniz. Bu durumda, birinci ve ikinci tablonuzda olmaması gereken güncellemeler kalıcı hale gelmiş olur. Yukarıdaki korumasız sorgu aşağıdaki gibi kodlanır ise veri bütünlüğünü de sağlamış olursunuz.(Ya hep/ya hiç, ya herro/ya merro)

BEGIN TRANSACTION;

  BEGIN TRY
    UPDATE Tablonuz
    SET
      Alanınız = ISNULL(@NullableParameteniz, Alanınız)
    WHERE
      UniqueIndexiniz = UniqueIndexDegeriniz

   UPDATE Tablonuz2
    SET
     Alanınız2 = ISNULL(@NullableParameteniz2, Alanınız2)
   WHERE
     UniqueIndexiniz2 = UniqueIndexDegeriniz2

   DELETE Tablonuz3
    WHERE
     UniqueIndexiniz3 = UniqueIndexDegeriniz3
  END TRY
  BEGIN CATCH
    IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

    RAISERRROR('Hata mesajınız', 16, 1)

    RETURN -1
  END CATCH

  IF @@TRANCOUNT > 0 COMMIT TRANSACTION;

Bu sayede, işlemleriniz ya tam manası ile olumlu olur; ya da hiç bir şey olmamış gibi olur. Ayrıca Begin Catch/End Catch blogları arasında oluşan hataları loglamak ister iseniz, aşağıdaki gibi bir procedure kullanabilirsiniz:

CREATE PROCEDURE [dbo].[sysSaveErrors]
     @Message NVARCHAR(500) = NULL
AS
DECLARE
   @ErrorNumber INT

IF ERROR_NUMBER() IS NULL RETURN;

-- Triggerların içinde kaç tane begin transaction olursa olsun; bir adet rollback transaction tüm transactionları rollback eder.
-- Örneğin 3 adet Begin Transaction'ın ardından global @@TRANCOUNT değeri 3 iken, bir adet rollback çağrımı @@TRANCOUNT değerini
 -- otomatikman sıfıra çeker. Bu değerin sıfır olması durumunda SQL Server 3609 hata mesajını verir.!
IF (SELECT ERROR_NUMBER()) IN (3609, 3616) RETURN; 

IF XACT_STATE() = -1 -- Açık kalmış bir transaction var ve açık bir transaction varken
BEGIN -- SQL Server Kayıt girişi/güncelleme gibi işlemlere müsaade etmez.
   RAISERROR('Açık kalmış bir transaction var. Lütfen önce ROLLBACK yapın.', 16,1);
  RETURN;
END

SET NOCOUNT ON;

SET @ErrorNumber = ERROR_NUMBER()

BEGIN TRY
   INSERT INTO Errors(
                                 ErrorNumber,
                                 ErrorLine,
                                 ErrorMessage,
                                 ErrorProcedure,
                                  ErrorSeverity,
                                 ErrorState
                               )
SELECT
   ERROR_NUMBER(),
  ERROR_LINE(),
  CASE
    WHEN ISNULL(@Message, '') = '' THEN ''
    ELSE @Message + CHAR(13) + CHAR(10)
  END + ERROR_MESSAGE(),
  ERROR_PROCEDURE(),
  ERROR_SEVERITY(),
  ERROR_STATE();
END TRY
BEGIN CATCH
END CATCH

SET NOCOUNT OFF;

DECLARE 
  @ErrorMessage NVARCHAR(4000)

SET @ErrorMessage = 
   CASE
     WHEN ISNULL(@Message, '') = '' THEN ''
     ELSE @Message + CHAR(13) + CHAR(10)
   END + ERROR_MESSAGE()

IF EXISTS(
   SELECT *
   FROM master.sys.messages
   WHERE
     message_id = @ErrorNumber AND 
     @ErrorNumber >= 50000
) 
 RAISERROR(@ErrorNumber , 16,1)
ELSE 
RAISERROR(@ErrorMessage, 16,1);

Not: Maalesef kod editörlerimiz tab tuşlarını göz ardı ediyor. Dolayısı ile örnek olması bağlamında burada paylaştığımız kodlar biraz dağınık görünüyor. Biraz düzenlemeye çalıştım ama, ancak bu kadar olabildi. :-)

Tuğrul Bey,

Konuya ek olarak bir soru da benden gelsin;

Guid kullanımı konusunda izlediğiniz bir yol haritası var mı? Son yılların revaçta olan meşhur yeni id ve indeksleme yapısı olan guid ile birbirine bağlı 8-10 tabloda içinde sadece guid olan ilişkisel yapılar kurgulanıyor. Biz halâ mapping ve diagram ile uğraşıp ilişkisel veritabanı yapısı kurgularken guid yapısını ekleme yada uygulama yoluna gitmedik. Bu konuda düşünceleriniz nelerdir?
{ talk is cheap show me the code. }
Cevapla
#9
(21-07-2017, Saat: 16:32)Abdullah Ilgaz Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Giriş yap veya Üye OlTuğrul Bey,

Konuya ek olarak bir soru da benden gelsin;

Guid kullanımı konusunda izlediğiniz bir yol haritası var mı? Son yılların revaçta olan meşhur yeni id ve indeksleme yapısı olan guid ile birbirine bağlı 8-10 tabloda içinde sadece guid olan ilişkisel yapılar kurgulanıyor. Biz halâ mapping ve diagram ile uğraşıp ilişkisel veritabanı yapısı kurgularken guid yapısını ekleme yada uygulama yoluna gitmedik. Bu konuda düşünceleriniz nelerdir?

 Merhaba Abdullah Bey, ilişkisel tabloları GUID üzerinden neden bağladıklarını bilmek lazım. Mantıklı bir nedeni var ise elbette olabilir. Ama bana pek de manidar gelmiyor açıkçası.

Her bir GUID 37 basamaktan müteşekkil. Bu veritabanında daha fazla yer kaplanacak anlamına geliyor. 2.147.483.647 (Signed MaxInt) kadar kayıt sayısı yeterli ise, 4 byte'ta temsil edilebilecek bir veri için, ilgili tabloda fazladan 33 byte daha kullanmanız icap ediyor. Bu da 1 milyon kayıtta, 33 milyon byte fazladan alan işgali demek. Aynı zamanda hepimizin malumu olduğu üzere arama, konumlanma algoritmaları tamsayı alanları üzerinde daha başarılı çalışıyor.

Sizin bildiğiniz faydalı bir kullanım alanı var ise, paylaşırsanız konu hakkında yorum yapabiliriz. Benim için her zaman birinci öncelik performans olduğu için, ID INT (AutoInc) alanları yerine GUID alanları tercih etmiyorum.
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#10
benim GUID kullanmak sebebi veritabanı transferlerinde kayıt eşlemesi için, şöyleki
tablette veri (GUID başlangıcı) oluştu (SQLlite),
lokaldeki veritabanına (firebird olsun) aktarırken ekleme/günleme için GUID ile kontrol ediyorum
buluttaki veritabanına (MySQL olsun) aktarırken yine ekleme/günleme için GUID ile kontrol ediyorum
aktarımları veritabanınlarının Primary key'leri ile kontrol etmek bana sağlıklı gelmedi
veritabanı/tablo yeniden oluşturma ya da trunc edilmesi ile sıfırlanan primary key nedeniyle
Cevapla

Konuyu Paylaş : facebook gplus twitter



Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  Sql Server Otomatik Kurulum narkotik 7 140 13-01-2018, Saat: 22:30
Son Yorum: narkotik
  Raporlamada hangisi daha performanslıdır View? Store Procedure? adelphiforumz 13 359 23-12-2017, Saat: 22:09
Son Yorum: FiRewaLL
  SQL Server veritabanını Suspect & Emergency modundan kurtarmak Abdullah ILGAZ 2 178 23-12-2017, Saat: 08:52
Son Yorum: Abdullah ILGAZ
  SQL Server - Client Güncelleme CaglarCoskun 10 429 14-08-2017, Saat: 11:06
Son Yorum: uparlayan
  SQL Server Numarator CaglarCoskun 5 396 05-08-2017, Saat: 15:20
Son Yorum: CaglarCoskun



Konuyu Okuyanlar: 1 Ziyaretçi