IT tutorials
 
Database
 

SQL Server : Constraints and Rock Solid Inventory Systems (part 1)

1/19/2013 5:04:42 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

The example is completely new and involves an inventory system that is used to store the history of changes to an inventory of items. Listing 1 creates the InventoryLog table, for this purpose.

Listing 1. The InventoryLog table, which stores the history of changes to our inventory.

To record any change in the inventory, we insert rows into the Inventory table. At the very beginning, when we do not have any rows in the InventoryLog table, we assume that we do not have any items in our inventory at all. An increase or decrease in inventory for a given item is reflected by adding new rows. For example, if we need to record the fact that we added five items of a given type to our inventory, we add a row with ChangeQuantity=5. If we withdraw three items, we add another row with ChangeQuantity=-3.

Our business rule states that, for a given item, a change in the inventory cannot be allowed if it will result in a net inventory that is less than zero. Of course, we cannot take out more of an item than we currently have in stock. In order to determine the current inventory amount for a given item, we must query the history of all inventory changes for the item, i.e. calculate the net total in the ChangeQuantity column, resulting from all previous transactions, and make sure that the proposed ChangeQuantity would not result in negative inventory. This is the sort of calculation that would traditionally be performed in a trigger but, in order to make the trigger watertight, the code would be quite complex. Instead, we'll perform the calculation as part of our INSERT statement, using a HAVING clause to prevent illegal withdrawals.

Listing 2 provides several examples of valid and invalid changes to the inventory. For simplicity, we disallow retrospective logging. For example, if we have recorded for Item1 an inventory change for 20100103 then we cannot subsequently log an inventory change for the same item with a date earlier than 20100103. In a production system, we would encapsulate our modification code in a stored procedure, which can be called for all changes. Here, however, we shall just use the simplest possible code that demonstrates the technique. Note that in several places we use the HAVING clause without GROUP BY. This is a perfectly correct usage; we don't really need a GROUP BY clause to select aggregates such as SUM, MIN, and AVG, as long as we only select aggregates and not column values. Likewise, we don't need a GROUP BY clause if we want to use such aggregates in the HAVING clause, as long as column values are not used in that clause.

Listing 2. Examples of possible and impossible changes to the inventory.

For the tiny number of rows in Listing 2, it is not a problem to perform a SUM on the ChangeQuantity column every time we change the inventory. However, for a real inventory system, with hundreds of thousands of transactions per item, this becomes a huge performance drain.

To eliminate this expensive querying of historical inventory changes, it is very tempting to store the new stock quantity of a given item that results from an inventory change, along with the change amount. Let's add one more column to InventoryLog table to store the current amount, as shown in Listing 3. At the same time, we add a CHECK constraint to make sure that CurrentQuantity is never negative.

Listing 3. Adding the CurrentQuantity column to the InventoryLog table.

Now, instead of querying the history of changes, we need only look up the CurrentQuantity value for the most recent row, and add it to the proposed ChangeQuantity, as shown in Listing 4.

Listing 4. An example of possible changes to the inventory.

This appears to suit our requirements, but unfortunately we have nothing that guarantees that the value of CurrentQuantity in the latest row is, indeed, the correct current quantity in stock.

To be more specific, there are currently many ways in which we can violate our business rules. To name just a few:

  • we can retrospectively delete or update a row from the log, and end up invalidating the whole log trail – for example, if we retrospectively deleted the log entry for Jan 1st in Listing 4, it immediately invalidates the withdrawal on Jan 5th

  • we can retrospectively update ChangeQuantity and fail to modify CurrentQuantity accordingly

  • we can manually update CurrentQuantity, or set it to the wrong value when adding a new row.

In order to make our inventory system robust, we require a reasonably complex "network" of interacting constraints. To fully understand how it all fits together will probably require some careful thought and experimentation. With that forewarning, let's take a look at the solution.

We need to find a way to ensure that the value stored in the CurrentQuantity column is always correct, which is a bigger challenge than it may sound. In order to guarantee this, we'll need to create several more constraints, and add some additional columns to our table.

First, we need to add two new columns, PreviousQuantity and PreviousChangeDate, as shown in Listing 5, in order to accurately navigate the chain of rows that modify the same item.

Listing 5. Add the PreviousQuantity and PreviousChangeDate columns to the InventoryLog table.

In our first solution, the user simply had to enter a change quantity and a date (alongside the ID of the item). In our new system, they are required to enter two date values (the dates for the current and for the previous entries) as well as three inventory values:

  • PreviousQuantity – the quantity in stock before the current change is made

  • ChangeQuantity – the quantity to be added or removed

  • CurrentQuantity – the quantity that will exist after the change is made.

Our system must make sure that all values entered are mutually consistent and abide by our business rules.

First, the CHK_InventoryLog_ValidChange constraint will enforce the obvious relation between previous quantity, current quantity, and the change being made, as shown in Listing 6.

Listing 6. The CHK_InventoryLog_ValidChange constraint – the value entered for CurrentQuantity must be equal to the PreviousQuantity plus the ChangeQuantity.

Note that, instead of having CHK_InventoryLog_ValidChange enforce the validity of CurrentQuantity, we could implement CurrentQuantity as a persisted computed column. This is left as an advanced exercise.

Next, the CHK_InventoryLog_ValidPreviousChangeDate constraint ensures that changes occur in chronological order.

Listing 7. CHK_InventoryLog_ValidPreviousChangeDate – PreviousChangeDate must occur before ChangeDate.

Clearly, for a given item, the current value for PreviousQuantity must match the previous value for CurrentQuantity. We'll use a FOREIGN KEY constraint, plus the required UNIQUE constraint or index, to enforce this rule. At the same time, this will also ensure that the PreviousChangeDate is a date that actually has an inventory change for the same item.

Listing 8. The FK_InventoryLog_Self FK constraint.

With these four constraints in place, in addition to our PRIMARY KEY constraint and original CHECK constraint (CHK_InventoryLog_NonnegativeCurrentQuantity), it's about time to run some tests.

 
Others
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us