how to trace SQL table drop create events - sql-server

I have some mysterious problem where every day one table in DB (SQL Server 2016) is being recreated (I suppose dropped and created) with old data. I checked various options to try to find what process is doing this, however was unable to do that.
Scheduled Tasks - nothing
SQL Agent Jobs - nothing
How to trace what user/application/anythingelse is doing this ?
I tried launching SQL Profiler and starting manual trace, but after some time (half a day or so) it just stopped.

The default trace captures schema changes. Review the Schema Change History report or run the query below to retrieve the info in T-SQL. Note that the default trace rollover files are limited to 5 files of up to 20MB each so older events may have rolled off.
--select object created and deleted information from default trace
SELECT
trace_table.StartTime
, te.name
, trace_table.ObjectName
, trace_table.ApplicationName
, trace_table.LoginName
FROM (
SELECT REVERSE(SUBSTRING(REVERSE(path), CHARINDEX('\', REVERSE(path)) , 255)) + 'log.trc'
FROM sys.traces
WHERE
is_default = 1
) AS trace(path)
CROSS APPLY sys.fn_trace_gettable(trace.path, DEFAULT) AS trace_table
JOIN sys.trace_events AS te ON
te.trace_event_id = trace_table.EventClass
WHERE
EventSubClass = 0
AND name IN('Object:Created', 'Object:Deleted')
ORDER BY StartTime;

create a database trigger and log the create/drop table events:
create table dbo.traceTabledropcreate(EventDataXML xml, LogDatetime datetime default(getdate()));
go
create or alter trigger dbtrigger_traceTabledropcreate
on database
with execute as 'dbo'
for CREATE_TABLE, DROP_TABLE
as
begin
set nocount on;
--insert into dbo.traceTabledropcreate(EventDataXML)
--values (EVENTDATA());
declare #sessionxml xml =
(
select EVENTDATA(),
(
select *
from sys.dm_exec_sessions
where session_id = ##spid
for xml path('sessioninfo'), type
)
for xml path('')
);
insert into dbo.traceTabledropcreate(EventDataXML)
values (#sessionxml);
end
go
---..... and wait....
--..test
create table dbo.testtable(id int)
go
select *
from dbo.traceTabledropcreate
go
drop table dbo.testtable
go
select *
from dbo.traceTabledropcreate
go

Related

How to use extended events to track tables used in stored procedures being run

How do I use extended events (SQL Server 2012) to tell me when certain tables are used in stored procedures. I want to drop some tables and so I want to know if the stored procedures the use those tables are actually being run.
The code sample sets up the supporting objects. And creates a session that I expect to work but doesn't. When you run those stored procedures (ListEmp and ListProd), I want them picked up because they contain the tables I am tracking (Employees and Products).
Note, I also tried using the sp_statement_starting event:
-- setup supporting objects
CREATE TABLE Employees (Col1 VARCHAR(10));
CREATE TABLE Products (Col1 VARCHAR(10));
GO
INSERT INTO Employees(Col1) VALUES ('Bob');
INSERT INTO Products(Col1) VALUES ('Bolts');
GO
CREATE PROCEDURE ListEmp
AS
SELECT * FROM Employees;
GO
CREATE PROCEDURE ListProd
AS
SELECT * FROM Products;
GO
-- create extended event (that is not doing what I want)
CREATE EVENT SESSION XE_TrackEmployeesAndProductsTables
ON SERVER
ADD EVENT sqlserver.sp_statement_completed
(
SET collect_statement=1
ACTION
(
sqlserver.database_name,
sqlserver.sql_text
)
WHERE
(
sqlserver.like_i_sql_unicode_string(sqlserver.sql_text,N'%Employees%')
OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text,N'%Products%')
)
);
ALTER EVENT SESSION XE_TrackEmployeesAndProductsTables ON SERVER STATE=START;
GO
-- run procs that I want to be picked up by my session (but aren't)
EXEC ListEmp;
EXEC ListProd;
-- cleanup
DROP EVENT SESSION XE_TrackEmployeesAndProductsTables ON SERVER;
DROP PROC ListEmp;
DROP PROC ListProd;
DROP TABLE Employees;
DROP TABLE Products;
I would just add this to the beginning of any stored proc you want to track:
declare #msg nvarchar(128) = concat('Stored proc ',OBJECT_NAME(##PROCID),' run.')
EXEC master..sp_trace_generateevent #event_class = 82, #userinfo = #msg;
Which you can track with an XEvent session like this:
CREATE EVENT SESSION [UserEvent] ON SERVER
ADD EVENT sqlserver.user_event
ADD TARGET package0.event_file(SET filename=N'UserEvent')
You can identify all the procedures that reference a table in static SQL like this:
select distinct object_name(d.object_id) referencing_object, SCHEMA_NAME(o.schema_id) referencing_object_schema
from sys.sql_dependencies d
join sys.objects o
on o.object_id = d.object_id
where d.referenced_major_id = object_id('Sales.SalesOrderHeader')
and o.type = 'P'

How to find the deadlock reason in sql server ro14?

Facing a deadlock in SQL server database and can see deadlock entry in SQL logs. How the log entries can be used to find the reason for this deadlock?
Deadlock information is captured by the system_health Extended Events trace by default. No need to turn on additional trace flags.
Information from the xml_deadlock event can be viewed from SSMS Object Explorer (Management-->Extended Events-->Sessions--system_health) or with T-SQL. Below is an example query to get the deadlock xml from the file target. You can also save the deadlock xml to a file with an xdl extension and open the file in SSMS for a graphical view of the deadlock.
--get xml_deadlock_report from system_health session file target
WITH
--get full path to current system_health trace file
CurrentSystemHealthTraceFile AS (
SELECT CAST(target_data AS xml).value('(/EventFileTarget/File/#name)[1]', 'varchar(255)') AS FileName
FROM sys.dm_xe_session_targets
WHERE
target_name = 'event_file'
AND CAST(target_data AS xml).value('(/EventFileTarget/File/#name)[1]', 'varchar(255)') LIKE '%\system[_]health%'
)
--get trace folder name and add base name of system_health trace file with wildcard
, BaseSystemHealthFileName AS (
SELECT
REVERSE(SUBSTRING(REVERSE(FileName), CHARINDEX(N'\', REVERSE(FileName)), 255)) + N'system_health*.xel' AS FileNamePattern
FROM CurrentSystemHealthTraceFile
)
--get xml_deadlock_report events from all system_health trace files
, DeadLockReports AS (
SELECT CAST(event_data AS xml) AS event_data
FROM BaseSystemHealthFileName
CROSS APPLY sys.fn_xe_file_target_read_file ( FileNamePattern, NULL, NULL, NULL) AS xed
WHERE xed.object_name like 'xml_deadlock_report'
)
--display 10 most recent deadlocks
SELECT TOP 10
DATEADD(hour, DATEDIFF(hour, SYSUTCDATETIME(), SYSDATETIME()), event_data.value('(/event/#timestamp)[1]', 'datetime2')) AS LocalTime
, event_data AS DeadlockReport
FROM DeadLockReports
ORDER BY LocalTime ASC;
Deadlock information can be captured in the SQL Server Error Log or by using Profiler / Server Side Trace.
1204 - this provides information about the nodes involved in the
deadlock
1222 - returns deadlock information in an XML format
You can turn on each of these separately or turn them on together.
To turn these on you can issue the following commands in a query window or you can add these as startup parameters. If these are turned on from a query window, the next time SQL Server starts these trace flags will not be active, so if you always want to capture this data the startup parameters is the best option.
DBCC TRACEON (1204, -1)
DBCC TRACEON (1222, -1)
Please refer to the following Links for more details
Redgate
MS SQL Tips
This code will display the error log which contains the query that creates the deadlock.
IF OBJECT_ID('tempdb.dbo.ErrorLog') IS Not Null
BEGIN
DROP TABLE tempdb.dbo.ErrorLog
END
CREATE TABLE tempdb.dbo.ErrorLog
(Id int IDENTITY (1, 1) NOT NULL,
logdate DATETIME, procInfo VARCHAR(10),
ERRORLOG VARCHAR(MAX))
-- insert the actual data from the Error log into our newly created table.
INSERT INTO tempdb.dbo.ErrorLog
EXEC master.dbo.sp_readerrorlog
declare #sql nvarchar(max)
set #sql='select logdate, procInfo, ERRORLOG from tempdb.dbo.ErrorLog
where Id >= (select top 1 id from tempdb.dbo.ErrorLog WHERE ERRORLOG Like
''%deadlock-list%'' order by id desc)'
select #SQL

Convert SQL Server trigger to Oracle and master.dbo.sysprocesses - not duplicate

I tried searching but could not find exactly what I'm looking for. I'm new to SQl Server and involved into SQL Server to Oracle conversion, and it is manual conversion. All I have is SQL Server files.
I see two types of SQL Server triggers - FOR UPDATE and FOR INSERT. They look to me as before update and before insert triggers in Oracle. I'd like to confirm this please and if you can provide examples that would be great.
Also, what is the equivalent to master.dbo.sysprocesses in Oracle please? Is this v$session? I can get user from dual in Oracle. Is this what nt_username is in below code?
Here is typical code examples I need to convert to Oracle - is this before insert?
CREATE TRIGGER trigger_name ON dbo.table_name
FOR Insert AS
declare #InsertUser varchar(32)
BEGIN
SELECT #InsertUser = nt_username from master.dbo.sysprocesses where spid = ##spid
Update table_name
SET dCreateDate = GETDATE(), cCreateUser = #InsertUser
FROM table1 a ,table2 i WHERE a.tab_id = i.tab_id
END
GO
Update Trigger - before update?
CREATE TRIGGER trigger_name ON dbo.table_name
FOR UPDATE AS
declare #UpdateUser varchar(32)
if not update(CreateUser) and not update(CreateDate)
BEGIN
SELECT #UpdateUser = nt_username from master.dbo.sysprocesses where spid = ##spid
Update table_name
SET UpdateDate = GETDATE(), UpdateUser = #UpdateUser
FROM table1 a ,table2 i WHERE a.tab_id = i.tab_id
END
GO
Should I combine these two into if inserting... elsif updating in Oracle?
Thank you very much to all.

sql server free tempdb

We are running an import stored procedure in SQL Server 2008R2-SP1 that loads thousand of rows into several tables at a time. We are having problems with tempDB and transaction log size.
Something like this:
CREATE PROCEDURE spReallyHugeImportDataProcedure
#id int
AS
BEGIN
CREATE TABLE #temp(...)
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
-- And so on....
END
We are trying to split the whole process and run several times for a small set of data.
Like this:
CREATE PROCEDURE spReallyHugeImportDataProcedure
#id int
AS
BEGIN
DECLARE #SomeSortOfCounter int = 100
WHILE(#SomeSortOfCounter <> 0)
BEGIN
-- TRY TO START A NEW TRANSACTION
BEGIN TRAN
CREATE TABLE #temp(...)
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
WHERE SomeFileterWorkinWithTheCounter = #SomeSortOfCounter
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
WHERE SomeFileterWorkinWithTheCounter = #SomeSortOfCounter
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
INSERT INTO #temp
SELECT *
FROM AlotOfJoins
WHERE SomeFileterWorkinWithTheCounter = #SomeSortOfCounter
INSERT INTO FinalTable
SELECT * FROM AlotOfJoins
DROP #tempTable
-- And so on....
-- TRY TO RELASE TEMP OBJECTS,
-- OR GIVE TO THE SERVER THE OPORTUNITY TO DO IT
COMMIT
SET #SomeSortOfCounter = #SomeSortOfCounter - 1
END
END
Is it possible for the SQL Server engine to work between those internal transactions?
Option 1: Using a table in a user database
If you really need to store the data in a temporary table, build that table in a user database (ImportTemp for example or in your destination DB) then use it instead of the tempdb. In this case, the SQL Server should not use as much space in TempDB and your data will be stored persistently -> you can reuse it and you can split your loader queries into multiple batches.
Optionally you can move this table to a different filegroup to prevent concurent writing and lower the chance of interferring with other processes.
In this case, the steps are:
Drop the 'temp' table if it exists
Create the 'temp' table (in a user database)
Fill the 'temp' table with the necessary data
Load the destination tables from the persisted 'temp' table
Drop the 'temp' table to free up space in data files
Optionally shrink the data and log files which are related to your 'temp' table
If you are using this import table relatively frequently, you can only truncate it before and after use instead of dropping and recreating it.
Option 2: Using an ETL tool
Use an ETL tool which can handle data in batches / buffers. If you are using SQL Server Standard or above you have the option to use SSIS (SQL Server Integration Services).
DBCC SHRINKFILE
You can release unused space from data and log files using the DBCC SHRINKFILE command:
USE [YourDatabase]
DBCC SHRINKFILE
(
{ file_name | file_id }
{ [ , EMPTYFILE ]
| [ [ , target_size ] [ , { NOTRUNCATE | TRUNCATEONLY } ] ]
}
)
[ WITH NO_INFOMSGS ]
Example
USE [tempdb]
DBCC SHRINKFILE (tempdb_data, TRUNCATEONLY)
Optionally
You can spread the TempDB files accross drives by adding additional data files to the TempDB:
ALTER DATABASE tempdb
ADD FILE (NAME = tempdev2, FILENAME = 'W:\tempdb2.mdf', SIZE = 256);
A related question: How to spread tempdb over multiple files?
simple answer is yes, as long as there is no outer transaction outside of the stored proc. also, there's no reason to add in explicit transactions. simply creating a loop and only working with a chunk of records per statement will let your tlog space get reused and you won't be forcing all of the records at one time into a temp table.

SQL Server track DDL CREATE USER

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

Resources