Enforce rules in a stock transaction table

I'm creating a fake stock market type database and the Transaction table needs some sort of logic to enforce certain rules. Here's the Transaction table I'm using:

Id  AccountId   Action  Qty Price   Amount
1   1           SELL    30  $1.00   $30.00
2   2           BUY     30  $1.00   -$30.00
3   1           SELL    20  $2.00   $40.00
4   3           BUY     20  $2.00   -$40.00
5   3           DEPOSIT            $100.00

As you can see here BUY/SELL Actions must have a Qty and Price and an Amount that should be calculated. The DEPOSIT action doesn't require Qty or Price because thats just a user putting money into an Account table

I was thinking of using some sort of trigger to do this. Is there a better practice?

Answers


Tested in SQL Server 2012. (Stock symbols are omitted.)

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

But look at what happened. Now that we've added deposits as a type of transaction, this is no longer a table of stock transactions. Now it's more like a table of account transactions.

You'll need more than a CHECK constraint to make sure that an account has enough in it to buy whatever the account holder wants to buy.

In SQL Server, decisions about clustered indexes are important. Give that some thought and testing. I'd expect you to query often on account id numbers.


Need Your Help

JPA get id of entity object

java hibernate jpa

Does anyone know how I can do the equivalent of this in hibernate:

Facebook Emotions

html css wpf facebook userscripts

This code is from Facebook Chat Emoticons Bar Grease Monkey UserScript

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.