Monday, 6 February 2012

A Script A Day - Day 6 - Drop and Create Database Snapshots

Today's Script will drop all database snapshots and create a database snapshot for all online read writeable user databases. I create this script for use in a database mirroring partnership so that the snapshots could be used on the mirroring partner so to impact the mirroring principal less. As such I set the mirroring partnership servers at the beginning of the script because the mirroring partners databases are inaccessible so I have to retrieve the file information from the mirroring principal. This can be changed to run on servers not in a mirroring partnership.

Other than that the only other thing to update is the @SnapshotDirectory variable to the path where you want the snapshots to exist. Each snapshot has a prefix of 'snap_' and a suffix of the time in the format of '_hh00' this was because the snapshots where created on an hourly basis.

/*
      -----------------------------------------------------------------
      Drop And Create Database Snapshots
      -----------------------------------------------------------------
     
      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -----------------------------------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -----------------------------------------------------------------
*/

 
/*  
* Name:   spDropAndCreateDatabaseSnapshots 
* Description: This procedure drops all database snapshots and creates a databsae snapshot of 
*     all online read / writable user databases 
* Returns:   
* Source Control:  
* Execution:  EXEC dbo.spDropAndCreateDatabaseSnapshots     
* VERSION CHANGES 
* Release Initials CostBefore CostAfter 
* v1.0   CMc   N/A   0.0033275 
*/ 
DROP PROCEDURE [dbo].[spDropAndCreateDatabaseSnapshots]  ;
GO
CREATE PROCEDURE [dbo].[spDropAndCreateDatabaseSnapshots] 
AS  
BEGIN 
 SET NOCOUNT ON 
    -- Declare Variables 
    DECLARE @MinDBID INT 
    DECLARE @MaxDBID INT 
    DECLARE @DatabaseName VARCHAR(100) 
    DECLARE @SQL VARCHAR(6000) 
    DECLARE @SQL1 VARCHAR(2000) 
    DECLARE @SQL2 VARCHAR(2000) 
    DECLARE @SQL3 VARCHAR(2000) 
    DECLARE @SQL4 VARCHAR(2000) 
    DECLARE @SnapshotPrefix VARCHAR(5) 
    DECLARE @SnapshotName VARCHAR(200) 
    DECLARE @SnapshotSeperator VARCHAR(1) 
    DECLARE @SnapshotHour VARCHAR(3) 
    DECLARE @SnapshotMin VARCHAR(2) 
    DECLARE @SnapshotExtension VARCHAR(5) 
    DECLARE @SnapshotDirectory VARCHAR(100) 
    DECLARE @MinFileID INT 
    DECLARE @MaxFileID INT 
    DECLARE @DBSnapshotName VARCHAR(100) 
    DECLARE @MinSnapshotID INT 
    DECLARE @MaxSnapshotID INT 
    DECLARE @ServerName VARCHAR(15) 
     
    -- Set Variables 
    SET @SnapshotDirectory = 'D:\Snapshot\' 
    SET @SnapshotPrefix = 'Snap_' 
    SET @SnapshotSeperator = '_' 
    SELECT  @SnapshotHour = DATEPART(hh, GETDATE()) 
     
    -- Set the servername for the mirroring partner to pick up file names for each database 
    IF @@SERVERNAME = 'PARTNERSERVER' 
  SET @ServerName = 'PRINCIPLESERVER' 
    IF @@SERVERNAME = 'PRINCIPLESERVER' 
  SET @ServerName = 'PARTNERSERVER' 
 
    -- If time is before 10am then add a leading 0 for consistancy 
    IF LEN(@SnapshotHour) < 2  
        SET @SnapshotHour = '0' + @SnapshotHour 
 
    SET @SnapshotHour = @SnapshotSeperator + @SnapshotHour  
    SET @SnapshotMin = '00' 
    SET @SnapshotExtension = '.snap' 
 
      -- Check for temporary tableS and drop it if it exists 
    IF OBJECT_ID('tempDB.dbo.#Database') IS NOT NULL  
        DROP TABLE [#Database] ; 
    IF OBJECT_ID('tempDB.dbo.#SQL2') IS NOT NULL  
        DROP TABLE #SQL2 ; 
    IF OBJECT_ID('tempDB.dbo.#Snapshot') IS NOT NULL  
        DROP TABLE #Snapshot ; 
         
      -- Create temporary tables 
    CREATE TABLE #Database 
        ( 
          ID INT IDENTITY(1, 1), 
          DatabaseName VARCHAR(100) 
        ) 
    CREATE TABLE #SQL2 
        ( 
          ID INT IDENTITY(1, 1), 
          SQL2 VARCHAR(2000) 
        ) 
    CREATE TABLE #Snapshot 
        ( 
          ID INT IDENTITY(1, 1), 
          SnapshotName VARCHAR(2000) 
        ) 
 
      -- Check for existing database snapshots and delete them 
    IF EXISTS ( SELECT  name 
                FROM    sys.databases 
                WHERE   --snapshot_isolation_state = 1 
                        --AND  
                        name NOT IN ( 'master', 'model', 'msdb', 'tempdb', 
                                          'distribution' ) 
                        AND LEFT(name, 5) = 'Snap_' )  
        BEGIN 
                  -- Insert all database snapshot names into a temporary table 
            INSERT  INTO #Snapshot ( SnapshotName ) 
                    SELECT  name 
                    FROM    sys.databases 
                    WHERE  --snapshot_isolation_state = 1 
                           --AND  
                            name NOT IN ( 'master', 'model', 'msdb', 
                                              'tempdb', 'distribution' ) 
                            AND LEFT(name, 5) = 'Snap_' 
                 
                  -- Set Variables for the drop snapshot loop              
            SELECT  @MinSnapshotID = MIN(ID), 
                    @MaxSnapshotID = MAX(ID) 
            FROM    #Snapshot 
 
                  -- Begin loop to drop snapshots 
            WHILE @MinSnapshotID <= @MaxSnapshotID 
                BEGIN 
                              -- Get SnapshotName 
                    SELECT  @DBSnapshotName = SnapshotName 
                              FROM    #Snapshot 
                    WHERE   ID = @MinSnapshotID 
             
                              -- Build DROP DATABASE COMMAND 
                    SET @SQL = 'DROP DATABASE ' + @DBSnapshotName + ';' 
   
                              -- Try Catch block to execute SQL and handle errors    
                    BEGIN TRY 
                                    -- Drop Database Snapshots  
                        EXEC ( @SQL 
                            ) 
                    END TRY 
                    BEGIN CATCH 
                        SELECT  @DatabaseName,  
                                                message_id, 
                                severity, 
                                [text], 
                                @SQL 
                        FROM    sys.messages 
                        WHERE   message_id = @@ERROR 
                                AND language_id = 1033 -- British English 
                    END CATCH 
   
                              -- Get the next SnapshotName ID 
                    SET @MinSnapshotID = @MinSnapshotID + 1   
                        -- End Loop 
                END 
        END
        
      -- Create Database Snapshots for all Online Read/Writable databases 
    IF EXISTS ( SELECT  name 
                FROM    sys.databases 
                WHERE   name NOT IN ( 'master', 'model', 'msdb', 'tempdb', 
                                      'distribution', 'reports', 
                                      'reportserver', 'reportservertempdb' ) 
                        AND DATABASEPROPERTYEX(name, 'Updateability') = 'READ_WRITE' 
                        AND DATABASEPROPERTYEX(name, 'Status') = 'ONLINE'  
                        )  
        BEGIN 
                  -- Insert Online, Read/Writable database names into temporary table 
            INSERT  INTO #Database ( DatabaseName ) 
                    SELECT  name 
                    FROM    sys.databases 
                    WHERE   name NOT IN ( 'master', 'model', 'msdb', 'tempdb', 
                                          'distribution', 'reports', 
                                          'reportserver', 'reportservertempdb' ) 
                            AND DATABASEPROPERTYEX(name, 'Updateability') = 'READ_WRITE' 
                            AND DATABASEPROPERTYEX(name, 'Status') = 'ONLINE' 
 
            SELECT  @MinDBID = MIN(ID), 
                    @MaxDBID = MAX(ID) 
            FROM    #Database 
 
                  -- Begin Loop 
            WHILE @MinDBID <= @MaxDBID 
                BEGIN 
                              -- Get DatabaseName 
                    SELECT  @DatabaseName = DatabaseName 
                    FROM    #Database 
                    WHERE   ID = @MinDBID 
   
                              -- Build up snapshot string 
                    SET @SnapshotName = @SnapshotPrefix + @DatabaseName 
                        + @SnapshotHour + @SnapshotMin 
         
                              -- Create Start of SQL command to be run 
                    SET @SQL1 = 'USE master; 
                              CREATE DATABASE ' + @SnapshotName + ' 
                              ON ' 
      
                              -- Remove records from table ready for next database 
                    TRUNCATE TABLE #SQL2 
 
                              -- Build command to Insert files into temp table 
                    SET @SQL2 = 'INSERT  #SQL2 
                              SELECT  ''( NAME = '' ' 
                        + '+ '''' + name + '', FILENAME =  ''''' + @SnapshotDirectory + ''' + name + ''' 
                        + @SnapshotHour + @SnapshotMin + '.snap'''')'' 
                              FROM ' + @ServerName + '.' + @DatabaseName + '.sys.database_files 
                              WHERE   type = 0 
                              AND state = 0' 
                 
                              --print @SQL2 
 
                              -- Try Catch block to execute SQL and handle errors    
                    BEGIN TRY 
                                    -- Insert files into tmp table  
                        EXEC ( @SQL2 
                            ) 
                    END TRY 
                    BEGIN CATCH 
             
                        SELECT  @DatabaseName,  
                                                message_id, 
                                severity, 
                                [text], 
                                @SQL 
                        FROM    sys.messages 
                                    WHERE   message_id = @@ERROR 
                        AND language_id = 1033 -- British English 
                    END CATCH 
         
                              -- Set Variables for the append , loop              
                    SELECT  @MinFileID = MIN(ID), 
                            @MaxFileID = MAX(ID) 
                    FROM    #SQL2 
 
                              -- Begin Loop to append , to the end of all except the last record 
                    WHILE @MinFileID < @MaxFileID 
                        BEGIN 
                                          -- Append , to the end of the current record 
                            UPDATE  #SQL2 
                            SET     SQL2 = SQL2 + ',' 
                            WHERE   ID = @MinFileID 
   
                                          -- Get the next DatabaseName ID 
                            SET @MinFileID = @MinFileID + 1   
                                    -- End Loop 
                        END 
 
                    SELECT  @MinFileID = MIN(ID), 
                            @MaxFileID = MAX(ID) 
                    FROM    #SQL2 
 
                    SET @SQL3 = '' 
   
                              -- Begin Loop to concatenante all files 
                    WHILE @MinFileID <= @MaxFileID 
                        BEGIN 
                                          -- Append , to the end of the current record 
                            SELECT  @SQL2 = SQL2 
                            FROM    #SQL2 
                            WHERE   ID = @MinFileID 
     
                            SET @SQL3 = @SQL3 + @SQL2 
   
                                          -- Get the next DatabaseName ID 
                            SET @MinFileID = @MinFileID + 1   
                 
                                    -- End Loop 
                        END 
 
                              -- Create End of SQL command to be run      
                    SET @SQL4 = 'AS SNAPSHOT OF ' + @DatabaseName + ';' 
   
                              -- Concatenate SQL variables ready for execution 
                    SET @SQL = @SQL1 + @SQL3 + @SQL4 
   
                              -- Try Catch block to execute SQL and handle errors    
                    BEGIN TRY 
                                    -- Create Database Snapshots 
                        EXEC ( @SQL 
                            ) 
                    END TRY 
                    BEGIN CATCH 
             
                        SELECT  @DatabaseName, 
                                                message_id, 
                                severity, 
                                [text], 
                                @SQL 
                        FROM    sys.messages 
                        WHERE   message_id = @@ERROR 
                                AND language_id = 1033 -- British English 
                    END CATCH 
         
                              -- Get the next DatabaseName ID 
                    SET @MinDBID = @MinDBID + 1 
                        -- End Loop 
                END 
        END 
END 
 
Enjoy!

Chris

Sunday, 5 February 2012

A Script A Day - Day 5 - Database Owner Permissions

Today's script will list all principals with membership in the db_owner fixed database role.


/*
      -----------------------------------------------------------------
      Database Owner Permissions
      -----------------------------------------------------------------
     
      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -----------------------------------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -----------------------------------------------------------------
*/
IF OBJECT_ID('tempdb..#tmp') IS NULL
    CREATE TABLE #tmp
        (
          Principal VARCHAR(250),
          DatabaseName VARCHAR(250)
        );
GO   
EXEC sp_Msforeachdb 'use [?];
INSERT #tmp SELECT  u.name AS Principal, db_name() AS DatabaseName
FROM sys.database_role_members drm
INNER JOIN sys.database_principals dp ON dp.principal_id = drm.role_principal_id
INNER JOIN sysusers u ON u.uid = drm.member_principal_id
WHERE dp.name = ''db_owner''
AND dp.name <> ''dbo'' AND u.isntuser = 0';
GO
SELECT  Principal,
        DatabaseName
FROM    #tmp
WHERE Principal != 'dbo';
GO
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
    DROP TABLE #tmp;
GO


Enjoy!


Chris

Saturday, 4 February 2012

A Script A Day - Day 4 - Estimated Time Left

Today's script allows you to keep track of how long left a statement has before it completes.  I find this useful for example when restoring databases.  All you need to do is simply change the der.command  = as required.



/*
      -----------------------------------------------------------------
      Estimated Time to Complete
      -----------------------------------------------------------------
     
      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -----------------------------------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -----------------------------------------------------------------
*/

SELECT
      DB_NAME(database_id) AS DatabaseName,
      der.session_id,
      der.command,
      der.[status],
      p.lastwaittype,
      p.waitresource,
      der.percent_complete,
      der.estimated_completion_time,
      CONVERT(VARCHAR(10),
      DATEADD(MS, der.estimated_completion_time, 0),8) AS EstimatedTimeLeft
FROM
      sys.dm_exec_requests AS der WITH(NOLOCK)
      INNER JOIN sys.sysprocesses AS p WITH(NOLOCK) ON p.spid = der.session_ID
WHERE
      der.command = 'RESTORE DATABASE'


Enjoy!


Chris

Friday, 3 February 2012

A Script A Day - Day 3 - Live Change Data Edit Template

Today's script is a template I use for the occasions when a data edit is required to be run against a live environment.  It has 8 steps to give you as DBA's control over what is being run and the safety of recording results before and after the modifications.  The comments section at the top is used to give you an instant view of who, what and where about the script.


/*
      -----------------------------------------------------------------
      Live Change Data Edit Script Template - Results to Text (Ctrl+T)
      -----------------------------------------------------------------
     
      Author:                                        
      Date:                              
      Code Reviewed By:            
      Test Environment:            
      Required Environment:        
      Description:                       
     
      -----------------------------------------------------------------

      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -----------------------------------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -----------------------------------------------------------------
*/

-- Step 1
-- Set database context
USE ;
GO

-- Step 2
-- Run select statement(s) before data modifications for historical record


-- Step 3
-- Record results before data modifications
/*

*/

-- Step 4
-- Start of data modifications - Use BEGIN TRANSACTION to allow rollback if errors or unexpected results occur
PRINT '*** START DATA MODIFICATIONS ***'

BEGIN TRANSACTION

-- Step 5
-- End of data modifications
PRINT '*** END OF DATA MODIFICATIONS'

-- Step 6
-- COMMIT OR ROLLBACK? - FOR DBA USE WHEN BEING EXECUTED LEAVE COMMENTED OUT!

-- COMMIT TRANSACTION
-- ROLLBACK TRANSACTION

-- Step 7
-- Run select statement(s) after data modifications


-- Step 8
-- Record results after data modifications for historical record
/*

*/


Enjoy!


Chris

Thursday, 2 February 2012

A Script A Day - Day 2 - Database Mail Troubleshooting

Today's script is a collection of simple queries I have saved for a time when I need to troubleshoot database mail problems.



/*
      -------------------------------------
      Summary:                      Database Mail Troubleshooting
      SQL Server Versions:          2005 onwards
      Written by:                   Chris McGowan
      -------------------------------------
     
      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -------------------------------------
*/
-- Set database context
USE msdb
GO

-- Declare and set @dteDate variable
DECLARE @dteDate DATETIME
SET @dteDate = '20100714'

-- Check the event log records
SELECT *
FROM msdb.dbo.sysmail_event_log
WHERE log_date > @dteDate;

-- Check if mail is being sent
SELECT *
FROM msdb.dbo.sysmail_allitems
WHERE send_request_date > @dteDate

-- Check the mail queue state
EXEC msdb.dbo.sysmail_help_queue_sp @queue_type = 'Mail' ;
GO

-- Check if the service broker is enabled
SELECT is_broker_enabled
FROM sys.databases
WHERE name = 'msdb';
GO

-- Start database mail
EXECUTE msdb.dbo.sysmail_start_sp;
GO

-- Check the members of the DatabaseMailUserRole role
EXEC msdb.sys.sp_helprolemember 'DatabaseMailUserRole';
GO

-- Check associations between Database Mail profiles and database principals
EXEC msdb.dbo.sysmail_help_principalprofile_sp;
GO

-- Check which accounts are sending mail
SELECT sent_account_id, sent_date
FROM msdb.dbo.sysmail_sentitems;
GO

-- Enable database mail
EXEC sp_configure 'Database Mail XPs', 1
GO
-- Caution causes the buffer cache to be flushed!!!
RECONFIGURE WITH OVERRIDE
GO



Enjoy!


Chris

Wednesday, 1 February 2012

A Script A Day - Day 1 - Database File To Volume Mapping

Today is the 1st February 2012 and as promised here is the first of script in my "A Script A Day" series.  There will be 29 scripts (yes this year is a leap year:) which I will also roll-up into a single document and make available to you some time in March.


I have used the below script many times and used as a starting point for other more specific scenarios like getting information about new servers / databases, auditing and resource allocation.  The script maps database files to logical volumes and includes some additional file information for good measure.


/*
      ----------------------------------------------------------------- 
      Summary:                Server volume to database file mapping
      SQL Server Versions:    2005 onwards
      Written by:             Chris McGowan
      -----------------------------------------------------------------

      For more SQL resources, check out SQLServer365.blogspot.co.uk

      -----------------------------------------------------------------

      You may alter this code for your own purposes.
      You may republish altered code as long as you give due credit.
      You must obtain prior permission before blogging this code.
 
      THIS CODE AND INFORMATION ARE PROVIDED "AS IS"
     
      -----------------------------------------------------------------
*/
IF OBJECT_ID('tempdb..#DBFile') IS NULL
    CREATE TABLE #DBFile
        (
          [LogicalName] VARCHAR(200),
          [FileID] TINYINT,
          [FileName] VARCHAR(1000),
          [FileGroup] VARCHAR(100),
          [Size] VARCHAR(100),
          [MaxSize] VARCHAR(100),
          [Growth] VARCHAR(100),
          [Usage] VARCHAR(100)
        )
GO
IF OBJECT_ID('tempdb..#DBFile2') IS NULL
    CREATE TABLE #DBFile2
        (
          [VolumeLetter] CHAR(3),
          [LogicalFileName] VARCHAR(200),
          [FileID] TINYINT,
          [PhysicalFileName] VARCHAR(1000),
          [FileGroup] VARCHAR(100),
          [Size] VARCHAR(100),
          [MaxSize] VARCHAR(100),
          [Growth] VARCHAR(100),
          [Usage] VARCHAR(100)
        )
GO      
EXEC sp_Msforeachdb 'use [?];INSERT INTO #DBFile EXEC sp_helpfile'
GO
INSERT  INTO #DBFile2
        SELECT  LEFT([FileName], 3),
                [LogicalName],
                [FileID],
                [FileName],
                [FileGroup],
                [Size],
                [MaxSize],
                [Growth],
                [Usage]
        FROM    #DBFile
GO
SELECT  [VolumeLetter],
        [LogicalFileName],
        [PhysicalFileName],
        [FileID],
        [FileGroup],
        [Size],
        [MaxSize],
        [Growth],
        [Usage]
FROM    #DBFile2
ORDER BY [VolumeLetter] ASC
GO
IF OBJECT_ID('tempdb..#DBFile') IS NOT NULL
    DROP TABLE #DBFile
IF OBJECT_ID('tempdb..#DBFile2') IS NOT NULL
    DROP TABLE #DBFile2

Enjoy

Chris