Basit bir banka şeması yazmak: Bakiyelerimi işlem geçmişiyle nasıl senkronize etmeliyim?


57

Şemayı basit bir banka veritabanı için yazıyorum. İşte temel özellikler:

  • Veritabanı, bir kullanıcıya ve para birimine karşı işlemleri kaydeder.
  • Her kullanıcının para birimi başına bir bakiyesi vardır, bu nedenle her bir bakiye, belirli bir kullanıcıya ve para birimine karşı tüm işlemlerin toplamıdır.
  • Bir denge negatif olamaz.

Banka başvurusu, veri tabanı ile yalnızca depolanmış prosedürler aracılığıyla iletişim kuracaktır.

Bu veritabanının günde yüz binlerce yeni işlemi kabul etmesini ve daha yüksek bir büyüklükteki sorgu sorgularını kabul etmesini bekliyorum. Bakiyeleri çok hızlı bir şekilde yerine getirmek için bunları önceden toplamam gerekiyor. Aynı zamanda, bir dengenin işlem geçmişiyle hiçbir zaman çelişmediğini garanti etmem gerekiyor.

Seçeneklerim:

  1. Ayrı bir balancesmasaya sahip olun ve aşağıdakilerden birini yapın:

    1. Hem işlemleri uygulayın transactionsve balancestablolar. TRANSACTIONBakiye ve işlemlerin her zaman senkronize olmasını sağlamak için saklı yordam katmanımda mantığı kullanın . ( Jack tarafından desteklenir .)

    2. İşlemleri transactionstabloya uygulayın ve balancestabloyu benim için işlem tutarıyla güncelleyen bir tetikleyiciye sahip olun .

    3. İşlemleri balancestabloya uygulayın ve transactionsbenim için işlem tutarını içeren tabloya yeni bir giriş ekleyen bir tetikleyiciyi kullanın .

    Saklı yordamların dışında hiçbir değişiklik yapılamayacağından emin olmak için güvenlik tabanlı yaklaşımlara güvenmek zorundayım. Aksi takdirde, örneğin, bazı işlemler doğrudan transactionstabloya bir işlem ekleyebilir ve şema altında 1.3ilgili bakiyenin senkronize olamayacağı belirtilir.

  2. Bir var balancesendeksli görünümü uygun işlemleri toplar. Bakiyeler, depolama motoru tarafından işlemleriyle uyum içinde olmaları için garanti altına alındığından, bunu garanti etmek için güvenlik tabanlı yaklaşımlara güvenmem gerekmiyor. Öte yandan, dengeleri zorlayamıyorum çünkü görüşlerin - endekslenmiş görünümlerin bile - CHECKkısıtlamaları olamaz . ( Denny tarafından desteklenmektedir .)

  3. Sadece bir transactionsmasaya sahip olun, ancak işlem yapıldıktan hemen sonra etkili bakiyeyi saklamak için ek bir sütuna sahip olun. Bu nedenle, bir kullanıcı ve para birimi için en son işlem kaydında mevcut bakiyeleri de bulunur. (Aşağıda Andrew tarafından önerildi; garik tarafından önerilen varyant .)

Bu problemle ilk karşılaştığımda, bu iki tartışmayı okudum ve seçeneğe karar verdim 2. Başvuru için, burada bir çıplak kemik uygulaması görebilirsiniz .

  • Yüksek yük profiline sahip böyle bir veritabanı tasarladınız veya yönetdiniz mi? Bu soruna çözümün neydi?

  • Doğru tasarım seçimini yaptığımı mı düşünüyorsun? Aklımda tutmam gereken bir şey var mı?

    Örneğin, transactionstablodaki şema değişikliklerinin balancesgörünümü yeniden oluşturmamı gerektireceğini biliyorum . Veritabanını küçük tutmak için işlemleri arşivliyor olsam bile (örneğin, başka bir yere taşıyarak ve bunları özet işlemlerle değiştirerek), her şema güncellemesinde on milyonlarca işlemin görüşünü yeniden oluşturmak zorunda kalmak, muhtemelen dağıtım başına önemli ölçüde daha fazla kesinti anlamına gelecektir.

  • Dizin oluşturulmuş görünüm gitme yoluysa, bakiyenin negatif olmadığını nasıl garanti ederim?


Arşivleme işlemleri:

Arşivleme işlemlerini ve yukarıda bahsettiğim "özet işlemleri" konusunu biraz ele alalım. İlk olarak, düzenli arşivleme, bunun gibi yüksek yüklü bir sistemde bir gereklilik olacaktır. Eski işlemlerin başka bir yere taşınmasına izin verirken bakiyelerle işlem geçmişi arasındaki tutarlılığı korumak istiyorum. Bunu yapmak için, her arşivlenmiş işlem grubunu kullanıcı başına ve para birimine göre miktarlarının bir özeti ile değiştireceğim.

Yani, örneğin, bu işlemlerin listesi:

user_id    currency_id      amount    is_summary
------------------------------------------------
      3              1       10.60             0
      3              1      -55.00             0
      3              1      -12.12             0

arşivlendi ve bununla değiştirildi:

user_id    currency_id      amount    is_summary
------------------------------------------------
      3              1      -56.52             1

Bu şekilde, arşivlenmiş işlemlere sahip bir bakiye, eksiksiz ve tutarlı bir işlem geçmişi tutar.


1
Seçenek 2'yi seçerseniz (daha temiz olduğunu düşünüyorum), pgcon.org/2008/schedule/attachments/… 'de "materyalize görünümler" in etkin bir şekilde nasıl uygulanacağına bakın . Seçenek 1 için, Haan'ın ve Koppelaars'ın Veri Tabanı Uzmanları için Uygulamalı Matematiğinin 11. Bölümü (unvandan endişe etmeyin) “geçiş kısıtlamalarını” etkili bir şekilde nasıl uygulayacağına dair bir fikir edinmek için yardımcı olacaktır. İlk bağlantı PostgreSQL ve ikincisi Oracle içindir, ancak teknikler herhangi bir makul veritabanı sistemi için çalışmalıdır.
jp

Teorik olarak # 3 yapmak istersiniz. Bir "çalışan bakiye" yapmanın doğru yolu, her işleme bir bakiye atamaktır. İşlemleri kesin olarak seri kimliği (tercih edilen) veya zaman damgası ile sipariş verdiğinizden emin olun. Çalışan bir dengeyi "hesaplamanız" gerekmez.
pbreitenbach

Yanıtlar:


15

Muhasebeye aşina değilim, ancak envanter tipi ortamlarda da benzer problemleri çözdüm. Çalışan toplamları işlemle aynı satırda saklarım. Kısıtlamalar kullanıyorum, böylece verilerim yüksek eşzamanlılık altında bile asla yanlış olmaz. Aşağıdaki çözümü 2009 yılında yazdım :

Akan toplamları hesaplamak, imleçle veya üçgen birleştirme ile yapsanız bile, oldukça yavaş. Bir sütunda akan toplamları depolamak, özellikle sık seçerseniz, denormalize etmek çok caziptir. Bununla birlikte, normalize ettiğiniz zamanki gibi normalleştirilen verilerinizin bütünlüğünü garanti etmeniz gerekir. Neyse ki, toplam çalışanların kısıtlamalarla bütünlüğünü garanti edebilirsiniz - tüm kısıtlamalarınıza güvenildiği sürece, tüm çalışan toplamlarınız doğrudur. Ayrıca bu yolla, cari bakiyenizin (çalışan toplamların) asla negatif olmadığından kolayca emin olabilirsiniz - başka yöntemlerle zorlamak da çok yavaş olabilir. Aşağıdaki komut dosyası tekniği göstermektedir.

CREATE TABLE Data.Inventory(InventoryID INT NOT NULL IDENTITY,
  ItemID INT NOT NULL,
  ChangeDate DATETIME NOT NULL,
  ChangeQty INT NOT NULL,
  TotalQty INT NOT NULL,
  PreviousChangeDate DATETIME NULL,
  PreviousTotalQty INT NULL,
  CONSTRAINT PK_Inventory PRIMARY KEY(ItemID, ChangeDate),
  CONSTRAINT UNQ_Inventory UNIQUE(ItemID, ChangeDate, TotalQty),
  CONSTRAINT UNQ_Inventory_Previous_Columns 
     UNIQUE(ItemID, PreviousChangeDate, PreviousTotalQty),
  CONSTRAINT FK_Inventory_Self FOREIGN KEY(ItemID, PreviousChangeDate, PreviousTotalQty)
    REFERENCES Data.Inventory(ItemID, ChangeDate, TotalQty),
  CONSTRAINT CHK_Inventory_Valid_TotalQty CHECK(
         TotalQty >= 0 
     AND (TotalQty = COALESCE(PreviousTotalQty, 0) + ChangeQty)
  ),
  CONSTRAINT CHK_Inventory_Valid_Dates_Sequence CHECK(PreviousChangeDate < ChangeDate),
  CONSTRAINT CHK_Inventory_Valid_Previous_Columns CHECK(
        (PreviousChangeDate IS NULL AND PreviousTotalQty IS NULL)
     OR (PreviousChangeDate IS NOT NULL AND PreviousTotalQty IS NOT NULL)
  )
);

-- beginning of inventory for item 1
INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)
VALUES(1, '20090101', 10, 10, NULL, NULL);

-- cannot begin the inventory for the second time for the same item 1
INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)
VALUES(1, '20090102', 10, 10, NULL, NULL);


Msg 2627, Level 14, State 1, Line 10

Violation of UNIQUE KEY constraint 'UNQ_Inventory_Previous_Columns'. 
Cannot insert duplicate key in object 'Data.Inventory'.

The statement has been terminated.


-- add more
DECLARE @ChangeQty INT;
SET @ChangeQty = 5;

INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)

SELECT TOP 1 ItemID, '20090103', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
  FROM Data.Inventory
  WHERE ItemID = 1
  ORDER BY ChangeDate DESC;

SET @ChangeQty = 3;

INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)

SELECT TOP 1 ItemID, '20090104', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
  FROM Data.Inventory
  WHERE ItemID = 1
  ORDER BY ChangeDate DESC;

SET @ChangeQty = -4;

INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)

SELECT TOP 1 ItemID, '20090105', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
  FROM Data.Inventory
  WHERE ItemID = 1
  ORDER BY ChangeDate DESC;

-- try to violate chronological order
SET @ChangeQty = 5;

INSERT INTO Data.Inventory(ItemID,
  ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty)

SELECT TOP 1 ItemID, '20081231', @ChangeQty, TotalQty + @ChangeQty, ChangeDate, TotalQty
  FROM Data.Inventory
  WHERE ItemID = 1
  ORDER BY ChangeDate DESC;

Msg 547, Level 16, State 0, Line 4

The INSERT statement conflicted with the CHECK constraint 
"CHK_Inventory_Valid_Dates_Sequence". 
The conflict occurred in database "Test", table "Data.Inventory".

The statement has been terminated.

SELECT ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty
FROM Data.Inventory ORDER BY ChangeDate;

ChangeDate              ChangeQty   TotalQty    PreviousChangeDate      PreviousTotalQty
----------------------- ----------- ----------- ----------------------- -----
2009-01-01 00:00:00.000 10          10          NULL                    NULL
2009-01-03 00:00:00.000 5           15          2009-01-01 00:00:00.000 10
2009-01-04 00:00:00.000 3           18          2009-01-03 00:00:00.000 15
2009-01-05 00:00:00.000 -4          14          2009-01-04 00:00:00.000 18


-- try to change a single row, all updates must fail
UPDATE Data.Inventory SET ChangeQty = ChangeQty + 2 WHERE InventoryID = 3;
UPDATE Data.Inventory SET TotalQty = TotalQty + 2 WHERE InventoryID = 3;

-- try to delete not the last row, all deletes must fail
DELETE FROM Data.Inventory WHERE InventoryID = 1;
DELETE FROM Data.Inventory WHERE InventoryID = 3;

-- the right way to update
DECLARE @IncreaseQty INT;

SET @IncreaseQty = 2;

UPDATE Data.Inventory 
SET 
     ChangeQty = ChangeQty 
   + CASE 
        WHEN ItemID = 1 AND ChangeDate = '20090103' 
        THEN @IncreaseQty 
        ELSE 0 
     END,
  TotalQty = TotalQty + @IncreaseQty,
  PreviousTotalQty = PreviousTotalQty + 
     CASE 
        WHEN ItemID = 1 AND ChangeDate = '20090103' 
        THEN 0 
        ELSE @IncreaseQty 
     END
WHERE ItemID = 1 AND ChangeDate >= '20090103';

SELECT ChangeDate,
  ChangeQty,
  TotalQty,
  PreviousChangeDate,
  PreviousTotalQty
FROM Data.Inventory ORDER BY ChangeDate;

ChangeDate              ChangeQty   TotalQty    PreviousChangeDate      PreviousTotalQty
----------------------- ----------- ----------- ----------------------- ----------------
2009-01-01 00:00:00.000 10          10          NULL                    NULL
2009-01-03 00:00:00.000 7           17          2009-01-01 00:00:00.000 10
2009-01-04 00:00:00.000 3           20          2009-01-03 00:00:00.000 17
2009-01-05 00:00:00.000 -4          16          2009-01-04 00:00:00.000 20

14

Müşterilerin 0 bakiyeden daha az bakiyesi olmasına izin vermemek bir iş kuralıdır (taslak gibi şeyler için ücretlerin hızlı bir şekilde değişmesi bankaların paralarının çoğunu kazanmasıdır). İşlem geçmişine satırlar eklendiğinde bunu uygulama işleminde ele almak isteyeceksiniz. Özellikle, bazı müşterilerin fazla para çekme korumasına sahip olması ve bazılarının ücret alması ve bazılarının negatif tutarların girmesine izin vermemesi gibi sonuçlanabileceği gibi.

Şimdiye kadar bununla gideceğin yeri seviyorum, ama eğer bu gerçek bir proje (okul değil) için, iş kurallarına çok fazla dikkat edilmesi gerekiyor. Bankacılık sistemi kurduktan sonra ve çalışanlar paralarına erişimi olan insanlar hakkında çok özel yasalar olduğu için yeniden tasarlanacak çok yer yok.


1
Denge kısıtının neden gerçekten bir işletme kuralı olması gerektiğini anlayabiliyorum. Veri tabanı sadece bir işlem hizmeti veriyor ve ne yapacağına karar vermek kullanıcıya kalmış.
Nick Chammas

Jack'in bu iki tabloyu kullanmanın geliştiricilere iş mantığını değiştirme veya uygulama konusunda daha fazla esneklik sağladığına dair yorumları hakkında ne düşünüyorsunuz? Ayrıca, bu endişeleri doğrulayan ya da sorgulayan endekslenmiş görüşleri doğrudan deneyimlediniz mi?
Nick Chammas

1
İki masaya sahip olmanın size esneklik sağladığını söylemem, iş mantığını uygulamak demek. Veri arşivlemede size daha fazla esneklik kazandırır. Ancak, bir banka olarak (en azından ABD’de), ne kadar veri saklamanız gerektiğini söyleyen yasalara sahipsiniz. Performansın üst görünümle nasıl göründüğünü test etmek istersiniz; bunun yanı sıra, dizine alınmış bir görüntünüz varsa, temel tabloların şemasını değiştiremeyeceğinizi de hesaba katarsınız. Düşünecek bir şey daha var.
mrdenny 13:11

Makalede belirtilen tüm maddeler, indekslenmiş bir görünüm kullanılırken geçerli olan kaygılardır.
mrdenny 13:11

1
Netleştirmek için: IMO, işlemsel bir API, iş mantığını uygulayan daha fazla esneklik sağlar (iki tablo içermez). Bu durumda, endekslenmiş görünüm yaklaşımı ile teklif edilen takaslar nedeniyle (en azından şu ana kadar sahip olduğumuz bilgiler verildi) iki tablo lehine de olacağım (örneğin, daha sonra DRI’yı dengelemek için kullanılamaz> 0 iş kural)
Jack Douglas

13

Dikkate almanız gereken biraz farklı bir yaklaşım ( 2. seçeneğinize benzer ), aşağıdaki tanımları içeren yalnızca işlem tablosuna sahip olmaktır:

CREATE TABLE Transaction (
      UserID              INT
    , CurrencyID          INT 
    , TransactionDate     DATETIME  
    , OpeningBalance      MONEY
    , TransactionAmount   MONEY
);

Bir işlem kimliği / Siparişi de isteyebilirsiniz, böylece aynı tarihte iki işlemi gerçekleştirebilir ve alım sorgunuzu iyileştirebilirsiniz.

Mevcut dengeyi elde etmek için tek ihtiyacınız olan son kayıt.

Son kaydı alma yöntemleri :

/* For a single User/Currency */
Select TOP 1 *
FROM dbo.Transaction
WHERE UserID = 3 and CurrencyID = 1
ORDER By TransactionDate desc

/* For multiple records ie: to put into a view (which you might want to index) */
SELECT
    C.*
FROM
    (SELECT 
        *, 
        ROW_NUMBER() OVER (
           PARTITION BY UserID, CurrencyID 
           ORDER BY TransactionDate DESC
        ) AS rnBalance 
    FROM Transaction) C
WHERE
    C.rnBalance = 1
ORDER BY
    C.UserID, C.CurrencyID

Eksileri:

  • Sıra dışı bir işlem eklerken (yani: bir sorunu / yanlış başlangıç ​​bakiyesini düzeltmek için), sonraki tüm işlemler için güncellemeleri basamaklandırmanız gerekebilir.
  • Kullanıcı / Para Birimi işlemlerinin doğru bir dengeyi sağlamak için serileştirilmesi gerekir.

    -- Example of getting the current balance and locking the 
    -- last record for that User/Currency.
    -- This lock will be freed after the Stored Procedure completes.
    SELECT TOP 1 @OldBalance = OpeningBalance + TransactionAmount  
    FROM dbo.Transaction with (rowlock, xlock)   
    WHERE UserID = 3 and CurrencyID = 1  
    ORDER By TransactionDate DESC;

Artıları:

  • Artık iki ayrı masa tutmanıza gerek yok ...
  • Bakiyeyi kolayca doğrulayabilirsiniz ve bakiyenin senkronizasyondan çıkması durumunda, işlem geçmişi kendiliğinden belgelenirken tam olarak ne zaman patladığını tespit edebilirsiniz.

Düzenleme: Mevcut bakiye alımı ile ilgili bazı örnek sorgular ve aleyhte vurgulamak (Thanks @Jack Douglas)


3
SELECT TOP (1) ... ORDER BY TransactionDate DESCSQL Server sürekli işlem tablosunu taraması yapmaz şekilde uygulamak çok zor olacaktır. Alex Kuznetsov , bu cevabı mükemmel bir şekilde tamamlayan benzer bir tasarım problemine çözüm yolladı .
Nick Chammas,

2
+1 Benzer bir yaklaşım kullanıyorum. BTW, eşzamanlı iş yükü altında kodumuzun doğru çalıştığından çok dikkatli olmalıyız.
AK

12

Bu iki tartışmayı okuduktan sonra seçenek 2'ye karar verdim

Bu tartışmaları da okuduktan sonra, DRI çözümüne neden karar verdiğiniz diğer seçeneklerin en mantıklısı olduğuna karar verdiğinizden emin değilim :

İşlemleri hem işlemler hem de bakiyeler tablolarına uygulayın. Bakiyelerin ve işlemlerin her zaman senkronize olmasını sağlamak için saklı yordam katmanımda TRANSACTION mantığını kullanın.

Bu tür bir çözüm, işlemsel API'niz aracılığıyla verilere tüm erişimi kısıtlama lüksüne sahipseniz çok büyük pratik faydalar sağlar . DRI'nın çok önemli faydasını kaybedersiniz, ki bu bütünlük veri tabanı tarafından güvence altına alınmıştır, ancak yeterli karmaşıklıktaki herhangi bir modelde, DRI tarafından uygulanamayacak bazı iş kuralları olacaktır .

İş kurallarınızı mümkün kılmak için modelinizi çok fazla bükmeden DRI'yı kullanmanızı mümkün kılar:

İşlemleri arşivliyor olsam bile (örneğin, başka bir yere taşıyarak ve bunları özet işlemlerle değiştirerek)

Modelinizi bu şekilde kirletmeyi düşünmeye başlar başlamaz, DRI’nın yararının tanıtmakta olduğunuz zorluklardan daha ağır basıldığı alana girdiğinizi düşünüyorum. Örneğin, arşivleme işleminizdeki bir hatanın teoride altın kuralınızın ( her zaman işlemlerin toplamını dengeler ) DRI çözümüyle sessizce kopmasına neden olabileceğini düşünün .

İşte işlem yaklaşımının, gördüğüm gibi avantajlarının bir özeti:

  • Mümkünse, bunu yine de yapmalıyız. Bu özel sorun için seçtiğiniz çözümü ne olursa olsun, size daha fazla tasarım esnekliği ve verileriniz üzerinde kontrol sağlar. Tüm erişim daha sonra, sadece veritabanı mantığı yerine, iş mantığı açısından "işlemsel" hale gelir.
  • Modelinizi temiz tutabilirsiniz
  • İş kurallarının çok daha geniş bir yelpazesini ve karmaşıklığını "uygulayabilirsiniz" ("uygulama" kavramının DRI'dan daha gevşek bir kural olduğuna dikkat edin)
  • Modele daha sağlam bir temel bütünlüğü vermek için pratik olarak her yerde DRI kullanabilirsiniz - ve bu işlem işlem mantığınızı kontrol edebilir
  • Sizi rahatsız eden performans sorunlarının çoğu eriyecek
  • Yeni gereksinimleri tanımak çok daha kolay olabilir - örneğin: tartışmalı işlemler için karmaşık kurallar sizi daha fazla boşa harcanan çaba anlamına gelen saf bir DRI yaklaşımından uzaklaştırabilir.
  • Tarihsel verilerin bölümlenmesi veya arşivlenmesi çok daha az riskli ve acı verici hale gelir

--Düzenle

Karmaşıklık veya risk eklemeden arşivlemeye izin vermek için, özet satırlarını sürekli olarak oluşturulan ayrı bir özet tablosunda tutmayı seçebilirsiniz (@Andrew ve @Garik'ten ödünç alma)

Örneğin, özetler aylıksa:

  • Her seferinde bir işlem (API'niz üzerinden) ilgili bir güncelleme veya özet tablosuna ekleme yapılır.
  • Özet tablosu hiçbir zaman arşivlenmez, ancak arşivleme işlemleri basit bir silme (veya bölüm bırakma?) kadar basit hale gelir.
  • Özet tablosundaki her satır 'açılış bakiyesi' ve 'tutar' içerir
  • Özet tablosuna 'açılış bakiyesi' + 'tutarı'> 0 ve 'açılış bakiyesi'> 0 gibi kısıtlamaları kontrol edin
  • En son özet satırını kilitlemeyi kolaylaştırmak için özet satırları aylık bir toplu işleme eklenebilir (mevcut ay için her zaman bir satır olur)

Düzenlemeniz ile ilgili: Yani, bu özet tabloyu ana balans tablosunun yanına yerleştirmeyi teklif ediyor musunuz? Bakiyeler tablosu daha sonra etkin bir şekilde cari ayın kayıtlarını içeren bir özet tablosu haline gelir mi (her ikisi de aynı türde verileri depolayacağından)? Doğru anladıysam, neden sadece bilanço tablosunu özet tablodaki uygun bölümle değiştirmiyorsunuz?
Nick Chammas

Maalesef net değil - net değil - Ben dengeleri tabloya dağıtmak demek istedim, çünkü her zaman özet tablosunda geçerli dengeyi bulmak için anahtar bir arama olacaktır (Andrews önerisi AFAIK için doğru değil). Bunun avantajı, önceki zamanlarda bakiyeleri hesaplamanın daha kolay hale gelmesi ve yanlış giderse bakiyeler için daha net bir denetim izinin oluşmasıdır.
Jack Douglas

6

Nick.

Ana fikir, bakiye ve işlem kayıtlarını aynı tabloda depolamaktır. Tarihsel olarak oldu sanıyordum. Yani bu durumda sadece son özet kaydı bularak dengeyi alabiliriz.

 id   user_id    currency_id      amount    is_summary (or record_type)
----------------------------------------------------
  1       3              1       10.60             0
  2       3              1       10.60             1    -- summary after transaction 1
  3       3              1      -55.00             0
  4       3              1      -44.40             1    -- summary after transactions 1 and 3
  5       3              1      -12.12             0
  6       3              1      -56.52             1    -- summary after transactions 1, 3 and 5 

Daha iyi bir değişken, özet kayıt sayısını azaltmaktır. Günün sonunda (ve / veya başlangıcı) bir bilanço kaydı yapabiliriz. Bildiğiniz gibi her bankanın operational dayaçılması gerekiyor ve bu güne ilişkin bazı özet işlemler yapmak için kapanıyor. Örneğin, günlük bilanço kayıtlarını kullanarak faizi kolayca hesaplamamızı sağlar , örneğin:

user_id    currency_id      amount    is_summary    oper_date
--------------------------------------------------------------
      3              1       10.60             0    01/01/2011 
      3              1      -55.00             0    01/01/2011
      3              1      -44.40             1    01/01/2011 -- summary at the end of day (01/01/2011)
      3              1      -12.12             0    01/02/2011
      3              1      -56.52             1    01/02/2011 -- summary at the end of day (01/02/2011)

Şans.


4

Gereksinimlerinize göre, seçenek 1 en iyi görünecektir. Her ne kadar benim tasarımımı sadece işlem tablosuna ekleyebileceğim şekilde tasarlayacağım Gerçek zamanlı bilanço tablosunu güncellemek için işlem tablosunda tetikleyiciyi bulundurun. Bu tablolara erişimi kontrol etmek için veritabanı izinlerini kullanabilirsiniz.

Bu yaklaşımda, gerçek zamanlı bakiyenin işlem tablosu ile senkronize edileceği garanti edilmektedir. Ve saklı yordamlar veya psql veya jdbc kullanılıp kullanılmadığı önemli değildir. Gerekirse negatif bakiye kontrolünü alabilirsiniz. Performans bir sorun olmayacak. Gerçek zamanlı bakiyeyi elde etmek için tekil bir sorgudur.

Arşivleme bu yaklaşımı etkilemeyecektir. Raporlar gibi şeyler için gerekirse, haftalık, aylık, yıllık bir özet tabloya sahip olabilirsiniz.


3

Oracle'da bunu, sadece dengeyi oluşturmak için bir araya getiren hızlı yenilenebilir bir Materyal Görünümlü işlemler tablosunu kullanarak yapabilirsiniz. Tetiği Materyalleştirilmiş Görünüm'de tanımlarsınız. Materyalleştirilmiş Görünüm 'ON COMMIT' ile tanımlanmışsa, temel tablolara veri eklenmesini / değiştirilmesini etkili bir şekilde önler. Tetikleyici [in] geçerli verilerini algılar ve işlemi geri aldığında bir istisna oluşturur. Güzel bir örnek burada http://www.sqlsnippets.com/tr/topic-12896.html

Sqlserver bilmiyorum ama belki benzer bir seçeneğe sahip?


2
Oracle'daki Materyalleştirilmiş Görünümler, SQL Server "endekslenmiş görünüm" e benzer, ancak Oracle'ın 'ON COMMIT' davranışı gibi açıkça yönetilen bir yöntem yerine otomatik olarak yenilenirler. Bkz social.msdn.microsoft.com/Forums/fi-FI/transactsql/thread/... ve techembassy.blogspot.com/2007/01/...
GregW
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.