SQL Server track DDL CREATE USER - sql-server

I am trying to track user creation. I have looked at the DDL triggers in many posts but those seem only to track objects, not users. Is there a way for me to track/record when a user is created or deleted in SQL Server?

CREATE_USER is absolutely a trackable DDL event, as is DROP_USER, and both have been since SQL Server 2005. BOL is hard-pressed for decent examples, though. The truth is the DDL trigger eventdata schema is not flexible enough to always have an entity named the way you want (like UserName). It's not intuitive, and may be the source of your confusion, but you actually need to pull the name of the created user from ObjectName:
USE [your_database_name];
GO
CREATE TRIGGER CatchUser
ON DATABASE
FOR CREATE_USER, DROP_USER
AS
BEGIN
SET NOCOUNT ON;
DECLARE #x XML = EVENTDATA();
-- INSERT dbo.LoggingTable(Columns)
SELECT
EventType = #x.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(256)'),
UserName = #x.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(256)'),
LoginName = #x.value('(/EVENT_INSTANCE/LoginName)[1]', 'nvarchar(512)'),
StartTime = #x.value('(/EVENT_INSTANCE/PostTime)[1]', 'datetime');
END
However, if you are just trying to audit this data after the fact, you can also pull this information from the default trace, if you poll frequently enough.
DECLARE #path NVARCHAR(260);
SELECT #path = REVERSE(SUBSTRING(REVERSE([path]),
CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM sys.traces WHERE is_default = 1;
SELECT EventType = CASE EventSubClass WHEN 3 THEN 'CREATE_USER'
WHEN 4 THEN 'DROP_USER' END, TargetUserName, LoginName, StartTime
FROM sys.fn_trace_gettable(#path, DEFAULT)
WHERE EventClass = 109 -- Create DB User Event
AND DatabaseName = N'your_database_name'
ORDER BY StartTime DESC;
This will get adds and drops, and you're supposed to be able to tell from the EventSubClass which event it was, but my experience is not matching with the documentation - I get 3 for Add, 4 for Drop, but they say 1 is Add, 2 is Drop, 3 is grant access, and 4 is revoke access. shrug

Related

What's the workaround to pass parameters to triggers?

I have a trigger in "Contracts" and I also have a table called "Audits" (self explanatory).
Everything is working fine. If I insert, edit or delete, a row is inserted into Audits table by the trigger...
The problem here is that Trigger does not accept parameters... and I have a table column called "TriggeredBy" inside of the Audits table... which is supposed to have the User's ID (whoever did the insert, delete or UPDATE).
Is there a workaround that I can use so I can pass that value to that trigger?
If you have the db connection opened for the duration of the application, you can keep track of who is associated with the current db session by having a table with session if, user id.
SessionId int,
UserId varchar(20)
At login time, use ##SPID to store the session ID and associated user.
The trigger can then use ##SPID and retrieve the user ID from the table and insert it into the log table.
Option 2:
Use an application role. Allow users to connect to SQL server database using Windows Integrated Security. Call sp_setapprole to set the role. Users should be given no access to any table. The app role should have insert update delete.
You can now determine the user in your trigger.
If the desktop application used Windows authentication, you could simply use ORIGINAL_LOGIN() or SUSER_SNAME() to get the end user account name in trigger code.
With a shared SQL login, one method is to store the end user name in SQL session context for use by the trigger. Session context allows you to store name/value pairs using the sp_set_session_context procedure and read current session values with the SESSION_CONTEXT function. Call sp_set_session_context with the current user name after opening a new SQL connection so that it can be used by triggers to identify the end user.
Example T-SQL code below. Also, see this answer for other methods to set/use session level values.
CREATE TRIGGER TR_YourTable
ON dbo.YourTable
FOR INSERT, UPDATE, DELETE
AS
DECLARE #TriggeredBy sysname = COALESCE(CAST(SESSION_CONTEXT(N'end-user-name') AS sysname), N'unknown');
IF EXISTS(SELECT 1 FROM inserted) AND EXISTS(SELECT 1 FROM deleted)
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'updated', SomeColumn, #TriggeredBy
FROM deleted;
END
ELSE
BEGIN
IF EXISTS(SELECT 1 FROM inserted)
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'inserted', SomeColumn, #TriggeredBy
FROM inserted;
END
ELSE
BEGIN
INSERT INTO dbo.YourAuditTable (Action, SomeColumn, TriggeredBy)
SELECT 'deleted', SomeColumn, #TriggeredBy
FROM deleted;
END;
END;
GO
--Example T-SQL usage. Queries should be parameterized in application code.
EXEC sp_set_session_context N'end-user-name', N'me';
INSERT INTO dbo.YourTable (SomeColumn) VALUES('example');
GO

How to Automate Query Results in Microsoft SQL Management Studio 2012 and send the query results to WCF service as Instant Notifications?

I am new to SQL Server. I need track the Data Changes in Database Table inside the Microsoft SQL Server Management Studio.If any Data insertion,Deletion and modification done..in Table ,the WCF service should receive notifications from SQL Server 2012 Database.
Is this possible to send instant notifications of Data Table from SQL server to WCF Service?
Done's and Studied:-
I am already refer some websites. I have an Idea about Change Data Capture and Change Tracking procedure in SQL Server.Then i were followed the Change Data Capture Procedure to Track the Data in SQL Server.Then i look over some information about Jobs in SQL Server Data Agent.With the help of jobs,possible to automate a query execution in sql server with a time interval?
Then i am created a Database in SQL server using DML and enabled the Change Data Capture Feature for Database and Table Level,specified number of Columns!
My New Database:-
CREATE DATABASE Testcdc_feature
GO
Use Testcdc_feature;
GO
CREATE TABLE dbo.one(
Name varchar(100),
Id int,
Designation varchar(100),
Salary int,
);
Enabled Change Data Capture Feature for Database:-
use Testcdc_feature;
GO
-- enable CDC on the database
EXEC sys.sp_cdc_enable_db;
GO
Enabled CDC Feature for The Table and Specified Columns:-
USE Testcdc_feature;
GO
EXEC sys.sp_cdc_enable_table
#source_schema = N'dbo',
#source_name = N'one',
#role_name = NULL, -- Role gating not enabled
#filegroup_name = N'PRIMARY', -- should consider writing audit date to separate filegroup
#captured_column_list = N'Name,Id,Designation,Salary';
GO
Inserted Values to the Table Columns:-
USE Testcdc_feature;
GO
SET NOCOUNT ON;
INSERT INTO dbo.one VALUES(
'thirunavukkarasu',2345,'TeamLeader',12000000
);
INSERT INTO dbo.one VALUES(
'Selva',3456,'Developer',30000);
INSERT INTO dbo.one VALUES(
'John',9876,'Application Tester',45000
);
INSERT INTO dbo.one VALUES(
'Anand',56789,'Developer',56000
);
INSERT INTO dbo.one VALUES(
'Priya',6709,'Developer',78000
);
INSERT INTO dbo.one VALUES(
'Swetha',8907,'Developer',100000
);
For Check the Data Changes the query is written as below(DEMO):-
USE Testcdc_feature;
GO
DECLARE #from_lsn binary(10), #to_lsn binary(10)
SET #from_lsn = sys.fn_cdc_get_min_lsn('dbo_one')
SET #to_lsn = sys.fn_cdc_get_max_lsn()
SELECT
CT.__$start_lsn,
CT.__$operation,
CASE CT.__$operation
WHEN 1 THEN 'Delete'
WHEN 2 THEN 'Insert'
WHEN 3 THEN 'Update - Pre'
WHEN 4 THEN 'Update - Post'
END AS Operation,
CT.*,
LSN.tran_begin_time,
LSN.tran_end_time,
LSN.tran_id
FROM
cdc.fn_cdc_get_all_changes_dbo_one
(#from_lsn, #to_lsn, N'all update old') AS CT INNER JOIN
cdc.lsn_time_mapping AS LSN ON CT.__$start_lsn = LSN.start_lsn
GO
When Executing the Last Query i got output as below
I need to automate the last Query (Demo) for a time interval and send it results as instant notifications of Data modifications from SQL server to WCF Service?
The WCF Should Receive the Insertion,Modification and Deletion of Data From SQL server.is this possible?
My Question:-
1.How to automate a query execution with a time interval in MS SQL Server 2012?
2.How to send the automated query results into wcf service?
How do i achieve this? Could anyone provide me a idea or solution?
It Might me more Helpful!
From what I understand.
Your needs are to communicate to a WCF Service any transactions done to a table with a time interval.
What I recommand you is to create a trigger on your table that will insert in a specific table for transaction informations that you need (columns changed? is it INSERT/UPDATE/DELETE? previous and new values? a status flag?).
After that, you set up a new SQL job with your time constraints that will raise your WCF Service. Finally, your WCF Service will read any news data in your new transaction table.
Here is a link to setting up a Job.
EDIT : You will probably need indexes on your transaction tables particularly on the previous tables key and on your status flag. What a mean by a status flag is information related to your WCF Service (did it already read this line? If you implements a re-processing in case of error, which step this line is on your WCF Service?)
When your WCF Service will read your transaction table, to do it properly you may use transactions in order to re-process from the start. Here is a link to use transactions. I don't recommand this since your transaction table will be highly accessed and using a transaction will put locks on your table if not use properly.
Or you may Update your status flag and retrieve the data with an OUTPUT clause in order to re-process step-by-step. Here is a link to use output clause on update. (The example you are looking for is C.)
By what I am understanding, all you want is an event triggering when you
"If any Data insertion,Deletion and modification done..in Table"
what you can do is create SQL triggers, you can set them to trigger(throw an event) when you delete from, update or insert into the table.

How do I add a “last modified” and "created" column in a SQL Server table?

I'm design a new db schema for a SQL Server 2012 database.
Each table should get two extra columns called modified and created which should be automatically change as soon a row gets inserted or updated.
I don't know how rather the best way to get there.
I assuming that trigger are the best way to handle it.
I was trying to find examples with triggers.. but the tutorials which I found insert data in another table etc.
I assumed it's a quite common scenario but I couldn't find the answer yet.
The created column is simple - just a DATETIME2(3) column with a default constraint that gets set when a new row is inserted:
Created DATETIME2(3)
CONSTRAINT DF_YourTable_Created DEFAULT (SYSDATETIME())
So when you insert a row into YourTable and don't specify a value for Created, it will be set to the current date & time.
The modified is a bit more work, since you'll need to write a trigger for the AFTER UPDATE case and update it - you cannot declaratively tell SQL Server to do this for you....
Modified DATETIME2(3)
and then
CREATE TRIGGER updateModified
ON dbo.YourTable
AFTER UPDATE
AS
UPDATE dbo.YourTable
SET modified = SYSDATETIME()
FROM Inserted i
WHERE dbo.YourTable.PrimaryKey = i.PrimaryKey
You need to join the Inserted pseudo table which contains all rows that were updated with your base table on your primary key for that table.
And you'll have to create this AFTER UPDATE trigger for each table that you want to have a modified column in.
Generally, you can have the following columns:
LastModifiedBy
LastModifiedOn
CreatedBy
CreatedOn
where LastModifiedBy and CreatedBy are references to a users table (UserID) and the LastModifiedOn and CreatedOn columns are date and time columns.
You have the following options:
Solution without triggers - I have read somewhere that "The best way to write triggers is not to write such." and you should know that generally they are hurting the performance. So, if you can avoid them it is better to do so, even using triggers may look the easiest thing to do in some cases.
So, just edit all you INSERT and UPDATE statements to include the current UserID and current date and time. If such user ID can not be defined (anonymous user) you can use 0 instead and the default value of the columns (in case no user ID is specified will be NULL). When you see NULL values are inserted you should find the "guilty" statements and edit it.
Solution with triggers - you can created AFTER INSERT, UPDATE trigger and populated the users columns there. It's easy to get the current date and time in the context of the trigger (use GETUTCDATE() for example). The issue here is that the triggers do not allowed passing/accepting parameters. So, as you are not inserting the user ID value and you are not able to pass it to the trigger. How to find the current user?
You can use SET CONTEXT_INFO and CONTEXT_INFO. Before all you insert and update statements you must use the SET CONTEXT_INFO to add the current user ID to the current context and in the trigger you are using the CONTEXT_INFO function to extract it.
So, when using triggers you again need to edit all your INSERT and UPDATE clauses - that's why I prefer not to use them.
Anyway, if you need to have only date and time columns and not created/modified by columns, using triggers is more durable and easier as you are not going to edit any other statements now and in the future.
With SQL Server 2016 we can now use the SESSION_CONTEXT function to read session details. The details are set using sp_set_session_context (as read-only or read and write). The things are a little bit user-friendly:
EXEC sp_set_session_context 'user_id', 4;
SELECT SESSION_CONTEXT(N'user_id');
A nice example.
Attention, above works fine but not in all cases,
I lost a lot of time and found this helpfull:
create TRIGGER yourtable_update_insert
ON yourtable
AFTER UPDATE
as
begin
set nocount on;
update yourtable set modified=getdate(), modifiedby = suser_sname()
from yourtable t
inner join inserted i on t.uniqueid=i.uniqueid
end
go
set nocount on; is needed else you get the error:
Microsoft SQL Server Management Studio
No row was updated.
The data in row 5 was not committed.
Error Source: Microsoft.SqlServer.Management.DataTools.
Error Message: The row value(s) updated or deleted either do not make the row unique or they alter multiple rows(2 rows).
Correct the errors and retry or press ESC to cancel the change(s).
OK Help
CREATE TRIGGER [dbo].[updateModified]
ON [dbo].[Transaction_details]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Transaction_details
SET ModifedDate = GETDATE() FROM dbo.Transaction_details t JOIN inserted i ON
t.TransactionID = i.TransactionID--SYSDATETIME()
END
One important thing to consider is that you should always have the inserted / updated time for all of your tables and rows be from the same time source. There is a danger - if you do not use triggers - that different applications making direct updates to your tables will be on machines that have different times on their clocks, or that there will not be consistent use of local vs. UTC in the application layer.
Consider a case where the system making the insert or update query that directly sets the updated / modified time value has a clock that is 5 minutes behind (unlikely, but worth considering) or is using local time versus UTC. If another system is polling using an interval of 1 minute, it might miss the update.
For a number of reasons, I never expose my tables directly to applications. To handle this situation, I create a view on the table explicitly listing the fields to be accessed (including the updated / modified time field). I then use an INSTEAD OF UPDATE, INSERT trigger on the view and explicitly set the updatedAt time using the database server's clock. This way I can guarantee that the timebase for all records in the database is identical.
This has a few benefits:
It only makes one insert to the base table and you don't have to
worry about cascading triggers being called
It allows me to control at the field level what information I expose
to the business layer or to other consumers of my data
It allows me to secure the view independently from the base table
It works great on SQL Azure.
Take a look at this example of the trigger on the view:
ALTER TRIGGER [MR3W].[tgUpdateBuilding] ON [MR3W].[vwMrWebBuilding]
INSTEAD OF UPDATE, INSERT AS
BEGIN
SET NOCOUNT ON
IF EXISTS(SELECT * FROM DELETED)
BEGIN
UPDATE [dbo].[Building]
SET
,[BuildingName] = i.BuildingName
,[isActive] = i.isActive
,[updatedAt] = getdate()
FROM dbo.Building b
inner join inserted i on i.BuildingId = b.BuildingId
END
ELSE
BEGIN
INSERT INTO [dbo].[Building]
(
[BuildingName]
,[isActive]
,[updatedAt]
)
SELECT
[BuildingName]
,[isActive]
,getdate()
FROM INSERTED
END
END
I hope this helps, and I would welcome comments if there are reasons this is not the best solution.
This solution might not work for all use cases but wherever possible its a very clean way.
Create an stored procedure for inserting/updating row in table and only use this sp for modifying the table. In stored procedure you can always set created and updated column as required. e.g. setting updatedTime = GetUTCTime()

How can i quickly create a copy of my demo database in sql server?

i have a demo database, which would be used by many users concurrently. Users can query and modify the data, and query those modified data. but other users should see the changes only made by themselves. when user logs out, i would clear all those changes made by that user.
i know i can just copy the database, and connect to different databases when user log in. but if there 're too many users, i would end up getting a lot of databses in one single server.
i wonder if there's any convinient way to do that.
What about creating Temp tables in SQL
and as soon as the user logout just drop them off.
to create a temp table from your original table
Declare #usera varchar(10)
set #usera = 'Ham'
Declare #sql varchar(70)
set #sql = 'use NorthTemp select * into ##temptable_' + #usera + ' from originaltable'
exec (#sql)
and allow users to work with the TempTables
am using ## so that the table is global

In SQL Server Management Studio can I search for assets across multiple databases?

My everyday IDE is Eclipse which has a wonderful Open Resource feature (CTRL+SHIFT+R or Navigate > Open Resource) which allows the user to search for files/resources across multiple projects.
I can't find a similar feature in SQL Server Management Studio, is there one?
I believe this is what you are looking for:
http://www.red-gate.com/products/sql-development/sql-search/
It is completely free, and completely awesome.
(source: red-gate.com)
You can search for objects in a sql database using the Information Schema Views
http://msdn.microsoft.com/en-us/library/ms186778.aspx
There's one for tables, columns, functions, sprocs, etc.
select * from INFORMATION_SCHEMA.routines
where ROUTINE_DEFINITION like '%xp%_'
No. There is no default mechanism in SMS to be able to search across projects.
You could use sp_MSforeachdb like so:
sp_MSforeachdb 'SELECT * FROM ?.INFORMATION_SCHEMA.routines WHERE ROUTINE_TYPE = ''PROCEDURE'''
The above will select all procedures across all databases and return them in different result sets. Using different views, you can also select tables, columns and so forth.
I hope someone has a better answer to this than I do. In the past, I've used a CURSOR to search through all the databases and insert results into a temp table. I could then select from the temp table and show the results.
I don't have this code laying around anymore. If no one comes up with a better answer, I'll come back and edit this with some real code. I would think that there'd be a DMV for this. Anyone?
I made the following CLR stored proc to search all tables and all columns in a database with explicit parallelism. Maybe it does what you want. It doesn't search stored procs or functions, but you can look for values, column names, table names, etc, and it returns results in XML rows. Please note that this shouldn't be used in the day to day, but it's useful for occasional auditing or forensics/DBA tasks, and its definitely fast... Searches all tables on AdventureWorks in 2 seconds flat hosted on a modest desktop PC.
https://pastebin.com/RRTrt8ZN
/*
This script creates a CLR stored procedure and its assembly on a database that will let you search for
keywords separated by a space on all columns of all tables of all types except 'binary', 'varbinary', 'bit',
'timestamp', 'image', 'sql_variant', and 'hierarchyid'. This was made as a CLR stored proc to take advantage
of explicit parallelism to make the search a lot faster. Be aware that this will use many cores so only use
this for occasional DBA work. This has the potential to cause a DDoS type of situation if broad searches with
many results are hammered into the server, since each request can try to parallelize its search. An optional
parameter exists to limit parallelism to a set number of cores. You can also set filters on the tables or
columns to search, including logical operators OR, AND, NOT, and parenthesis (see examples below). Results
are returned as XML rows.
To install you need owner rights. Also, because SQL Server doesn't allow secondary CLR threads access to
the stored procedure context, we extract the connection string from the first context connection we make.
This works fine, but only if you are connected with a trusted connection (using a Windows account).
------------------------------------------------------------------
-- CLR access must be enabled on the instance for this to work. --
------------------------------------------------------------------
-- sp_configure 'show advanced options', 1; --
-- GO --
-- RECONFIGURE; --
-- GO --
-- sp_configure 'clr enabled', 1; --
-- GO --
-- RECONFIGURE; --
-- GO --
------------------------------------------------------------------
-----------------------------------------------------------------------------------
-- Database needs to be flagged trustworthy to be able to access CLR assemblies. --
-----------------------------------------------------------------------------------
-- ALTER DATABASE [AdventureWorks] SET TRUSTWORTHY ON; --
-----------------------------------------------------------------------------------
Example usages:
---------------
Using all available processors on the server:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john michael'
Limiting the server to 4 concurrent threads:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john michael', #maxDegreeOfParallelism = 4
Using logical operators in search terms:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = '(john or michael) and not jack', #tablesSearchTerm = 'not contact'
Limiting search to table names and/or column names containing some search terms:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john michael', #tablesSearchTerm = 'person contact', #columnsSearchTerm = 'address name'
Limiting search results to the first row of each table where the terms are found:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john michael', #getOnlyFirstRowPerTable = 1
Limiting the search to the schema only automatically returns only the first row for each table:
EXEC [dbo].[SearchAllTables] #tablesSearchTerm = 'person contact'
Only return the search queries:
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john michael', #tablesSearchTerm = 'person contact', #onlyOutputQueries = 1
Capturing results into temporary table and sorting:
CREATE TABLE #temp (Result NVARCHAR(MAX));
INSERT INTO #temp
EXEC [dbo].[SearchAllTables] #valueSearchTerm = 'john';
SELECT * FROM #temp ORDER BY Result ASC;
DROP TABLE #temp;
*/

Resources