Çok Yakında Yeni Bir Arayüzle karşınızdayız! http://yeni.delphican.com/

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
#9
Tuğrul bey merhabalar,

"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."

 Burayı biraz açabilir misiniz. Böyle bir ar-ge yapmaya çalışıyorum. Eğer basit bir örnek gönderebilirseniz çok yardımcı olacaktır.
Cevapla
#10
eğer veritabanınızda data miktarı fazla ve veritabanının çalıştığı sunucu işlem hızı olarak yetersizse(bu kısmı tespit etmek zamanda zamana, artan data miktari ile size veya veritabanı yöneticisine düşer, yerinizde olsam query execution time ları loglatır rapor arabirimi hazırlardım, oturum bazlı)

ve üstelik buna birde eş zamanlı dağıtık kullanım dahil ise, transaction başlattığınız an tablonun kitleneceğini,
o esnada gelen diğer insert updata vs gerçekleştirilmeyeceğini,

sizin transactionunuz bitene kadar diğer işlemlerin queue ye alınacağını ve bu queuenin bir timeout zamanının olduğunu
(bu queue çok şişerse, bunun anlamı bekleyen açık sokettir aynı zamanda, veritabanlarında connection/idle/execution limitlerininde olduğunu)

sonrasında bekleyenler querylerin drop lanacağını,

üstelik sürekli yaptığınız rollbackler yüzünden
(her bir transaction esasında işlenecek olan verinin kopyasını bellekde başka bir alanda yaratılması, query çalıştırıldıkdan sonra etkilenen row ların güncel verilere yansıtılması gerektiği olduğuna göre buda data miktarına göre ve veritabanı optimizasyonuna göre ciddi bellek tüketeceğini ve siz bunu her clientin oturumunda yapıyorsanız),

işlenmesi gereken verilerin tabloya yansıtılmayacağını, yansıtılmayan bu verilerinde diğer queryler işletildiğinde güncel olmayan bilgiler üzerinden iş yapacağını,
ve bu exceptiona uğramış rollback yaptırdığınız transactionlar için exception handler mekanızmasına bir loglama eklemediğinizde bunun farkına varamayacağınızı,

bu loglamaya çalışan query ve alınan hatayıda ekletmezseniz haliyle neler olup bittiğini göremeyeceğinizide(misal bu exception oluşan noktalar insert noktalar ise birde acil durum bayrağıda eklemek yararlı olur),

stored procedure ler her zaman performanslı çalışmaz, hatta innerli joinerli yapılarınız varsa ve data büyükse, indexleriniz dahi olsa bazen db yi bu işlemler için azat etmelisiniz,
tüm veriyi çekip bellekde bir array map yaratarak, üzerinde çalıştığınız pc nin kaynaklarını anlık kullanarak kendiniz bir for döngüsü ile array map alanı üzerinde bu kontrolleri yaptırmanız, veritabanına nefes aldırır size zaman kazandırır vede veritabanından çok çok daha hızlı etkilenen recordları tespit edip, db ye bu etkilenen record lar için indexler üzerinden direktif vermeniz çok daha etkilidir,

şimdilik aklıma gelenler bunlar, daha çok konu var esasında.
Cevapla


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



Konuyu Okuyanlar: 1 Ziyaretçi