Konuyu Oyla:
  • Derecelendirme: 5/5 - 1 oy
  • 1
  • 2
  • 3
  • 4
  • 5
Stored Procedure Nasıl Olmalı!!!
#1
Merhaba,

Bir uygulama yapıyorum ve uygulama içindeki SQL kodları Delphi içinde yazıldı. Fakat bunları birer Stored Procedure olarak kullanmak istiyorum.
Tek tablo ve standart işlemlerde sorun yok.
Örneğin TabloA için 
add_tabloa   insert işlemi ID return
del_tabloa    delete işlemi
set_tabloa    update işlemi
add_tablob
del_tablob
set_tablob

şeklinde yazıp kullanıyorum.

Fakat klasik bir örnek olan bir Fatura kaydı gibi çoklu tablo ve çoklu satır içeren kayıtlarda nasıl bir yol izlenmeli?
Mesela Fatura kayıt işleminde
Bir Fatura Insert
Birden Fazla Fatura Detay
Fatura Bedeli kadar Cari Hesap Hareketi kayıtları oluşturulmalı.

Bu şekildeki business_insert işlemini nasıl kullanmalıyız.
Delphi tarafında 
try
  begin_transaction ile başlayıp
  fatura_insert
  fatura_detaylar_insert
  hesap_hareketi_insert
  commit_transaction
except
  rollback_transaction
end;

bi_tabloa (business_insert_tabloa) gibi yazıp.
begin
begin
_id = add_tabloa;
add_tablob(_id);
commit;

gibi mi olmalı.
PostgreSQL - Linux - Delphi, Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
WWW
Cevapla
#2
Ben stored procedure'leri ve dolayısı ile tabloları OOP prensibine göre düşünürüm. Yani her bir tabloya yapılabilecek işlem adedi kadar (Insert, Update, Delete, Select) stored procedure yazarım ve bu stored procedure'ler sadece kendi işlerini yaparlar.

 Birden fazla tablonun kayıt değişikliklerinden etkilenmesi gerekiyor ise; bu durumda aynı connection üzerinden bir transaction başlatırım, tüm gereken sp'lerimi çağırırım , eğer bir hata olmadı ise commit , hata oldu ise rollback çağrımı yaparım. Psedo kod olarak aşağıdaki gibi;

procedure Save;
begin
  AConnection.BeginTransaction;

  try
    ...
    try
      AProc1.ExecProc;
      ....
      AProc2.ExecProc;
      ....
      AProc3.ExecProc;
    except
      if AConnection.InTransaction then
        AConnection.RollbackTransaction;

      raise;
    end;
  finally
    if AConnection.InTransaction then
      AConnection.Commit;
  end;
end;
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#3
(14-01-2019, Saat: 08:39)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.Ben stored procedure'leri ve dolayısı ile tabloları OOP prensibine göre düşünürüm. Yani her bir tabloya yapılabilecek işlem adedi kadar (Insert, Update, Delete, Select) stored procedure yazarım ve bu stored procedure'ler sadece kendi işlerini yaparlar.

 Birden fazla tablonun kayıt değişikliklerinden etkilenmesi gerekiyor ise; bu durumda aynı connection üzerinden bir transaction başlatırım, tüm gereken sp'lerimi çağırırım , eğer bir hata olmadı ise commit , hata oldu ise rollback çağrımı yaparım. Psedo kod olarak aşağıdaki gibi;

procedure Save;
begin
  AConnection.BeginTransaction;

  try
    ...
    try
      AProc1.ExecProc;
      ....
      AProc2.ExecProc;
      ....
      AProc3.ExecProc;
    except
      if AConnection.InTransaction then
        AConnection.RollbackTransaction;

      raise;
    end;
  finally
    if AConnection.InTransaction then
      AConnection.Commit;
  end;
end;

Peki bu try except finally kod blogunu sp içinde kullanmak doğru mu? Yani 
AProc1.ExecProc;
çağırmak doğru mu? Bütün try except ve transaction yönetimi buradan yapılsa nasıl olur?

Ne kadar doğru bilmiyorum ama bu şekilde olduğunu düşünerek. Datasnap ile mobil uygulama yazdığımızı farz ediyorum. Datasnap Server tarafında da sadece bu sp çağırma işlemi yapılması halinde bütün işlemi tek bir satırla tek bir noktada yapmış oluyoruz.

Yazdığım kod gerçek kod değil. Tamamen örnek olması için öylesine yazdım.
CREATE OR REPLACE FUNCTION public.sp_bi_fatura($fatura, $fatura_detay /*merak ettiğim asıl nokta burası. Çok parametre var Burası nasıl organize ediliyor??? Özellikle birden fazla satır için nasıl oluyor*/)
 RETURNS Integer AS
$BODY$
DECLARE
  _id integer;
BEGIN
  begin
    begin transaction;
    _id := add_fatura();
    add_fatura_detay();
    add_hesap_hareketi();
    commit transaction;
    return _id;
  exception
    rollback transaction;
  end
END;

Tabi bu şekilde yazmak çok meşakkatli olacaktır. Delphi tarafında ki kontrol kolaylığı veya esnekliği RDMS tarafında biraz daha zor olacaktır. Eğer doğru/mantıklı ise tüm bu blokları sp içinde kullanırsak bir sürü parametre göndermek gerekecek.

Bu birden fazla kayıt olan(ilk mesajda da örneğini verdiğim gibi) Fatura başlık bilgisi için Insert yapılacak, Fatura Detayları için birden fazla insert işlemi olacak. Bu şekilde olan kayıtlar için sp ler nasıl organize ediliyor. Parametre olarak array mi kullanılıyor?

Daha önce tecrübe etmediğim için öğrenmek adına soruyorum. RDMS sistemlerinin kendilerine has tip veya yöntemleri var mı? Bunu öğrenmek istiyorum.
PostgreSQL - Linux - Delphi, Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
WWW
Cevapla
#4
(14-01-2019, Saat: 09:15)3ddark Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
(14-01-2019, Saat: 08:39)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.Ben stored procedure'leri ve dolayısı ile tabloları OOP prensibine göre düşünürüm. Yani her bir tabloya yapılabilecek işlem adedi kadar (Insert, Update, Delete, Select) stored procedure yazarım ve bu stored procedure'ler sadece kendi işlerini yaparlar.

 Birden fazla tablonun kayıt değişikliklerinden etkilenmesi gerekiyor ise; bu durumda aynı connection üzerinden bir transaction başlatırım, tüm gereken sp'lerimi çağırırım , eğer bir hata olmadı ise commit , hata oldu ise rollback çağrımı yaparım. Psedo kod olarak aşağıdaki gibi;

procedure Save;
begin
  AConnection.BeginTransaction;

  try
    ...
    try
      AProc1.ExecProc;
      ....
      AProc2.ExecProc;
      ....
      AProc3.ExecProc;
    except
      if AConnection.InTransaction then
        AConnection.RollbackTransaction;

      raise;
    end;
  finally
    if AConnection.InTransaction then
      AConnection.Commit;
  end;
end;

Peki bu try except finally kod blogunu sp içinde kullanmak doğru mu? Yani 
AProc1.ExecProc;
çağırmak doğru mu? Bütün try except ve transaction yönetimi buradan yapılsa nasıl olur?

Ne kadar doğru bilmiyorum ama bu şekilde olduğunu düşünerek. Datasnap ile mobil uygulama yazdığımızı farz ediyorum. Datasnap Server tarafında da sadece bu sp çağırma işlemi yapılması halinde bütün işlemi tek bir satırla tek bir noktada yapmış oluyoruz.

Yazdığım kod gerçek kod değil. Tamamen örnek olması için öylesine yazdım.
CREATE OR REPLACE FUNCTION public.sp_bi_fatura($fatura, $fatura_detay /*merak ettiğim asıl nokta burası. Çok parametre var Burası nasıl organize ediliyor??? Özellikle birden fazla satır için nasıl oluyor*/)
 RETURNS Integer AS
$BODY$
DECLARE
  _id integer;
BEGIN
  begin
    begin transaction;
    _id := add_fatura();
    add_fatura_detay();
    add_hesap_hareketi();
    commit transaction;
    return _id;
  exception
    rollback transaction;
  end
END;

Tabi bu şekilde yazmak çok meşakkatli olacaktır. Delphi tarafında ki kontrol kolaylığı veya esnekliği RDMS tarafında biraz daha zor olacaktır. Eğer doğru/mantıklı ise tüm bu blokları sp içinde kullanırsak bir sürü parametre göndermek gerekecek.

Bu birden fazla kayıt olan(ilk mesajda da örneğini verdiğim gibi) Fatura başlık bilgisi için Insert yapılacak, Fatura Detayları için birden fazla insert işlemi olacak. Bu şekilde olan kayıtlar için sp ler nasıl organize ediliyor. Parametre olarak array mi kullanılıyor?

Daha önce tecrübe etmediğim için öğrenmek adına soruyorum. RDMS sistemlerinin kendilerine has tip veya yöntemleri var mı? Bunu öğrenmek istiyorum.

Sizin ne demek istediğiniz anladım. Lâkin benim yanıtım, "kesinlikle doğru değil". Elbette bu benim görüşüm. Yapılabilir mi, tabii ki. Ama önermem ben. Bir sınıfın içine o sınıfla ilgisi olmayan(mantıken) bir çok metod, property ekleyebilirsiniz, bunda bir sınır ve engel yoktur. Ancak eklemeli misiniz ? Elbetteki hayır.

Ben yine kendi kullanımımdan bir örnek vereyim. Daha önce de bu örneği vermiş olabilirim, tam emin değilim.

ALTER PROCEDURE [dbo].[sp_Fatura_Master_INSERT]
@BelgeKodu VARCHAR(50),
@BelgeTarihi SMALLDATETIME,
@VadeTarihi SMALLDATETIME,
@HareketTuru VARCHAR(2),
@KasaID INT = NULL,
@BankaKrediKartlariHesapID INT = NULL,
@CariID INT = NULL,
@KDVDahilTutar DECIMAL(21, 6),
@KDVTutari DECIMAL(21, 6),
@GenelToplamYaziIle VARCHAR(100) = NULL,
@DovizID INT,
@DovizKuru DECIMAL(21, 6),
@TutarIskontosu DECIMAL(21, 6) = NULL,
@YuzdeIskontosu DECIMAL(4, 2) = NULL,
@DetayIskontoTutari DECIMAL(21, 6) = NULL,
@Aciklama VARCHAR(150) = NULL,
@OzelKodID INT = NULL,
@DepoID INT,
@AdresID INT = NULL,
@DepoID2 INT = NULL,
@RelationID INT = NULL
AS
SET NOCOUNT ON;

BEGIN TRANSACTION;

BEGIN TRY
INSERT INTO dbo.t_Fatura_Master
( 
BelgeKodu ,
BelgeTarihi ,
VadeTarihi ,
HareketTuru ,
KasaID ,
BankaKrediKartlariHesapID ,
CariID ,
KDVDahilTutar ,
KDVTutari ,
GenelToplamYaziIle ,
DovizID ,
DovizKuru ,
TutarIskontosu ,
YuzdeIskontosu ,
DetayIskontoTutari ,
Aciklama ,
OzelKodID ,
DepoID ,
AdresID ,
CreateDateTime,
DepoID2,
RelationID
)
SELECT
@BelgeKodu ,
@BelgeTarihi ,
@VadeTarihi ,
@HareketTuru ,
@KasaID ,
@BankaKrediKartlariHesapID ,
@CariID ,
@KDVDahilTutar ,
@KDVTutari ,
@GenelToplamYaziIle ,
@DovizID ,
@DovizKuru ,
@TutarIskontosu ,
@YuzdeIskontosu ,
@DetayIskontoTutari ,
@Aciklama ,
@OzelKodID ,
@DepoID,
@AdresID,
GETDATE(),
@DepoID2,
@RelationID
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

EXEC sysSaveErrors;

RETURN -1;
END CATCH

IF @@TRANCOUNT > 0 COMMIT TRANSACTION;

RETURN(
SELECT MAX(ID)
FROM dbo.t_Fatura_Master
WHERE
BelgeKodu = @BelgeKodu AND
HareketTuru = @HareketTuru AND
ISNULL(CariID, -1) = ISNULL(@CariID, ISNULL(CariID, -1))
 )


Burada dikkatinizi parametre isimleri ile alan isimleri arasındaki farkın sadece baştaki @ işareti olmasına çekmek isterim. Bu sayede Delphi tarafında;

var
  AField : TField;
  AParam : TParameter;

begin
  for AField in ADataSet.Fields do
  begin
    AParam := ATargetDataSet.FindParam('@' + AField.FieldName);

    if Assigned(AParam) then
      AParam.Value := AField.Value;
  end;
end;

bu şekilde basitçe ve zahmetsizce atama işlemlerimi gerçekleştirebiliyorum.


Ek Dosyalar Resimler
   
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#5
Resim eklemek zorunda kaldım. Maalesef bu SQL Code Formatter çok kötü.
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla
#6
@Tuğrul HELVACI bey çok teşekkürler. İstediğim bilgilere cevap aldım. Diğer aradığım cevap ise burada mevcut.

Bu tabi PostgreSQL RDMS için geçerli bir fonksiyon. Önce istenilen tablo bilgilerinin tutulduğu tip tanımlanıyor. Bu delphi tarafında ki record benzeri bir yapı. Birden fazla bilginin olduğu fatura/irsaliye detayı gibi düşünün. Bunları array tipinde parametre olarak gönderiyor. 

Bundan sonra veri istenilen şekilde kullanılabilir. for in loop ile bütün bilgilere ulaşılabilir. İstenilen hesaplama varsa yapılabilir. Parametre Array tipinde gönderilmiş oldu. 

Örneğe göre daha sonra unnest ile de array satır satır gösterilecek şekilde parçalanıyor.
Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
drop function if exists bar(bigint, variadic foo[]);
drop type if exists foo;

CREATE TYPE foo AS (
   x bigint,
   y int,
   z varchar(64)
);

CREATE OR REPLACE FUNCTION bar(bigint, variadic foo[]) RETURNS TABLE(
 a bigint,
 x bigint,
 y int,
 z varchar(64)) AS
$$
   SELECT $1, x, y, z FROM unnest($2);
$$
LANGUAGE SQL;
SELECT * FROM bar(1, (1,2,'body1'), (2,1,'body2'), (3,4,'taddy bear'));
PostgreSQL - Linux - Delphi, Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
WWW
Cevapla
#7
Konuya katkısı olması açısından SQL Server ve C# üzerinden konuyu anlatan teknik bir video da ben paylaşmak istiyorum;

Video'da bir tablo yapısını bir veri tipi olarak tanımlayıp, bunu Stored Procedure'de parametre olarak nasıl kullanacağınızı ve bunun C# tarafında nasıl kullanıldığını gösteriyor. Benzer teknikleri kullanarak bu yöntemi Delphi'de de kullanmak mümkün.



P.Safa:Yaşlanarak değil, yaşayarak tecrübe kazanılır.Zaman insanları değil,armutları olgunlaştırır
C.Yücel:Toprak gibi olmalısın! Ezildikçe sertleşmelisin!Seni ezenler sana muhtaç kalmalı! Hayatı sende bulmalı
S.Canan:Bildiğini zannettiğin an hiç bir şey öğrenemezsin
Bilgi uçar
WWW
Cevapla
#8
(14-01-2019, Saat: 09:54)3ddark Adlı Kullanıcıdan Alıntı: Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.@Tuğrul HELVACI bey çok teşekkürler. İstediğim bilgilere cevap aldım. Diğer aradığım cevap ise burada mevcut.

Bu tabi PostgreSQL RDMS için geçerli bir fonksiyon. Önce istenilen tablo bilgilerinin tutulduğu tip tanımlanıyor. Bu delphi tarafında ki record benzeri bir yapı. Birden fazla bilginin olduğu fatura/irsaliye detayı gibi düşünün. Bunları array tipinde parametre olarak gönderiyor. 

Bundan sonra veri istenilen şekilde kullanılabilir. for in loop ile bütün bilgilere ulaşılabilir. İstenilen hesaplama varsa yapılabilir. Parametre Array tipinde gönderilmiş oldu. 

Örneğe göre daha sonra unnest ile de array satır satır gösterilecek şekilde parçalanıyor.
Linkleri Görebilmeniz İçin Üye Olmanız Gerekiyor. Üye Olabilmek İçin Lütfen Buraya Tıklayınız.
drop function if exists bar(bigint, variadic foo[]);
drop type if exists foo;

CREATE TYPE foo AS (
   x bigint,
   y int,
   z varchar(64)
);

CREATE OR REPLACE FUNCTION bar(bigint, variadic foo[]) RETURNS TABLE(
 a bigint,
 x bigint,
 y int,
 z varchar(64)) AS
$$
   SELECT $1, x, y, z FROM unnest($2);
$$
LANGUAGE SQL;
SELECT * FROM bar(1, (1,2,'body1'), (2,1,'body2'), (3,4,'taddy bear'));

Rica ederim. Bahsettiğiniz şey Microsoft SQL Server'ın TVP(Table Valued Parameters)'si. Ancak bu alanı kayıt girmek güncellemek vs için kullanmanızı önermem. Bu alan daha ziyade birden fazla değerin hariç tutulmak istendiği durumlarda anlam ifade eder(bence). Örneğin; stok listesi çekeceksiniz ama kullanıcı 1,3,5,7,9,11,13,15,17,107 stoklarını istemiyor.

Yukarıda kısmi örneğini verdiğim kullanım tarzına ilaveten ben aynı zamanda CachedUpdates yapısını da kullanıyorum. Eğer kullandığım component setinin CachedUpdates özelliği var ise onu kullanıyorum yok ise, kendim simüle ediyorum. (İki adet memory dataset. Memory dataset'lerden birisi Browse sp'sinden gelen tüm dataları içeriyor, diğer memory dataset ise silinen kayıtların ID'sini içeriyor).

Bu yapı sayesinde disconnected mode çalışmış oluyorum. Veritabanına yapılan değişiklikleri aksettirmek istediğim zaman ilgili memory dataset'ler içinde dönüp sadece değişikliğe uğrayan kayıtları yolluyorum.
Mal sahibi, mülk sahibi
Hani bunun ilk sahibi ?
Mal da yalan mülk de yalan
Var biraz da sen oyalan...
WWW
Cevapla


Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  Calculated Alanda Ondalık Sayısı Nasıl Belirlenir? Fesih ARSLAN 5 429 11-06-2019, Saat: 17:46
Son Yorum: boreas



Konuyu Okuyanlar: 1 Ziyaretçi