-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathINSERT_INSTEAD_OF.sql
298 lines (242 loc) · 17 KB
/
INSERT_INSTEAD_OF.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
USE [WideWorldImporters]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER TRIGGER [Sales].[TI_Orders]ON
[Sales].[Orders]AFTER INSERTAS
BEGIN
RAISERROR('The TI_Orders trigger was fired',1,1);
END;
GO
/*
Add in the Boilerplate code to verify that data
was actually modified. If not, return and save
the work of the Trigger.
With this change, we won't see the message when
no data is modified.
*/
CREATE OR ALTER TRIGGER [Sales].[TI_Orders_InsteadOf]ON
[Sales].[Orders]INSTEAD OF INSERTAS
BEGIN
RAISERROR('The TI_Orders_InsteadOf trigger was fired',1,1);
END;
GO
/*
The Trigger will be fired REGARDLESS of actual data manipulation.
Remember, it is the DML Action being called which causes
the Trigger to execute
*/
INSERT INTO Sales.Orders
SELECT * FROM sales.orders WHERE OrderID = 0;
GO
DROP TRIGGER Sales.TI_Orders;
/*
With INSTEAD OF Triggers, constraints are checked only
when the trigger performs DML on the table.
CustomerID=0 does not exist, but no warning is issued
because data is not actually being manipulated.
*/
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
VALUES (0, 7, 1001, GETDATE(), DATEADD(DAY, 10, GETDATE()), 0, 10);
GO
/*
ALTER the Trigger to check if the Customer is
currently on a Credit Freeze.
If not, INSERT the data into the table
*/
CREATE OR ALTER TRIGGER [Sales].[TI_Orders_InsteadOf]ON
[Sales].[Orders]INSTEAD OF INSERTAS
BEGIN
IF (ROWCOUNT_BIG() = 0)
RETURN;
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM INSERTED)
RETURN;
-- Is this customer credit on hold?
IF EXISTS
(
SELECT 1 FROM INSERTED i
INNER JOIN Customers c on i.CustomerID = c.CustomerID
WHERE c.IsOnCreditHold = 1
)
BEGIN
RAISERROR('Customer is currently on a credit freeze',16,1);
ROLLBACK TRAN;
RETURN;
END;
-- All checks passed, insert data into table
-- Constraints will be checked with this INSERT and any
-- AFTER Triggers for this DML action will be executed
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
SELECT i.CustomerID, i.SalespersonPersonID, i.ContactPersonID, i.OrderDate, i.ExpectedDeliveryDate, i.IsUndersupplyBackordered, i.LastEditedBy
FROM INSERTED i;
END;
GO
/*
Set CustomerID=10 to be on credit freeze
*/
UPDATE sales.Customers SET IsOnCreditHold = 1 WHERE CustomerID=10;
/*
Attempt the insert.
*/
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
VALUES (10, 8, 1001, GETDATE(), DATEADD(DAY, 10, GETDATE()), 0, 10);
GO
/*
Set CustomerID=10 to be on credit freeze
*/
UPDATE sales.Customers SET IsOnCreditHold = 0 WHERE CustomerID=10;
/*
Attempt the insert again
*/
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
VALUES (10, 8, 1001, GETDATE(), DATEADD(DAY, 10, GETDATE()), 0, 10);
GO
/***********************
*
* Correcting Bad Data
*
**********************/
/*
Attempt the insert again. This time with a
provide a NULL ExpectedDeliveryDate as if coming
fro a know, bad application or integration
*/
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
VALUES (10, 8, 1001, GETDATE(), NULL, 0, 10);
GO
/*
ALTER the Trigger to include a second check for SalesPerson.
*/
CREATE OR ALTER TRIGGER [Sales].[TI_Orders_InsteadOf]ON
[Sales].[Orders]INSTEAD OF INSERTAS
BEGIN
IF (ROWCOUNT_BIG() = 0)
RETURN;
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM INSERTED)
RETURN;
-- Is this customer credit on hold?
IF EXISTS
(
SELECT 1 FROM INSERTED i
INNER JOIN Sales.Customers c on i.CustomerID = c.CustomerID
WHERE c.IsOnCreditHold = 1
)
BEGIN
RAISERROR('Customer is currently on a credit freeze',16,1);
ROLLBACK TRAN;
RETURN;
END;
-- System X always sends NULL for ExpectedDeliveryDate
-- Set to OrderDate + 10 Days
IF EXISTS
(
SELECT 1 FROM INSERTED i WHERE i.ExpectedDeliveryDate is NULL
)
BEGIN
/*
Create Temp Table of data for use throughout Trigger
This also allows us to update INSERTED data as needed for use later.
*/
SELECT * INTO #tempInserted FROM INSERTED;
UPDATE #tempInserted SET ExpectedDeliveryDate = DATEADD(day,10,OrderDate)
WHERE ExpectedDeliveryDate IS NULL;
END;
-- All checks passed, insert data into table
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
SELECT i.CustomerID, i.SalespersonPersonID, i.ContactPersonID, i.OrderDate, i.ExpectedDeliveryDate, i.IsUndersupplyBackordered, i.LastEditedBy
FROM #tempInserted i;
END;
GO
/*
Attempt the insert again. This time with a
provide a NULL ExpectedDeliveryDate as if coming
fro a know, bad application or integration
*/
INSERT INTO Sales.Orders (CustomerID, SalespersonPersonID, ContactPersonID, OrderDate, ExpectedDeliveryDate, IsUndersupplyBackordered, LastEditedBy)
VALUES (10, 8, 1001, GETDATE(), NULL, 0, 10);
GO
/*******************************
*
* UPDATING data using a VIEW
*
******************************/
/*
Create a view that only shows the Delivery Address for customers by CustomerID
*/
CREATE OR ALTER VIEW Sales.CustomerAddressDeliveryView AS
SELECT CustomerID, DeliveryAddressLine1 AS AddressLine1, DeliveryAddressLine2 AS AddressLine2,
DeliveryPostalCode AS PostalCode, CityName AS City, StateProvinceCode AS State
FROM Sales.Customers CU
INNER JOIN Application.Cities C on CU.DeliveryCityID = C.CityID
INNER JOIN Application.StateProvinces SP on C.StateProvinceID = SP.StateProvinceID;
GO
/*
Verify that the view is working.
*/
SELECT * FROM Sales.CustomerAddressDeliveryView;
GO
CREATE OR ALTER TRIGGER [Sales].[TI_CustomerAddressDeliveryView]
ON Sales.CustomerAddressDeliveryView INSTEAD OF UPDATE
AS
BEGIN
IF (ROWCOUNT_BIG() = 0)
RETURN;
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM INSERTED)
RETURN;
-- Create Temp Table for incoming data
CREATE TABLE #tempAddress (
CustomerID int,
AddressLine1 nvarchar(60) COLLATE database_default,
AddressLine2 nvarchar(60) COLLATE database_default,
PostalCode nvarchar(10) COLLATE database_default,
City nvarchar(50) COLLATE database_default,
State nvarchar(50) COLLATE database_default,
DeliveryCityID int null,
);
-- Select all data into new Temp Table
INSERT INTO #tempAddress
SELECT CustomerID, AddressLine1, AddressLine2, PostalCode, City, State, null from INSERTED;
-- Update the Temp Table with the DeliveryCityID
-- based on City name and State
-- NOTE: SIGNIFICANTLY MORE ERROR CHECKING WOULD HAVE TO BE DONE IN PRODUCTION!!
UPDATE #tempAddress SET DeliveryCityID = C.CityID FROM #tempAddress
INNER JOIN Application.Cities C ON #tempAddress.City = C.CityName
INNER JOIN Application.StateProvinces SP on C.StateProvinceID = SP.StateProvinceID
WHERE SP.StateProvinceCode = #tempAddress.State
-- Finally Update the Customer table with the
-- correct Address Lines, Postal Code, and DeliveryCityID
-- that was looked up from the Cities table
UPDATE Sales.Customers SET
DeliveryAddressLine1 = i.AddressLine1,
DeliveryAddressLine2 = i.AddressLine2,
DeliveryPostalCode = i.PostalCode,
DeliveryCityID = i.DeliveryCityID
FROM #tempAddress i INNER JOIN Sales.Customers
ON i.CustomerID = Sales.Customers.CustomerID
WHERE i.DeliveryCityID IS NOT NULL;
END;
/*
Examine the current customer address first
*/
select CustomerID, DeliveryAddressLine1, DeliveryAddressLine2, DeliveryPostalCode, DeliveryCityID
from Sales.Customers WHERE CustomerID=10;
/*
Update the values USING the view!
*/
UPDATE Sales.CustomerAddressDeliveryView SET
AddressLine1 = '100 Jones Road',
AddressLine2 = 'Suite 200',
PostalCode = '90210',
City = 'Bell',
State = 'CA'
WHERE CustomerId = 10;
/*
Verify the updated customer address
*/
select CustomerID, DeliveryAddressLine1, DeliveryAddressLine2, DeliveryPostalCode, DeliveryCityID
from Sales.Customers WHERE CustomerID=10;