간단한 은행 스키마 작성 : 거래 내역과 균형을 유지하려면 어떻게해야합니까?


57

간단한 은행 데이터베이스에 대한 스키마를 작성 중입니다. 기본 사양은 다음과 같습니다.

  • 데이터베이스는 사용자 및 통화에 대한 트랜잭션을 저장합니다.
  • 모든 사용자는 통화 당 하나의 잔액을 가지므로 각 잔액은 단순히 주어진 사용자 및 통화에 대한 모든 거래의 합계입니다.
  • 잔액은 음수 일 수 없습니다.

은행 애플리케이션은 스토어드 프로 시저를 통해 독점적으로 데이터베이스와 통신합니다.

이 데이터베이스가 하루에 수십만 건의 새로운 트랜잭션을 수용하고 쿼리의 규모가 더 커질 것으로 기대합니다. 잔액을 매우 빨리 처리하려면 미리 집계해야합니다. 동시에, 잔액이 거래 내역과 모순되지 않도록 보장해야합니다.

내 옵션은 다음과 같습니다

  1. 별도의 balances테이블을 가지고 다음 중 하나를 수행하십시오.

    1. transactionsbalances테이블 모두에 트랜잭션을 적용하십시오 . TRANSACTION저의 저장 프로 시저 계층에서 논리를 사용 하여 균형과 트랜잭션이 항상 동기화되도록합니다. ( Jack 지원 )

    2. 트랜잭션을 transactions테이블에 적용 balances하고 트랜잭션 금액으로 테이블을 업데이트하는 트리거가 있습니다 .

    3. 트랜잭션을 balances테이블에 적용 transactions하고 트랜잭션 금액으로 테이블에 새 항목을 추가하는 트리거가 있습니다.

    저장 프로 시저 외부에서 변경할 수 없도록하려면 보안 기반 접근 방식을 사용해야합니다. 그렇지 않으면, 예를 들어, 일부 프로세스는 트랜잭션을 transactions테이블에 직접 삽입 1.3할 수 있으며 계획 하에서 관련 잔액이 동기화되지 않을 수 있습니다.

  2. balances트랜잭션을 적절하게 집계 하는 인덱싱 된 뷰있어야합니다 . 스토리지 엔진은 트랜잭션과 동기화를 유지하기 위해 잔액을 보장하므로 보안을 보장하기 위해 보안 기반 접근 방식을 사용할 필요는 없습니다. 반면, 인덱싱 된 뷰조차도 CHECK제약 조건을 가질 수 없기 때문에 더 이상 잔액을 음이 아닌 값으로 지정할 수 없습니다 . ( Denny 지원 )

  3. transactions거래가 실행 된 직후에 잔액을 저장할 수있는 추가 열이 있는 테이블 만 있습니다. 따라서 사용자 및 통화에 대한 최신 거래 레코드에도 현재 잔액이 포함됩니다. (아래 Andrew가 제안하고 garik에서 제안한 변형 )

내가 처음이 문제를 달려 드는 때, 본인은 두 가지 논의를하고 옵션을 결정했다 2. 참고로 여기서 본 구현을 볼 수 있습니다 .

  • 로드 프로파일이 높은 이와 같은 데이터베이스를 설계 또는 관리 했습니까? 이 문제에 대한 해결책은 무엇입니까?

  • 내가 올바른 디자인을 선택했다고 생각하십니까? 명심해야 할 것이 있습니까?

    예를 들어, transactions테이블의 스키마를 변경 하면 balances뷰를 다시 작성해야 한다는 것을 알고 있습니다. 데이터베이스를 작게 유지하기 위해 트랜잭션을 보관하는 경우에도 (예 : 다른 곳으로 이동하여 요약 트랜잭션으로 교체) 스키마를 업데이트 할 때마다 수천만 건의 트랜잭션에서 뷰를 다시 작성해야 할 경우 배포 당 중단 시간이 크게 늘어날 것입니다.

  • 인덱싱 된 뷰를 사용하는 방법이 없으면 마이너스 잔액이 없는지 어떻게 확인할 수 있습니까?


거래 보관 :

아카이빙 트랜잭션과 위에서 언급 한 "요약 트랜잭션"에 대해 자세히 설명하겠습니다. 첫째, 이와 같은 고부하 시스템에서는 정기적 인 아카이빙이 필요합니다. 기존 거래를 다른 곳으로 옮길 수 있도록 잔액과 거래 내역 간의 일관성을 유지하고 싶습니다. 이를 위해 모든 아카이브 된 트랜잭션 배치를 사용자 및 통화 당 금액 요약으로 대체합니다.

예를 들어이 거래 목록은 다음과 같습니다.

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

아카이브되어 다음으로 대체됩니다.

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

이러한 방식으로 보관 된 트랜잭션과의 균형은 완전하고 일관된 트랜잭션 기록을 유지합니다.


1
더 깨끗하다고 ​​생각되는 옵션 2를 선택한다면 pgcon.org/2008/schedule/attachments/… "구체화 된 뷰"를 효율적으로 구현하는 방법을 살펴보십시오 . 옵션 1의 경우 데이터베이스 전문가를위한 Haan과 Koppelaars의 응용 수학 11 장 (제목에 대해 걱정하지 마십시오)은 "전환 제약 조건"을 효율적으로 구현하는 방법을 이해하는 데 도움이됩니다. 첫 번째 링크는 PostgreSQL 용이고 두 번째 링크는 Oracle 용이지만이 기술은 모든 합리적인 데이터베이스 시스템에서 작동합니다.
jp

이론적으로 당신은 # 3을하고 싶다. "실행 잔액"을 수행하는 올바른 방법은 각 거래에 잔액을 할당하는 것입니다. 일련 번호 (선호) 또는 타임 스탬프를 사용하여 트랜잭션을 확실하게 주문할 수 있는지 확인하십시오. 당신은 실제로 실행 잔액을 "계산"해서는 안됩니다.
pbreitenbach 5

답변:


15

회계에 익숙하지 않지만 재고 유형 환경에서 비슷한 문제를 해결했습니다. 거래와 동일한 행에 누적 합계를 저장합니다. 동시성이 높더라도 데이터가 잘못되지 않도록 제약 조건을 사용하고 있습니다. 2009 년 당시에 다음과 같은 솔루션을 작성했습니다. :

커서를 사용하거나 삼각 조인을 사용하여 누적 합계를 계산하면 속도가 느려집니다. 특히 자주 선택하는 경우 누적 합계를 열에 저장하는 것은 비정규 화하는 것이 매우 유혹적입니다. 그러나 비정규화할 때 평소와 같이 비정규 화 된 데이터의 무결성을 보장해야합니다. 다행스럽게도 모든 제약 조건을 신뢰할 수있는 한 모든 누적 합계가 올바른 경우 제약 조건을 사용하여 누적 합계의 무결성을 보장 할 수 있습니다. 또한이 방법을 사용하면 현재 잔액 (총계)이 절대로 음수가되지 않도록 쉽게 확인할 수 있습니다. 다른 방법으로 시행하는 것도 매우 느릴 수 있습니다. 다음 스크립트는이 기술을 보여줍니다.

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

고객이 잔액이 0 미만인 것을 허용하지 않는 것은 비즈니스 규칙입니다 (초안 작성과 같은 수수료가 은행이 돈을 최대한 활용하는 방식이기 때문에 빠르게 변경됨). 행이 트랜잭션 히스토리에 삽입 될 때 애플리케이션 처리에서이를 처리하려고합니다. 특히 초과 인출 보호 기능이있는 고객과 일부 비용이 청구되고 일부 고객은 마이너스 금액을 입력 할 수 없습니다.

지금까지 나는 당신이 어디로 가고 있는지 좋아하지만, 이것이 실제 프로젝트 (학교가 아닌)를위한 것이라면 비즈니스 규칙 등에 관한 많은 생각이 필요합니다. 돈을 벌 수있는 사람들에 대한 매우 구체적인 법이 있기 때문에 재 설계의 여지가 많지 않습니다.


1
잔액 제한이 실제로 비즈니스 규칙이어야하는 이유를 알 수 있습니다. 데이터베이스는 단지 트랜잭션 서비스를 제공하고 있으며, 데이터베이스를 어떻게 처리할지 결정하는 것은 사용자의 몫입니다.
Nick Chammas

두 테이블을 사용하면 개발자가 비즈니스 로직을 변경하거나 구현할 때 더 많은 유연성을 얻을 수 있다고 Jack의 의견에 대해 어떻게 생각하십니까? 또한 이러한 우려 를 검증하거나 이의를 제기하는 인덱싱 된 뷰에 대한 직접적인 경험 있습니까?
Nick Chammas

1
두 개의 테이블을 가지고 있으면 유연성이 향상되어 비즈니스 로직이 구현된다고 말할 수 없습니다. 데이터 아카이빙을보다 유연하게 수행 할 수 있습니다. 그러나 은행 으로서는 (적어도 미국에서는) 얼마나 많은 데이터를 보관해야하는지에 관한 법률이 있습니다. 인덱스가있는 뷰가있는 경우 기본 테이블의 스키마를 변경할 수 없다는 점을 고려할뿐만 아니라 뷰가 맨 위에 표시되는 방식을 테스트하고 싶을 것입니다. 생각해 볼 또 다른 것.
mrdenny

기사에서 언급 한 모든 항목은 인덱스 된 뷰를 사용할 때 고려해야 할 사항입니다.
mrdenny

1
명확히하기 위해 : IMO 트랜잭션 API는 비즈니스 로직을 구현하는 데 더 많은 유연성을 제공합니다 (두 테이블이 없음). 이 경우 인덱스 된 뷰 접근 방식 (예 : DRI를 사용하여 잔액> 0 비즈니스를 시행 할 수 없음)으로 인해 제안 된 절충 때문에 두 개 이상의 테이블 (적어도 지금까지는 정보가 제공됨)을 선호합니다. 규칙)
잭 더글러스

13

고려해야 할 약간 다른 접근법 ( 두 번째 옵션 과 유사 )은 다음과 같이 정의 된 트랜잭션 테이블 만 갖는 것입니다.

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

트랜잭션 ID / 주문을 원할 수도 있으므로 동일한 날짜의 두 트랜잭션을 처리하고 검색 쿼리를 향상시킬 수 있습니다.

현재 잔액을 얻으려면 마지막 기록 만 있으면됩니다.

마지막 레코드를 얻는 방법 :

/* 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

단점 :

  • 트랜잭션을 순서에 맞지 않게 삽입 할 때 (예 : 문제 / 잘못된 시작 잔액을 수정하기 위해) 모든 후속 트랜잭션에 대한 업데이트를 계단식으로 만들어야합니다.
  • 정확한 균형을 유지하려면 사용자 / 통화 거래를 직렬화해야합니다.

    -- 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;

장점 :

  • 더 이상 두 개의 별도 테이블을 유지할 필요가 없습니다 ...
  • 잔액을 쉽게 확인할 수 있으며, 잔액이 동기화되지 않은 경우 거래 내역이 자체 문서화됨에 따라 잔액이 나올 때 정확히 식별 할 수 있습니다.

편집 : 현재 잔액을 검색하고 단점을 강조하기위한 일부 샘플 쿼리 (감사합니다. @Jack Douglas)


3
SELECT TOP (1) ... ORDER BY TransactionDate DESCSQL 서버가 지속적으로 트랜잭션 테이블을 스캔하지 않는 방식으로 구현하기가 매우 까다로운 일이 될 것입니다. Alex Kuznetsov는 이 답변을 완벽하게 보완하는 유사한 설계 문제 에 대한 솔루션을 여기 에 게시 했습니다 .
Nick Chammas 2016 년

2
+1 비슷한 접근법을 사용하고 있습니다. BTW, 우리는 매우 신중해야하며 동시 작업 부하에서 코드가 올바르게 작동하는지 확인해야합니다.
AK

12

이 두 토론을 읽은 후 옵션 2를 결정했습니다

이러한 토론을 읽은 후, 당신이 설명하는 다른 옵션 중 가장 합리적인 방법으로 DRI 솔루션 을 결정한 이유는 확실하지 않습니다 .

트랜잭션과 잔액 테이블 모두에 트랜잭션을 적용하십시오. 저의 저장 프로 시저 계층에서 TRANSACTION 논리를 사용하여 균형과 트랜잭션이 항상 동기화되도록하십시오.

이러한 종류의 솔루션은 트랜잭션 API를 통해 데이터 에 대한 모든 액세스를 제한하는 고급 기능이있는 경우 실질적인 이점을 제공합니다 . 데이터베이스가 무결성을 보장한다는 점에서 DRI의 중요한 이점을 잃어 버릴 수 있지만, 복잡성 충분한 모델에는 DRI로 시행 할 수없는 몇 가지 비즈니스 규칙이 있습니다 .

가능한 한 모델을 너무 많이 굽히지 않고 비즈니스 규칙을 시행하기 위해 가능한 경우 DRI를 사용하는 것이 좋습니다.

거래를 보관하는 경우에도 (예 : 다른 곳으로 이동하여 요약 거래로 교체)

이와 같이 모델의 오염을 고려하기 시작하자마자, 당신이 도입하는 어려움으로 인해 DRI의 이점보다 중요한 영역으로 이사하고 있다고 생각합니다. 예를 들어, 아카이빙 프로세스의 버그로 인해 이론상 골든 규칙 ( 항상 트랜잭션 합계와 같은 균형 ) 이 DRI 솔루션과 함께 자동 으로 중단 될 수 있습니다 .

다음은 트랜잭션 접근 방식의 장점을 요약 한 것입니다.

  • 우리는 가능하다면 어쨌든 이것을해야합니다. 이 특정 문제에 대해 어떤 솔루션을 선택하든 설계 유연성을 높이고 데이터를 제어 할 수 있습니다. 그러면 모든 액세스는 단순한 데이터베이스 로직이 아닌 비즈니스 로직 측면에서 "트랜잭션"이됩니다.
  • 모델을 깔끔하게 유지할 수 있습니다
  • 훨씬 광범위하고 복잡한 비즈니스 규칙을 "강제화"할 수 있습니다 ( "강제"라는 개념이 DRI보다 느슨하다는 점에 유의하십시오)
  • 실용적 어디에서나 DRI를 사용하여 모델에보다 강력한 기본 무결성을 제공 할 수 있으며, 이는 트랜잭션 논리를 확인하는 역할을 할 수 있습니다
  • 문제가되는 대부분의 성능 문제는 해결 될 것입니다
  • 새로운 요구 사항을 도입하는 것이 훨씬 쉬워 질 수 있습니다. 예를 들어, 분쟁이 발생한 거래에 대한 복잡한 규칙으로 인해 순수한 DRI 접근 방식에서 멀어 질 수 있습니다.
  • 히스토리 데이터의 파티션 또는 아카이브는 훨씬 덜 위험하고 고통 스럽습니다.

--편집하다

복잡성이나 위험을 추가하지 않고 아카이빙을 허용하기 위해 연속적으로 생성 된 @Andrew 및 @Garik에서 요약 행을 별도의 요약 테이블에 유지하도록 선택할 수 있습니다.

예를 들어, 요약이 월별 인 경우 :

  • 트랜잭션이있을 때마다 (API를 통해) 요약 테이블에 해당 업데이트 또는 삽입이 있습니다.
  • 요약 테이블은 아카이브 되지 않지만 아카이브 트랜잭션은 삭제 (또는 파티션 삭제)처럼 간단 해집니다.
  • 요약 테이블의 각 행에는 '개시 잔액'및 '금액'이 포함됩니다.
  • '개시 잔액'+ '금액'> 0 및 '개방 잔액'> 0과 같은 점검 제한 조건을 요약 테이블에 적용 할 수 있습니다.
  • 최신 요약 행을보다 쉽게 ​​잠그기 위해 요약 행을 월 단위 배치에 삽입 할 수 있습니다 (현재 월의 행이 항상 있음)

편집과 관련하여 :이 요약표를 기본 잔액 표와 함께 제안 하시겠습니까? 그러면 잔액 테이블이 현재 월의 레코드 만있는 요약 테이블이됩니까 (둘 다 동일한 종류의 데이터를 저장하므로)? 올바르게 이해했다면 왜 잔액 테이블을 요약 테이블의 적절한 파티션으로 바꾸지 않겠습니까?
Nick Chammas

불명확 한 점에 대해 죄송합니다. 잔액 테이블을 사용하여 현재 잔액을 얻기 위해 항상 요약 테이블의 주요 조회가 될 것입니다 (Andrews 제안 AFAIK에서는 해당되지 않음). 이점은 이전에 잔액을 계산하는 것이 쉬워지고 잔액이 잘못되면 잔고에 대한 명확한 감사 내역이 있다는 것입니다.
Jack Douglas

6

새긴 ​​금.

주요 아이디어는 잔액 및 거래 레코드를 동일한 테이블에 저장하는 것입니다. 역사적으로 나는 생각했다. 따라서이 경우 마지막 요약 레코드를 찾아서 균형을 잡을 수 있습니다.

 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 

더 좋은 변형은 요약 레코드 수를 줄이는 것입니다. 우리는 하루의 끝 (또는 시작)에 하나의 잔고 기록을 가질 수 있습니다. 아시다시피 모든 은행은 operational day오늘 하루 동안 요약 작업을 수행하기 위해 은행 을 개설해야합니다. 예를 들어 다음과 같이 매일 균형 기록을 사용하여 관심 을 쉽게 계산할 수 있습니다 .

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)

운.


4

요구 사항에 따라 옵션 1이 가장 좋습니다. 트랜잭션 테이블에 삽입 만 허용하도록 설계했습니다. 실시간 잔액 테이블을 업데이트하려면 트랜잭션 테이블에 트리거를 설정하십시오. 데이터베이스 권한을 사용하여 이러한 테이블에 대한 액세스를 제어 할 수 있습니다.

이 방법에서는 실시간 균형이 트랜잭션 테이블과 동기화되도록 보장합니다. 저장 프로시 저나 psql 또는 jdbc가 사용되는지는 중요하지 않습니다. 필요한 경우 마이너스 잔액 확인을 할 수 있습니다. 성능은 문제가되지 않습니다. 실시간 균형을 얻으려면 단일 쿼리입니다.

보관은이 방법에 영향을 미치지 않습니다. 보고서와 같은 것들에 필요한 경우 매주, 매월, 매년 요약표를 가질 수도 있습니다.


3

Oracle에서는 집계를 수행하여 균형을 이루는 빠른 새로 고침 가능한 Materialized View가있는 거래 테이블 만 사용하여이 작업을 수행 할 수 있습니다. 구체화 된보기에서 트리거를 정의합니다. 구체화 된보기가 'ON COMMIT'로 정의되면 기본 테이블에 데이터를 추가 / 수정하지 못하게 효과적으로 방지합니다. 트리거는 [in] 유효한 데이터를 감지하고 트랜잭션을 롤백하는 예외를 발생시킵니다. 좋은 예는 다음과 같습니다. http://www.sqlsnippets.com/en/topic-12896.html

sqlserver를 모르지만 비슷한 옵션이 있습니까?


2
Oracle의 구체화 된 뷰는 SQL Server "인덱싱 된 뷰"와 유사하지만 Oracle의 'ON COMMIT'동작과 같이 명시 적으로 관리되는 방식이 아니라 자동으로 새로 고쳐집니다. 참조 social.msdn.microsoft.com/Forums/fi-FI/transactsql/thread/...techembassy.blogspot.com/2007/01/...
GregW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.