2009-09-26

Database version

When the daily administration of several database instances is done using a standard database, it is rather nice to be able to check that all management database have the same structure.
A quick solution is to calculate a checksum of the database objects and its columns.

To hold the version number of the database and its checksum a (singleton) table is created.
CREATE TABLE [dbo].[Database_Version]
(
    [Database_Version_No] nvarchar(128) NOT NULL,
    [Database_Version_Checksum] bigint NOT NULL,
    CONSTRAINT PK_Database_Version PRIMARY KEY ([Database_Version_No])
);
The table can be initialized by a INSERT statement:
INSERT INTO [dbo].[Database_Version] ([Database_Version_No], [Database_Version_Checksum])
VALUES ('0', 42);

The table is used by two stored procedures - one to read:
CREATE PROCEDURE [dbo].[Get_Database_Version]
AS
BEGIN
    SET NOCOUNT ON;
   
    -- Check stored checksum
    --   Calculate checksum
    DECLARE @objects_checksum bigint;
    SELECT @objects_checksum = SUM(CAST(CHECKSUM(OBJECT_DEFINITION([object_id])) AS bigint))
    FROM sys.objects
    WHERE is_ms_shipped = 0;
    DECLARE @columns_checksum bigint;
    SELECT @columns_checksum = SUM(CAST(CHECKSUM(OBJECT_DEFINITION([column_id])) AS bigint))
    FROM sys.columns;
    --   Check is the checksum stored is equal to the calculated checksum
    IF (@objects_checksum + @columns_checksum) <> (
        SELECT [Database_Version_Checksum]
        FROM [dbo].[Database_Version]
    )
    BEGIN
        DECLARE @calculated_checksum nvarchar(128) = CAST((@objects_checksum + @columns_checksum) AS nvarchar(128));
        DECLARE @stored_checksum nvarchar(128) = CAST((SELECT [Database_Version_Checksum] FROM [dbo].[Database_Version]) AS nvarchar(128));
        RAISERROR(N'The calculated checksum (%s) is not equal to the stored checksum (%s).',10, 1,@calculated_checksum,@stored_checksum) WITH NOWAIT;
    END
   
    -- Get stored checksum
    SELECT [Database_Version_No], [Database_Version_Checksum]
    FROM [dbo].[Database_Version];
END
...and one to update:
CREATE PROCEDURE [dbo].[Set_Database_Version]
    @Database_Version_No nvarchar(128)
AS
BEGIN
    SET NOCOUNT ON;
   
    -- Check if the given Database Version value is different from the existing
    IF @Database_Version_No = (
        SELECT [Database_Version_No]
        FROM [dbo].[Database_Version]
    )
    RAISERROR('You have to update the Database Version to another value than the existing one.', 18, 0) WITH NOWAIT;

    -- Calculate checksum
    DECLARE @objects_checksum bigint;
    SELECT @objects_checksum = SUM(CAST(CHECKSUM(OBJECT_DEFINITION([object_id])) AS bigint))
    FROM sys.objects
    WHERE is_ms_shipped = 0;
    DECLARE @columns_checksum bigint;
    SELECT @columns_checksum = SUM(CAST(CHECKSUM(OBJECT_DEFINITION([column_id])) AS bigint))
    FROM sys.columns;
   
    -- Check is the checksum stored is equal to the calculated checksum
    IF (@objects_checksum + @columns_checksum) = (
        SELECT [Database_Version_Checksum]
        FROM [dbo].[Database_Version]
    )
    RAISERROR('The calculated checksum is equal to the stored checksum.',18, 1) WITH LOG;
   
    -- @objects_checksum + @columns_checksum
    UPDATE [dbo].[Database_Version]
    SET [dbo].[Database_Version].[Database_Version_No] = @Database_Version_No,
        [dbo].[Database_Version].Database_Version_Checksum = @objects_checksum + @columns_checksum;
END


The get procedure also checks if the stored checksum is like to actual calculated checksum. Of course the checksum inserted above is not correct, so the get procedure raise an error:
EXEC [dbo].[Get_Database_Version];
{Message} The calculated checksum (3381089942582) is not equal to the stored checksum (42).

The update procedure calculate a checksum and update the stored checksum. The version number is actually just a string, so you can make your own version standard.
EXEC [dbo].[Set_Database_Version] @Database_Version_No = '3.1';
The get procedure now executes with success.
Database_Version_No   Database_Version_Checksum
-------------------   -------------------------
3.1                   3381089942582

If a object is changes, e.g. a column is added
ALTER TABLE [dbo].[Database_Version] ADD
    [Database_Version_Description] nvarchar(MAX) NULL;

Now the get procedure raises a error
The calculated checksum (3383237426229) is not equal to the stored checksum (3381089942582).
If the column is dropped,
ALTER TABLE [dbo].[Database_Version]
    DROP COLUMN [Database_Version_Description];

the get procedure executes with success.

Discussion
The database version objects are in the schema "dbo", but you might want to keep this schema clean.
When the get procedure fails, you have no indication on what is different. You could use a comparison tool like Red Gate SQL Compare.
There is no timestamp in the solution, but you could format the version as a timestamp, e.g. "2009-09-17A".
The version checksum calculation is somewhat simple. It could also include indexes, users, files and what else you need.
A version and checksum could be calculated and stored for each database. Some changes to the version objects are needed, but it should not be a problem to the dedicated DBA.

2009-08-18

msdb.dbo.backupset

This morning I wanted to have a quick look at the backup history of a given database. This I got by this statement

SELECT [backup_start_date] AS 'Start'
,([backup_finish_date]-[backup_start_date]) AS 'Duration'
,(([backup_size]/1024)/1024)/1024 AS 'Size [GB]'
FROM [msdb].[dbo].[backupset]
WHERE [database_name]='my_database' AND [type]='D'
ORDER BY [Start] DESC;


The multiple division by 1024 is to get a clear number.

2009-08-08

UNMOUNTABLE_BOOT_VOLUME

A friend of mine have an (very) old notebook running Windows XP, and it came up with this awful message in a Blue Screen:
UNMOUNTABLE_BOOT_VOLUME

The fix was
  1. Boot the machine on a installation set (Windows XP CD-ROM)
  2. Go to the Recovery Console
  3. Run the command "chkdsk c: /r"
The contents of the disk is rescued, but the machine is still due to a replacement...

2009-06-16

Central Registrered Servers

With SQL Server 2008 came the feature "Central Management Servers". This is poorly documented by Microsoft, but the naming of the database objects are somewhat meaningful. In the system database [msdb] there are two tables for storing the server registrations. Only the tables in msdb at the Central Management Server are used. The two tables are
  • msdb.dbo.sysmanagement_shared_registered_servers_internal
  • msdb.dbo.sysmanagement_shared_server_groups_internal

There is no use in accessing these two tables as we have these two views for the data

  • msdb.dbo.sysmanagement_shared_registered_servers
  • msdb.dbo.sysmanagement_shared_server_groups

The first view sysmanagement_shared_registered_servers shows the registered servers by server_id : int server_group_id : int name : sysname server_name : sysname description : nvarchar(2048) server_type : int

The second view sysmanagement_shared_server_groups shows the registered server groups and their hieracy by server_group_in : int NOT NULL name : sysname NOT NULL description : nvarchar(2048) NOT NULL server_type : int NOT NULL parent_id : int NULL is_system_object : bit NULL num_server_group_children : int NULL num_registered_server_children : int NULL

I found the view sysmanagement_shared_registered_servers a little technical, but this query gives me a quick glance at the central registered servers

SELECT [server_id] ,[server_groups].[name] AS 'server_group_name' ,[server_name] ,[shared_servers].[description] AS 'server_description' ,( SELECT [name] FROM msdb.dbo.sysmanagement_shared_server_groups AS [shared_groups] WHERE shared_groups.is_system_object = 1 AND shared_servers.server_type = shared_groups.server_type) AS 'server_type_name' FROM msdb.dbo.sysmanagement_shared_registered_servers AS [shared_servers] INNER JOIN msdb.dbo.sysmanagement_shared_server_groups AS [server_groups] ON shared_servers.server_group_id = server_groups.server_group_id;

It looks like the data are handled by 11 stored procedures. I have tried them a little, and you can see my notes and examples in Github > SQLAdmin > Central Management Server.

  • msdb.dbo.sp_sysmanagement_verify_shared_server_type @server_type : int
  • msdb.dbo.sp_sysmanagement_add_shared_server_group @name : sysname @description nvarchar(2048) = N'' @parent_id : int @server_type : int @server_group_id : int OUTPUT
  • msdb.dbo.sp_sysmanagement_add_shared_registered_server @name : sysname @server_group_id : int @server_name : sysname @description : nvarchar(2048) = N'' @server_type : int @server_id : int OUTPUT
  • msdb.dbo.sp_sysmanagement_delete_shared_server_group @server_group_id : int
  • msdb.dbo.sp_sysmanagement_delete_shared_registered_server @server_id : int
  • msdb.dbo.sp_sysmanagement_move_shared_server_group @server_group_id : int @new_parent_id : int
  • msdb.dbo.sp_sysmanagement_move_shared_registered_server @server_id : int @new_parent_id : int
  • msdb.dbo.sp_sysmanagement_update_shared_server_group @server_group_id : int @description : nvarchar(2048) = NULL
  • msdb.dbo.sp_sysmanagement_update_shared_registered_server @server_id : int @server_name : sysname = NULL @description : nvarchar(2048) = NULL
  • msdb.dbo.sp_sysmanagement_rename_shared_server_group @server_group_id : int @new_name : sysname
  • msdb.dbo.sp_sysmanagement_rename_shared_registered_server @server_id : int @new_name : sysname

Also it loks like Central Management Servers can be handled through SQL Server Management Objects (SMO). I found seven classes in the namespace Microsoft.SqlServer.Management.Smo.RegisteredServers by the .NET component Microsoft.SqlServer.SmoExtended implemented in the assembly Microsoft.SqlServer.SmoExtended.dll. The classes are

  • RegisteredServer
  • RegisteredServerCollection
  • RegSvrCollectionBase
  • RegSvrSmoObject
  • ServerGroup
  • ServerGroupBase
  • ServerGroupCollection

Looking at the classes using Visual Studio Object Browser didn't give me that much - only I noticed that we once again have to fight with the .NET component Microsoft.SqlServer.SmoExtended :-(

About security suddenly Books Online is useful:

How to: Create a Central Management Server and Server Group

It looks like you don't have to be sysadmin to be able to use Central Management Servers. :-)

The above is the result of a quick browse in the system objects. When I have something usefull, it will surface here...

History

2021-11-04  Link to Github notes and examples added.

2009-06-16  Post created

2009-06-03

PowerShell V2 differences

This blogentry describes very nice where PowerShell has evolved from CTP3 to RC:
PowerShell v2.0 – Differences Between CTP3/Win7Beta and Win7RC
Many thanks to Oisin Grehan!