Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1ccad56

Browse files
Trigers & Functions
1 parent 1dc6994 commit 1ccad56

File tree

9 files changed

+367
-0
lines changed

9 files changed

+367
-0
lines changed

‎.vs/slnx.sqlite

88 KB
Binary file not shown.

‎Triger_Functions/DDL.sql

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
Use WideWorldImporters-Pluralsight;
2+
GO
3+
4+
/*
5+
Create a DDL trigger in this current database that prevents
6+
altering or dropping a table
7+
*/
8+
CREATE OR ALTER TRIGGER TDB_PreventTableDropOrAlter
9+
ON DATABASE
10+
FOR DROP_TABLE,ALTER_TABLE
11+
AS
12+
BEGIN
13+
PRINT 'DROP and ALTER table events are not allowed. Disable trigger TDB_PreventTableDropOrAlter to complete action.'
14+
ROLLBACK
15+
END
16+
17+
/*
18+
Attempt to drop our Audit Log table
19+
*/
20+
DROP TABLE Application.AuditLog;
21+
22+
/*
23+
Attempt to add a column to the Application.Person table
24+
*/
25+
ALTER TABLE Application.People ADD TwitterHandle nvarchar(100);
26+
27+
/*
28+
Disable the trigger so that we can complete our modification;
29+
*/
30+
DISABLE TRIGGER TDB_PreventTableDropOrAlter ON DATABASE;
31+
32+
/*
33+
Attempt to add a column to the Application.Person table again
34+
*/
35+
ALTER TABLE Application.People ADD TwitterHandle nvarchar(100);
36+
37+
/*
38+
Enable the trigger again
39+
*/
40+
ENABLE TRIGGER TDB_PreventTableDropOrAlter ON DATABASE;
41+
GO
42+
43+
44+
/*************************************
45+
*
46+
* Log DDL Events to a table
47+
*
48+
*************************************/
49+
50+
CREATE TABLE Application.AuditLogDDL
51+
(
52+
Id INT IDENTITY,
53+
EventTime DATETIME,
54+
EventType NVARCHAR(100),
55+
LoginName NVARCHAR(100),
56+
Command NVARCHAR(MAX)
57+
)
58+
GO
59+
60+
/*
61+
This will log ALL DDL events at the database level.
62+
63+
In reality, this should be refined to only capture events
64+
on specific types OR this audit table should be aggressively
65+
managed to only keep relevant events and table size small.
66+
*/
67+
CREATE TRIGGER AuditLogDDLEvents
68+
ON DATABASE
69+
FOR DDL_DATABASE_LEVEL_EVENTS
70+
AS
71+
BEGIN
72+
SET NOCOUNT ON
73+
DECLARE @EventData XML = EVENTDATA()
74+
INSERT INTO Application.AuditLogDDL(EventTime,EventType,LoginName,Command)
75+
SELECT @EventData.value('(/EVENT_INSTANCE/PostTime)[1]', 'DATETIME'),
76+
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'VARCHAR(100)'),
77+
@EventData.value('(/EVENT_INSTANCE/LoginName)[1]', 'VARCHAR(100)'),
78+
@EventData.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)')
79+
END
80+
81+
82+
/*
83+
Now, let's perform some DDL Events and see what we get
84+
*/
85+
86+
/*
87+
Attempt to add a column to the Application.Person table
88+
*/
89+
ALTER TABLE Application.People ADD InstagramHandle nvarchar(100);
90+
91+
/*
92+
Check the Log
93+
*/
94+
SELECT * FROM Application.AuditLogDDL;
95+
96+
/*
97+
Disable the trigger so that we can complete our modification;
98+
*/
99+
DISABLE TRIGGER TDB_PreventTableDropOrAlter ON DATABASE;
100+
101+
/*
102+
Attempt to add a column to the Application.Person table again
103+
*/
104+
ALTER TABLE Application.People ADD InstagramHandle nvarchar(100);
105+
106+
/*
107+
Check the Log
108+
*/
109+
SELECT * FROM Application.AuditLogDDL;
110+
111+
112+
/*
113+
Enable the trigger again
114+
*/
115+
ENABLE TRIGGER TDB_PreventTableDropOrAlter ON DATABASE;
116+
GO
7.44 KB
Binary file not shown.

‎Triger_Functions/INSERT_AFTER.sql

9.86 KB
Binary file not shown.

‎Triger_Functions/INSERT_INSTEAD_OF.sql

17 KB
Binary file not shown.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
USE master;
2+
GO
3+
4+
/*
5+
Create a new local login to test LOGON Triggers.
6+
7+
ONLY do something like this in a local, non-shared environment!
8+
*/
9+
CREATE LOGIN login_test WITH PASSWORD = 'abc123!';
10+
GO
11+
12+
/*
13+
Grant them access to see DMVs. There are many better ways to
14+
accomplish this in production, but this will work for the demo.
15+
*/
16+
GRANT VIEW SERVER STATE TO login_test;
17+
GO
18+
19+
/*
20+
Create a LOGON trigger that will limit the number of connections
21+
for this specific user. They cannot open more than 2 connections.
22+
*/
23+
CREATE OR ALTER TRIGGER LimitConnectionsForUser
24+
ON ALL SERVER
25+
FOR LOGON
26+
AS
27+
BEGIN
28+
IF ORIGINAL_LOGIN()= 'login_test' AND
29+
(SELECT COUNT(*) FROM sys.dm_exec_sessions
30+
WHERE is_user_process = 1 AND
31+
original_login_name = 'login_test') > 2
32+
ROLLBACK;
33+
END;
34+
35+
36+
37+
38+
39+
/***********************************************
40+
*
41+
* Create a server-level audit logging database
42+
*
43+
**********************************************/
44+
45+
/*
46+
Create an Audit Log Database for server-level events. This
47+
will be owned by my local user for now.
48+
*/
49+
CREATE DATABASE AuditLogDB;
50+
GO
51+
52+
USE AuditLogDB;
53+
GO
54+
55+
/*
56+
Create a table within the new database to store login event data
57+
*/
58+
CREATE TABLE LogonEventData
59+
(
60+
LogonTime datetime,
61+
SPID int,
62+
HostName nvarchar(50),
63+
AppName nvarchar(100),
64+
LoginName nvarchar(50),
65+
ClientHost nvarchar(50)
66+
);
67+
GO
68+
69+
/*
70+
Create a second LOGON Trigger to log information.
71+
72+
This will be executed each time as the owner of the logging
73+
database. Without this, the Trigger would be executed as the
74+
CALLER ('test_login' in this case) and it would return an error
75+
because they do not have permission to write into that
76+
database.
77+
78+
Much care and thought should be given to how permissions are
79+
used within your environments to protect data and users
80+
appropriately.
81+
*/
82+
CREATE OR ALTER TRIGGER SuccessfulLogonAudit
83+
ON ALL SERVER WITH EXECUTE AS 'RYANB-DEV\Ryan'
84+
FOR LOGON
85+
AS
86+
BEGIN
87+
DECLARE @LogonTriggerData xml,
88+
@EventTime datetime,
89+
@SPID int,
90+
@LoginName nvarchar(50),
91+
@ClientHost nvarchar(50),
92+
@LoginType nvarchar(50),
93+
@HostName nvarchar(50),
94+
@AppName nvarchar(100);
95+
96+
SET @LogonTriggerData = EventData();
97+
98+
SET @EventTime = @LogonTriggerData.value('(/EVENT_INSTANCE/PostTime)[1]', 'datetime');
99+
SET @SPID = @LogonTriggerData.value('(/EVENT_INSTANCE/SPID)[1]', 'int');
100+
SET @LoginName = @LogonTriggerData.value('(/EVENT_INSTANCE/LoginName)[1]', 'nvarchar(50)');
101+
SET @ClientHost = @LogonTriggerData.value('(/EVENT_INSTANCE/ClientHost)[1]', 'nvarchar(50)');
102+
SET @HostName = HOST_NAME();
103+
SET @AppName = APP_NAME();
104+
105+
INSERT INTO AuditLogDB.dbo.LogonEventData
106+
(
107+
LogonTime, SPID, HostName, AppName, LoginName, ClientHost
108+
)
109+
VALUES
110+
(
111+
@EventTime, @SPID, @HostName, @AppName, @LoginName, @ClientHost
112+
)
113+
END
114+
GO
115+
116+
/*
117+
Create a new query window and check the log
118+
*/
119+
SELECT * FROM AuditLogDB.dbo.LogonEventData
120+
ORDER BY LogonTime DESC;
121+
122+
123+
/*
124+
We can reset the order of these Triggers. Because everything is run in
125+
the same transaction, more work would be needed to log attempts that
126+
are successful but cross the number of connections limit.
127+
*/
128+
sp_settriggerorder @triggername = 'SuccessfulLogonAudit', @order = 'first',
129+
@stmttype = 'LOGON', @namespace = 'SERVER';
130+
GO
131+
132+
sp_settriggerorder @triggername = 'LimitConnectionsForUser', @order = 'last',
133+
@stmttype = 'LOGON', @namespace = 'SERVER';
134+
GO
135+
136+
137+
138+
139+
140+
141+
142+
143+
/*
144+
Cleanup
145+
*/
146+
147+
DROP TRIGGER LimitConnectionsForUser ON ALL SERVER;
148+
DROP TRIGGER SuccessfulLogonAudit ON ALL SERVER;
149+
150+
DROP DATABASE AuditLogDB;
151+
152+
DROP LOGIN login_test;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
Look at a select statement with three built-in Scalar functions
3+
*/
4+
SELECT MONTH(GETDATE()) AS [MONTH], YEAR(GETDATE()) AS [YEAR];
5+
GO
6+
7+
8+
/*
9+
Create a very simple User-Defined, Multi-Statement Scalar Function
10+
*/
11+
CREATE OR ALTER FUNCTION dbo.SuperAdd_scaler(@a INT, @b INT)
12+
RETURNS INT
13+
WITH SCHEMABINDING
14+
AS
15+
BEGIN
16+
/*
17+
Although this just happens to be one statement, I can have more than
18+
one in a scalar function, so don't let that confuse you.
19+
*/
20+
RETURN @a + @b;
21+
END;
22+
GO
23+
24+
/*
25+
Do simple addition
26+
*/
27+
select dbo.SuperAdd_scaler(2,2);
28+
GO
29+
30+
/*
31+
Attempt to pass in bad data
32+
*/
33+
select dbo.SuperAdd_scaler('a',3);
34+
GO
35+
36+
/*
37+
Create a slightly more complext Multi-Statement Scalar Function that
38+
returns the Fiscal Year Ending of a provided date/time.
39+
40+
Defaulting to June as the default Fiscal Ending Month
41+
*/
42+
CREATE OR ALTER FUNCTION dbo.FiscalYearEnding(@SaleDate DATETIME, @FiscalEndMonth INT = 6)
43+
RETURNS INT
44+
WITH SCHEMABINDING
45+
AS
46+
BEGIN
47+
DECLARE @saleMonth INT = MONTH(@SaleDate);
48+
DECLARE @saleYear INT = YEAR(@SaleDate);
49+
DECLARE @fiscalYear INT = @saleYear;
50+
51+
IF(@saleMonth > @FiscalEndMonth AND @FiscalEndMonth != 1)
52+
BEGIN
53+
SET @fiscalYear = @saleYear + 1;
54+
END;
55+
56+
RETURN @fiscalYear;
57+
END;
58+
GO
59+
60+
/*
61+
Select a given date and see if we get the correct year
62+
*/
63+
64+
SELECT '2019年01月01日' SampleDate, dbo.FiscalYearEnding('2019年01月01日',1) FiscalYear; -- 2019
65+
SELECT '2019年07月01日' SampleDate, dbo.FiscalYearEnding('2019年07月01日',6) FiscalYear; -- 2020
66+
SELECT '2019年07月01日' SampleDate, dbo.FiscalYearEnding('2019年07月01日',7) FiscalYear; -- 2019
67+
SELECT '2019年12月01日' SampleDate, dbo.FiscalYearEnding('2019年05月01日',4) FiscalYear; -- 2020
68+
SELECT '2019年12月01日' SampleDate, dbo.FiscalYearEnding('2019年12月01日',12) FiscalYear; -- 2019
69+
70+
/*
71+
Now on some real data
72+
*/
73+
SELECT TOP 100 OrderId, OrderDate, dbo.FiscalYearEnding(OrderDate, DEFAULT) as FiscalSaleYear from Sales.Orders
74+
WHERE OrderDate > '2013年06月28日'
75+
GO
76+
77+
/*
78+
As stated earlier, Scalar Functions are often used in WHERE predicates. Unfortunately
79+
both of these examples are very inefficient because they prevent SARGability, which
80+
we'll discuss in a bit.
81+
82+
These will both cause the Sales.Orders table to be scanned row-by-row to calculate the
83+
year and fiscal year respectively.
84+
85+
Finding Orders based on YEAR
86+
*/
87+
SELECT TOP 100 OrderId, OrderDate, dbo.FiscalYearEnding(OrderDate, DEFAULT) as FiscalSaleYear from Sales.Orders
88+
WHERE YEAR(OrderDate) > 2015
89+
GO
90+
91+
/*
92+
Using our new FiscalYearEnding function
93+
*/
94+
SELECT TOP 100 OrderId, OrderDate, dbo.FiscalYearEnding(OrderDate, DEFAULT) as FiscalSaleYear from Sales.Orders
95+
WHERE dbo.FiscalYearEnding(OrderDate,DEFAULT) > 2015
96+
GO
97+
98+
99+

‎Triger_Functions/Trigger_Order.sql

11 KB
Binary file not shown.

‎Triger_Functions/UPDATE_AFTER.sql

7.43 KB
Binary file not shown.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /