Beginning Microsoft SQL Server 2008 ... - S3 Tech Training

Beginning Microsoft SQL Server 2008 ... - S3 Tech Training Beginning Microsoft SQL Server 2008 ... - S3 Tech Training

cdn.s3techtraining.com
from cdn.s3techtraining.com More from this publisher
17.06.2013 Views

Chapter 15: Triggers 460 IF EXISTS ( SELECT ‘True’ FROM Inserted i JOIN Deleted d ON i.ProductID = d.ProductID AND i.LocationID = d.LocationID WHERE (d.Quantity - i.Quantity) > d.Quantity / 2 AND d.Quantity – i.Quantity > 0 ) BEGIN RAISERROR(‘Cannot reduce stock by more than 50%% at once.’,16,1) ROLLBACK TRAN END Before we test this, let’s analyze what we’re doing here. First, we’re making use of an IF EXISTS just as we have throughout this chapter. We want to do the rollback only if something exists that meets the evil, mean, and nasty criteria that we’ll be testing for. Then we join the Inserted and Deleted tables together — this is what gives us the chance to compare the two. Our WHERE clause is the point where things might become a bit confusing. The first line of it is pretty straightforward. It implements the nominal statement of our business requirement; updates to the Quantity column that are more than half the units we previously had on hand will meet the EXISTS criterion and cause the conditional code to run (rejecting the transaction). The next line, though, is not quite so straightforward. As with all things in programming, we need to think beyond the nominal statement of the problem, and think about other ramifications. The requirement really applies only to reductions in orders — we certainly don’t want to restrict how many units are put in stock — so we make sure that we worry only about updates where the number in stock after the update is less than before the update. The > 0 requirement also addresses the scenario where there was only one item in stock. In such a scenario, we want to go ahead and sell that last one, but 1-1 is greater than ½, and the trigger would have rejected it without our > 0 addition. If both of these conditions have been met (over 50 percent and a reduction, rather than addition, to the inventory), then we raise the error. Notice the use of two % signs, rather than one, in RAISERROR. Remember that a % works as a placeholder for a parameter, so one % by itself won’t show up when your error message comes out. By putting two in a row (%%), we let SQL Server know that we really did want to print out a percent sign. OK — let’s check how it works. We’ll just pick a record and try to do an update that reduces the stock by more than 50 percent: UPDATE Production.ProductInventory SET Quantity = 1 -- Was 408 if you want to set it back WHERE ProductID = 1 AND LocationID = 1

I just picked out “Adjustable Race” as our victim, but you could have chosen any ProductID, as long as you set the value to less than 50 percent of its previous value. If you do, you’ll get the expected error: Msg 50000, Level 16, State 1, Procedure ProductIsRationed, Line 16 Cannot reduce stock by more than 50% at once. Msg 3609, Level 16, State 1, Line 1 The transaction ended in the trigger. The batch has been aborted. Note that we could have also implemented this in the SalesOrderDetail table by referencing the actual order quantity against the current Quantity amount, but we would have run into several problems: ❑ Updates that change: Is the process that’s creating the SalesOrderDetail record updating ProductInventory before or after the SalesOrderDetail record? That makes a difference in how we make use of the Quantity value in the ProductInventory table to calculate the effect of the transaction. ❑ The inventory external to the SalesOrderDetails table updates would not be affected: They could still reduce the inventory by more than half (this may actually be a good thing in many circumstances, but it’s something that has to be thought about). Using Triggers for Custom Error Messages We’ve already touched on this in some of our other examples, but remember that triggers can be handy for when you want control over the error message or number that gets passed out to your user or client application. With a CHECK constraint for example, you’re just going to get the standard 547 error along with its rather nondescript explanation. As often as not, this is less than helpful in terms of the user really figuring out what went wrong — indeed, your client application often doesn’t have enough information to make an intelligent and helpful response on behalf of the user. In short, sometimes you create triggers when there is already something that would give you the data integrity that you want, but won’t give you enough information to handle it. It’s worth noting that the need for custom error messages in SQL Server should be relatively rare, although passing custom error codes is often useful. Why not pass the custom error message? Well, one would think that you probably have an application layer on top of it, and it is likely going to want to put more context on the error anyway, so the SQL-Server-specific text may not be all that useful. Using a special error code may, however, be very useful to your application in terms of determining what exactly happened and applying the correct client-side error handling code. Other Common Uses for T riggers In addition to the straight data integrity uses, triggers have a number of other uses. Indeed, the possibilities are fairly limitless, but here are a few common examples: ❑ Updating summary information Chapter 15: Triggers 461

Chapter 15: Triggers<br />

460<br />

IF EXISTS<br />

(<br />

SELECT ‘True’<br />

FROM Inserted i<br />

JOIN Deleted d<br />

ON i.ProductID = d.ProductID<br />

AND i.LocationID = d.LocationID<br />

WHERE (d.Quantity - i.Quantity) > d.Quantity / 2<br />

AND d.Quantity – i.Quantity > 0<br />

)<br />

BEGIN<br />

RAISERROR(‘Cannot reduce stock by more than 50%% at once.’,16,1)<br />

ROLLBACK TRAN<br />

END<br />

Before we test this, let’s analyze what we’re doing here.<br />

First, we’re making use of an IF EXISTS just as we have throughout this chapter. We want to do the<br />

rollback only if something exists that meets the evil, mean, and nasty criteria that we’ll be testing for.<br />

Then we join the Inserted and Deleted tables together — this is what gives us the chance to compare<br />

the two.<br />

Our WHERE clause is the point where things might become a bit confusing. The first line of it is pretty<br />

straightforward. It implements the nominal statement of our business requirement; updates to the<br />

Quantity column that are more than half the units we previously had on hand will meet the EXISTS<br />

criterion and cause the conditional code to run (rejecting the transaction).<br />

The next line, though, is not quite so straightforward. As with all things in programming, we need to<br />

think beyond the nominal statement of the problem, and think about other ramifications. The requirement<br />

really applies only to reductions in orders — we certainly don’t want to restrict how many units<br />

are put in stock — so we make sure that we worry only about updates where the number in stock after<br />

the update is less than before the update. The > 0 requirement also addresses the scenario where there<br />

was only one item in stock. In such a scenario, we want to go ahead and sell that last one, but 1-1 is greater<br />

than ½, and the trigger would have rejected it without our > 0 addition.<br />

If both of these conditions have been met (over 50 percent and a reduction, rather than addition, to the<br />

inventory), then we raise the error. Notice the use of two % signs, rather than one, in RAISERROR. Remember<br />

that a % works as a placeholder for a parameter, so one % by itself won’t show up when your error message<br />

comes out. By putting two in a row (%%), we let <strong>SQL</strong> <strong>Server</strong> know that we really did want to print<br />

out a percent sign.<br />

OK — let’s check how it works. We’ll just pick a record and try to do an update that reduces the stock by<br />

more than 50 percent:<br />

UPDATE Production.ProductInventory<br />

SET Quantity = 1 -- Was 408 if you want to set it back<br />

WHERE ProductID = 1<br />

AND LocationID = 1

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!