I ran into an issue recently with a computed column, which I’ve rarely used, so I investigated them and wrote this short piece in computed columns, mostly as a reminder for me.
What’s a computed column?
You can read the BOL definition, but basically it is a column whose value is based on the values of other columns in a table. By having SQL Server perform the calculation, you prevent errors in the logic, or more likely, get around the chance that some application might forget to perform an update.
Let’s say I have this simple orderdetail table.
CREATE TABLE OrderDetail ( OrderID INT , ProductID INT , Qty INT , Price NUMERIC(10, 2) , LineTotal NUMERIC(10, 2) )
If I want to add a simple order to this table, I can do this:
INSERT dbo.OrderDetail ( OrderID , ProductID , Qty , Price , LineTotal ) VALUES ( 1 , -- OrderID - int 23 , -- ProductID - int 10 , -- Qty - int 8.50 , -- Price - numeric 85.00 -- LineTotal - numeric )
Note that in this case I have to perform the arithmetic of having the line total equal to the price multiplied by the quantity. The math is:
Linetotal = Qty * Price
85.00 = 10 * 8.50
However if I were to make a mistake in the arithmetic and insert $84 instead, or even $8.50, the SQL Server would not catch this.
Instead, I could use a trigger to calculate this value, but triggers seem to have other overhead and force me to maintain them. Instead I could use a computed column, I can save space by not persisting these columns, or I can persist them and index them if needed.
In my simple example, I could change my table to be this:
DROP TABLE dbo.OrderDetail go CREATE TABLE OrderDetail ( OrderID INT , ProductID INT , Qty INT , Price NUMERIC(10, 2) , LineTotal AS (Qty * Price) ) GO INSERT dbo.OrderDetail ( OrderID , ProductID , Qty , Price ) VALUES ( 1 , -- OrderID - int 23 , -- ProductID - int 10 , -- Qty - int 8.50 -- Price - numeric ) go
The LineTotal value is not stored in the table, which isn’t a big issue in this case. In fact, it means that I am saving a few bytes per row, which could translate into quite a few rows for this table (meaning less pages, less I/O, more chance of this remaining in the buffer pool, etc). The tradeoff is the computation is performed for this result set for every query. Which is better? It depends
Notice that I don’t have a value for my computed column in the insert statement, but the results from this version are the same as the previous one:
In fact, if I try the first insert above, which has the LineTotal in it, I get this error:
Msg 271, Level 16, State 1, Line 1
The column "LineTotal" cannot be modified because it is either a computed column or is the result of a UNION operator.
That’s a simple look at computed columns. Are they useful? I’ve never really worried about them, allowing either a stored procedure or the application to handle any logic like this. In general I dislike storing computations, even defined ones like this, but that’s me.
Use them if they fit your environment.