Reputation: 4538
I have two tables...
CREATE TABLE [dbo].[Invoice](
[InvoiceID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[TotalSumBeforeTax] [decimal](12, 2) NOT NULL,
[TotalSumAfterTax] [decimal](12, 2) NOT NULL
)
CREATE TABLE [dbo].[InvoiceItem](
[InvoiceItemID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[InvoiceID] [uniqueidentifier] NOT NULL,
[AmountBeforeTax] [decimal](12, 2) NOT NULL,
[AmountAfterTax] [decimal](12, 2) NOT NULL
)
The Invoice table holds a calculated sum of all the InvoiceItems
that refer to that Invoice.
I need help figuring out the best practice to keep the Invoice sum correct without losing performance.
Calculated column?
If I use a calculated column for TotalSumBeforeTax
and TotalSumAfterTax
in the Invoice table, that fetches values via a function from the InvoiceItem
table, it's not persisted and will be very slow, since I have hundreds of thousands of rows in the InvoiceItem
table.
Trigger?
I also thought about using a trigger on the InvoiceItem
table and then updating the Invioce TotalSumBeforeTax
TotalAfterBeforeTax
columns.
What to do?
Is there a way to always keeping the TotalSumBeforeTax
and TotalSumAfterTax
columns up-to-date with minimal impact on performance when listing many rows of the Invoice table?
Upvotes: 0
Views: 80
Reputation: 27451
tl;dr; Start with a view then if/when necessary modify to an indexed view.
I would initially create a view on your Invoice table e.g.
create view dbo.InvoiceView
as
SELECT I.* -- Don't actually use *, spell out all your columns
, (select SUM(AmountBeforeTax) from dbo.InvoiceItem II where II.InvoiceId = I.InvoiceId) AS TotalSumBeforeTax
, (select SUM(AmountAfterTax) from dbo.InvoiceItem II where II.InvoiceId = I.InvoiceId) AS TotalSumAfterTax
FROM dbo.Invoice I;
with (at least) an index on dbo.InvoiceItem.InvoiceId
.
I imagine this will perform pretty well for quite some time. Then when the performance drops off, turn it into an indexed view by creating a clustered index on it e.g.
CREATE UNIQUE CLUSTERED INDEX CX_InvoiceId
ON dbo.InvoiceView (InvoiceID); -- And any other relevant columns
GO
Which has a similar effect under the hood to creating a trigger but you don't have to manage it.
Upvotes: 1
Reputation: 82524
I would argue that you shouldn't keep these columns at all. Just create an index on the InvoiceItem
table that will have the InvoiceID
and include the AmountBeforeTax
and AmountAfterTax
columns, and calculate when needed using a simple sum
and group by
query:
SELECT InvoiceID
, SUM(AmountBeforeTax) AS TotalSumBeforeTax
, SUM(AmountAfterTax) AS TotalSumAfterTax
FROM dbo.InvoiceItem
GROUP BY InvoiceID
As a rule of thumb, things that can easily be computed shouldn't be stored - because it's easier to compute than to synchronize the stored values.
BTW, if there's a tax involved shouldn't it be price
and not amount
?
Upvotes: 2