SQL Server 2012でテスト済み。(銘柄記号は省略されています。)
create table stock_transactions (
trans_id integer primary key,
trans_ts datetime not null default current_timestamp,
account_id integer not null, -- references accounts, not shown
-- char(1) keeps the table narrow, while avoiding a needless
-- join on an integer.
-- (b)uy, (s)ell, (d)eposit
action char(1) not null check (action in ('b', 's', 'd')),
qty integer not null check (qty > 0),
-- If your platform offers a special data type for money, you
-- should probably use it.
price money not null check (price > cast(0.00 as money)),
-- Assumes it's not practical to calculate amounts on the fly
-- for many millions of rows. If you store it, use a constraint
-- to make sure it's right. But you're better off starting
-- with a view that does the calculation. If that doesn't perform
-- well, try an indexed view, or (as I did below) add the
-- "trans_amount" column and check constraint, and fix up
-- the view. (Which might mean just including the new "trans_amount"
-- column, or might mean dropping the view altogether.)
trans_amount money not null,
-- Only (b)uys always result in a negative amount.
check (
trans_amount = (case when action = 'b' then qty * price * (-1)
else qty * price
end )
),
-- (d)eposits always have a quantity of 1. Simple, makes logical
-- sense, avoids NULL and avoids additional tables.
check (
qty = (case when action = 'd' then 1 end)
)
);
insert into stock_transactions values
(1, current_timestamp, 1, 's', 30, 1.00, 30.00),
(2, current_timestamp, 2, 'b', 30, 1.00, -30.00),
(3, current_timestamp, 1, 's', 20, 2.00, 40.00),
(4, current_timestamp, 3, 'b', 20, 2.00, -40.00),
(5, current_timestamp, 3, 'd', 1, 100.00, 100.00);
しかし、何が起こったのか見てください。トランザクションのタイプとして預金を追加したので、これは株式トランザクションのテーブルではなくなりました。今では、当座預金のテーブルのようなものになっています。
アカウント所有者が購入したいものを購入するのに十分なアカウントがアカウントにあることを確認するには、CHECK制約以上のものが必要になります。
SQL Serverでは、クラスター化インデックスに関する決定が重要です。少し考えてテストしてください。アカウントID番号について頻繁に問い合わせることを期待しています。