/
5297_Chapter_10 (TSQL Only).sql
433 lines (374 loc) · 14.5 KB
/
5297_Chapter_10 (TSQL Only).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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
-------------------------------------------------------------------
-- Ad hoc SQL - flexibility and control
-------------------------------------------------------------------
CREATE DATABASE architectureChapter
GO
Use architectureChapter
go
-------------------------------------------------------------------
CREATE SCHEMA sales
GO
CREATE TABLE sales.contact
(
contactId int CONSTRAINT PKsales_contact PRIMARY KEY,
firstName varchar(30),
lastName varchar(30),
companyName varchar(100),
contactNotes varchar(max),
personalNotes varchar(max),
CONSTRAINT AKsales_contact UNIQUE (firstName, lastName, companyName)
)
GO
-------------------------------------------------------------------
SELECT contactId, firstName, lastName, companyName,
right(contactNotes,500) as notesEnd
FROM sales.contact
GO
-------------------------------------------------------------------
SELECT contactId, firstName, lastName, companyName
FROM sales.contact
GO
-------------------------------------------------------------------
CREATE TABLE sales.purchase
(
purchaseId int CONSTRAINT PKsales_purchase PRIMARY KEY,
amount numeric(10,2),
purchaseDate datetime,
contactId int
CONSTRAINT FKsales_contact$hasPurchasesIn$sales_purchase
REFERENCES sales.contact(contactId)
)
GO
-------------------------------------------------------------------
SELECT contact.contactId, contact.firstName, contact.lastName,
sales.yearToDateSales, sales.lastSaleDate
FROM sales.contact as contact
LEFT OUTER JOIN
(SELECT contactId,
SUM(amount) AS yearToDateSales,
MAX(purchaseDate) AS lastSaleDate
FROM sales.purchase
WHERE purchaseDate >= --the first day of the current year
cast(datepart(year,getdate()) as char(4)) + '0101'
GROUP by contactId) AS sales
ON contact.contactId = sales.contactId
WHERE contact.lastName like 'Johns%'
GO
-------------------------------------------------------------------
SELECT contact.contactId, contact.firstName, contact.lastName
--,sales.yearToDateSales, sales.lastSaleDate
FROM sales.contact as contact
-- LEFT OUTER JOIN
-- (SELECT contactId,
-- SUM(amount) AS yearToDateSales,
-- MAX(purchaseDate) AS lastSaleDate
-- FROM sales.purchase
-- WHERE purchaseDate >= --the first day of the current year
-- cast(datepart(year,getdate()) as char(4)) + '0101'
-- GROUP by contactId) AS sales
-- ON contact.contactId = sales.contactId
WHERE contact.lastName like 'Johns%'
GO
-------------------------------------------------------------------
--note, no rows will be updated since no data has been added
UPDATE sales.contact
SET firstName = 'First Name',
lastName = 'Last Name',
companyName = 'Company Name',
contactNotes = 'Notes about the contact',
personalNotes = 'Notes about the person'
WHERE contactId = 1
GO
-------------------------------------------------------------------
UPDATE sales.contact
SET firstName = 'First Name'
WHERE contactId = 1
GO
-------------------------------------------------------------------
SELECT firstName, lastName, companyName
FROM sales.contact
WHERE firstName like 'firstNameValue%'
AND lastName like 'lastNamevalue%'
GO
-------------------------------------------------------------------
SELECT firstName, lastName, companyName
FROM sales.contact
WHERE lastName like 'lastNamevalue%'
GO
-------------------------------------------------------------------
-------------------------------------------------------------------
-- Ad hoc SQL - Performance
-------------------------------------------------------------------
ALTER DATABASE AdventureWorks
SET PARAMETERIZATION SIMPLE
Go
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address AS address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 = '1, rue Pierre-Demoulin'
GO
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address AS address
join Person.StateProvince AS state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 = '1, rue Pierre-Demoulin'
GO
-------------------------------------------------------------------
SELECT address.AddressLine1, address.AddressLine2
FROM Person.Address AS address
WHERE address.AddressLine1 = '1, rue Pierre-Demoulin'
GO
-------------------------------------------------------------------
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address AS address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 = '1, rue Pierre-Demoulin'
GO
-------------------------------------------------------------------
ALTER DATABASE AdventureWorks
SET PARAMETERIZATION FORCED
GO
SET SHOWPLAN_TEXT ON
go
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address AS address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 like '1, rue Pierre-Demoulin'
GO
-------------------------------------------------------------------
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address AS address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 like '1, rue Pierre-Demoulin'
GO
-------------------------------------------------------------------
-------------------------------------------------------------------
-- Stored Procedures
-------------------------------------------------------------------
CREATE PROCEDURE person.address$select
(
@addressLine1 nvarchar(120) = '%',
@city nvarchar(60) = '%',
@state nchar(3) = '___', --special because it is a char column
@postalCode nvarchar(8) = '%'
) AS
--simple procedure to execute a single query
SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address as address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.AddressLine1 like @addressLine1
AND address.City like @city
AND state.StateProvinceCode like @state
AND address.PostalCode like @postalCode
GO
-------------------------------------------------------------------
person.address$select @city = 'london'
GO
person.address$select @postalCode = '3%', @state = 'TN'
GO
-------------------------------------------------------------------
-------------------------------------------------------------------
-- Stored Procedures, Dynamic Procedures
-------------------------------------------------------------------
ALTER PROCEDURE person.address$select
(
@addressLine1 nvarchar(120) = '%',
@city nvarchar(60) = '%',
@state nchar(3) = '___',
@postalCode nvarchar(50) = '%'
) AS
BEGIN
DECLARE @query varchar(max)
SET @query =
'SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address as address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE address.City like ''' + @city + '''
AND state.StateProvinceCode like ''' + @state + '''
AND address.PostalCode like ''' + @postalCode + '''
--this param is last because it is largest to make the example
--easier as this column is very large
AND address.AddressLine1 like ''' + @addressLine1 + ''''
SELECT @query --just for testing purposes
EXECUTE (@query)
END
GO
-------------------------------------------------------------------
EXECUTE person.address$select @city = 'london'
GO
EXECUTE person.address$select @addressLine1 = '~''select name from sysusers--'
GO
-------------------------------------------------------------------
ALTER PROCEDURE person.address$select
(
@addressLine1 nvarchar(120) = '%',
@city nvarchar(60) = '%',
@state nchar(3) = '___',
@postalCode nvarchar(50) = '%'
) AS
BEGIN
DECLARE @query varchar(max)
SET @query =
'SELECT address.AddressLine1, address.AddressLine2,
address.City, state.StateProvinceCode, address.PostalCode
FROM Person.Address as address
join Person.StateProvince as state
on address.stateProvinceId = state.stateProvinceId
WHERE 1=1'
IF @city <> '%'
SET @query = @query + ' AND address.City like ' + quotename(@city,'''')
IF @state <> '___'
SET @query = @query + ' AND state.StateProvinceCode like ' +
quotename(@state,'''')
IF @postalCode <> '%'
SET @query = @query + ' AND address.City like ' + quotename(@city,'''')
IF @addressLine1 <> '%'
SET @query = @query + ' AND address.addressLine1 like ' +
quotename(@addressLine1,'''')
SELECT @query
EXECUTE (@query)
END
GO
-------------------------------------------------------------------
-------------------------------------------------------------------
-- Security
-------------------------------------------------------------------
USE architectureChapter
GO
CREATE LOGIN fred with password = 'freddy'
CREATE USER fred from login fred
GO
-------------------------------------------------------------------
CREATE PROCEDURE testChaining
AS
EXECUTE ('select * from sales.contact')
GO
GRANT EXECUTE ON testChaining TO fred
GO
-------------------------------------------------------------------
EXECUTE AS user = 'fred'
EXECUTE testChaining
REVERT
GO
-------------------------------------------------------------------
ALTER PROCEDURE testChaining
WITH EXECUTE AS SELF
AS
EXECUTE ('select * from person.contact')
GO
-------------------------------------------------------------------
EXECUTE AS user = 'fred'
EXECUTE testChaining
REVERT
GO
-------------------------------------------------------------------
CREATE PROCEDURE dbo.doAnything
(
@query nvarchar(4000)
)
WITH EXECUTE AS OWNER
AS
EXECUTE (@query)
GO
-------------------------------------------------------------------
CREATE PROCEDURE sales.contact$update
(
@contactId int,
@firstName varchar(30),
@lastName varchar(30),
@companyName varchar(100),
@contactNotes varchar(max),
@personalNotes varchar(max)
)
AS
BEGIN TRY
UPDATE sales.contact
SET firstName = @firstName,
lastName = @lastName,
companyName = @companyName,
contactNotes = @contactNotes,
personalNotes = @personalNotes
WHERE contactId = @contactId
END TRY
BEGIN CATCH
EXECUTE dbo.errorLog$insert --from back in chapter 6
RAISERROR ('Error creating new sales.contact',16,1)
END CATCH
GO
-------------------------------------------------------------------
CREATE TRIGGER sales.contact$insteadOfUpdate
ON sales.contact
INSTEAD OF UPDATE
AS
BEGIN
DECLARE @rowsAffected int, --stores the number of rows affected
@msg varchar(2000) --used to hold the error message
SET @rowsAffected = @@rowcount
--no need to continue on if no rows affected
IF @rowsAffected = 0 return
SET NOCOUNT ON --to avoid the rowcount messages
SET ROWCOUNT 0 --in case the client has modified the rowcount
BEGIN TRY
--[validation blocks]
--[modification blocks]
--<peform action>
UPDATE contact
SET firstName = inserted.firstName,
lastName = inserted.lastName,
companyName = inserted.companyName
FROM sales.contact as contact
JOIN inserted
on inserted.contactId = contact.contactId
UPDATE contact
SET personalNotes = inserted.personalNotes
FROM sales.contact as contact
JOIN inserted
on inserted.contactId = contact.contactId
--this correlated subquery checks for rows that have changed
WHERE EXISTS (SELECT *
FROM deleted
WHERE deleted.contactId = inserted.contactId
AND deleted.personalNotes <> inserted.personalNotes
or (deleted.personalNotes is null and
inserted.personalNotes is not null)
or (deleted.personalNotes is not null and
inserted.personalNotes is null))
UPDATE contact
SET contactNotes = inserted.contactNotes
FROM sales.contact as contact
JOIN inserted
on inserted.contactId = contact.contactId
--this correlated subquery checks for rows that have changed
WHERE EXISTS (SELECT *
FROM deleted
WHERE deleted.contactId = inserted.contactId
AND deleted.contactNotes <> inserted.contactNotes
or (deleted.contactNotes is null and
inserted.contactNotes is not null)
or (deleted.contactNotes is not null and
inserted.contactNotes is null))
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRANSACTION
EXECUTE dbo.errorLog$insert
DECLARE @ERROR_MESSAGE varchar(8000)
SET @ERROR_MESSAGE = ERROR_MESSAGE()
RAISERROR (@ERROR_MESSAGE,16,1)
END CATCH
END
GO
-------------------------------------------------------------------