SQL SERVER – SSMS: Schema Change History Report

The heat is picking up and I am glad you are liking this series going so far. This particular report is close to my heart and the most recommended. At my recent trip to Delhi and the user group out there, I had the opportunity to meet a number of young DBA’s who were getting into their professional career at various organizations. I always try to persuade such groups with interesting questions to make them inquisitive about learning new concepts with SQL Server.

At this user-group session I wanted people to answer a simple question:

  1. How can I know, who created/dropped/altered the database?
  2. How can I know, who created/dropped/altered the objects?

This caught the attention of this group and I got various answers from DDL Triggers, Auditing, Error Logs, Extended Events and many more innovative response which I will refrain from disclosing his because they were really funny. All these answers were correct in a way and I had to counter them with yet another question to make them thinking.

Though your answers are correct in a way, “what is the easiest / simplest way to find this without writing a single line of code”. Now the twist made the response into something simple. And one attendee had a response stating – “why not use Profiler?”

This response stumped me totally and I said, let me achieve the same with lesser number of clicks for you. And my idea was to show them the use of SQL Server Management Studio – Schema Change History. It has interesting dimensions to examine and let me take a moment to walk them through the same.

Where to start?

The report location can be found from Server node -> Right Click -> Reports -> Standard Reports -> “Schema Changes History”.

SQL SERVER - SSMS: Schema Change History Report schema-change-history-1

One of the important information worth noting here is, the report fetches information from the default trace. We have talked about default trace and how to enable it in our previous post on “Configuration Changes History” report.

If default trace is not enable then this report is smart enough to look at each database and find objects which were created or altered in last 7 days. I was not aware of this until I disable default trace to see the error in the report. To my surprise, the report still came up in some different format. Let us look at the outlook with these options.

With default trace enabled

The report when the default trace enabled is as shown below:

SQL SERVER - SSMS: Schema Change History Report schema-change-history-2

To catch the query that populates this report, I ran Profiler and here is the basic query:

SELECT FROM:: fn_trace_gettabl(e @base_tracefilename, default )
WHERE EventClass in (46,47,164) AND EventSubclass = 0 AND DatabaseID <> 2

To elaborate a bit, the EventClass 46, 47 and 164 corresponds to Object:Created, Object:Deleted and Object:Altered respectively (Refer sys.trace_events from MSDN for more info).

With default trace disabled

Now here is the twist, when Default Trace is disabled, the query which is executed in each database is shown below for reference:

SELECT o.name AS OBJECT_NAME,
o.type_desc,
o.create_date,
s.name AS schema_name
FROM sys.all_objects o
LEFT OUTER JOIN sys.schemas s
ON ( o.schema_id = s.schema_id)
WHERE create_date > ( GETDATE() - 7);

And below is the report generated by the query. It is evident from the last section of “Note” from the report that our default trace is not enabled.

SQL SERVER - SSMS: Schema Change History Report schema-change-history-3

There are two sections in the report. They are based on similar query which I pointed above with a minor difference of “create_date” and “modify_date” column as below.

SELECT o.name AS OBJECT_NAME,
o.type_desc,
o.create_date,
s.name AS schema_name
FROM sys.all_objects o
LEFT OUTER JOIN sys.schemas s
ON ( o.schema_id = s.schema_id)
WHERE modify_date > ( GETDATE() - 7);

The disadvantage of disabling default trace is that we would not be able to see any information if a database was dropped. I generally have seen this trace to be non-intrusive on most of the systems. But would love to hear from you and learn if you faced any problems with it.

Caveats with Schema Change History Report

One problem in the report is, even if one database is inaccessible, it would give error and fails to report anything for remaining databases. For illustration purposes, I made the database to norecovery state and I refreshed the report to get the below error:

SQL SERVER - SSMS: Schema Change History Report schema-change-history-4

If you ever have such situation, you can run the T-SQL query mentioned above manually on the database under question to view the changes.

Does anyone reading this post, ever disabled your Default Trace ever? Have you used this reports in your environment? Let me know your learnings.

Reference: Pinal Dave (https://blog.sqlauthority.com)

,
Previous Post
Developer’s Life – Every Developer is Like Transformers
Next Post
SQL SERVER – Three Top Efficiency Features of Management Tool for SQL Server

Related Posts

30 Comments. Leave new

  • I’m trying to run the SCH report and getting an error:
    “Trace file name ‘/var/opt/mssql/log/log_.trc\log.trc’ is invalid”

    Looking at the code extracted from profiler:
    set @base_tracefilename = LEFT( @curr_tracefilename,len(@curr_tracefilename) – @indx) + ‘\log.trc’;

    I see where the “\log.trc”, I see in the error I’m getting, is being appended to the file name.
    Is there a way to stop the appending behavior and only take the default log name for the report?

    When I manually set the default log file name in the query, the query works:
    select
    ObjectName
    , CASE EventClass WHEN 46 THEN ‘CREATE’ WHEN 47 THEN ‘DROP’ WHEN 164 THEN ‘ALTER’ END DDLOperation
    , ObjectID
    , DatabaseName
    , StartTime
    , EventClass
    , EventSubClass
    , ObjectType
    , ServerName
    , LoginName
    , NTUserName
    , ApplicationName
    FROM ::fn_trace_gettable(‘/var/opt/mssql/log/log.trc’, default )
    where EventClass in (46,47,164) and EventSubclass = 0 and DatabaseID = db_id();

    Reply

Leave a Reply

Menu