2 – The SQL Server landscape 35 WHEN SERVERPROPERTY('EditionID') = 4161255391 THEN 'Express Edition with Advanced Services' END AS ProductEdition, CASE WHEN CONVERT(CHAR(100), SERVERPROPERTY('IsClustered')) = 1 THEN 'Clustered' WHEN SERVERPROPERTY('IsClustered') = 0 THEN 'Not Clustered' WHEN SERVERPROPERTY('IsClustered') = NULL THEN 'Error' END AS IsClustered, CASE WHEN CONVERT(CHAR(100), SERVERPROPERTY('IsFullTextInstalled')) = 1 THEN 'Full-text is installed' WHEN SERVERPROPERTY('IsFullTextInstalled') = 0 THEN 'Full-text is not installed' WHEN SERVERPROPERTY('IsFullTextInstalled') = NULL THEN 'Error' END AS IsFullTextInstalled, CONVERT(CHAR(100), SERVERPROPERTY('SqlCharSet')) AS SqlCharSet, CONVERT(CHAR(100), SERVERPROPERTY('SqlCharSetName')) AS SqlCharSetName, CONVERT(CHAR(100), SERVERPROPERTY('SqlSortOrder')) AS SqlSortOrderID, CONVERT(CHAR(100), SERVERPROPERTY('SqlSortOrderName')) AS SqlSortOrderName ORDER BY CONVERT(CHAR(100), SERVERPROPERTY('Servername')) Listing 2.1: Server information. As you can see, it's a pretty simple script that makes liberal use of the SERVERPROPERTY function to return the required data. Figure 2.1: Collecting server information. 2 – The SQL Server landscape 36 NOTE All of the various properties of the SERVERPROPERTY function can be found in Books Online or MSDN, see http://msdn.microsoft.com/en- us/library/ms174396.aspx. If you were to run this query against one of your SQL Servers, you'd see results similar to those shown in Figure 2.1, all of which will be useful in your daily reporting of your infrastructure. One piece of information that this script does not return is the location of the server. There is no way to glean the location information from a query. Some things, at present, still have to be manually gathered. Database management It is obviously important that DBAs know what databases are on each of their servers. While DBAs may not be intimately familiar with every database schema on every SQL Server, it is essential that they are aware of the existence of every database, and at least understand the basic characteristics of each, such as what server they are on, what size they are and where on disk they are located. You can also gather the information you need to monitor the growth of the data and log files for each database, or answer questions such as "where are the system database files located?" This question brings up the interesting topic of implementing standards across all of your servers. Are the data files for each server stored on the correct, predetermined data drive? The log files on the correct log drive? Are naming conventions consistently enforced? Is each database using the correct default recovery model (e.g. SIMPLE) unless specified otherwise? You may find that the answer is, generally, "no". It is an unfortunate reality that, often, a DBA will inherit an infrastructure whereby a hodge-podge of different standards have been set and only erratically imposed by a variety of former DBAs. However, once you've got all of this data stored in a central repository, for every server, you can quickly report on how well your current standards have been enforced, and can start the job of pulling the "non-standard" ones into shape. And then, who knows, if you can stay in the position long enough, say ten years, you may actually get to see an infrastructure that properly adheres to all the standards you set forth. To gather this database management information, you will need to run the same two queries on each SQL 2000, 2005 and 2008 instance. The first of these queries is shown in Listing 2.2. It makes use of the sp_msforeachdb system stored procedure, which issues the same query for each database on a server, and saves 2 – The SQL Server landscape 37 you the time of writing your own cursor or set-based query to iterate through each database. I create a temp table, HoldforEachDB and then populate that table with the results from each database. In this way, I have one result set for all databases on the server, instead of individual result sets for each database, which would have otherwise been the case. Also, since I know that I will ultimately want to get this information from SSIS, and into a central DBA repository, having the temp table pre-defined is ideal. IF EXISTS ( SELECT * FROM tempdb.dbo.sysobjects WHERE id = OBJECT_ID(N'[tempdb].[dbo].[HoldforEachDB]') ) DROP TABLE [tempdb].[dbo].[HoldforEachDB] ; CREATE TABLE [tempdb].[dbo].[HoldforEachDB] ( [Server] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [DatabaseName] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [Size] [int] NOT NULL, [File_Status] [int] NULL, [Name] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [Filename] [nvarchar](260) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [Status] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [Updateability] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [User_Access] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [Recovery] [nvarchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ) ON [PRIMARY] INSERT INTO [tempdb].[dbo].[HoldforEachDB] EXEC sp_MSforeachdb 'SELECT CONVERT(char(100), SERVERPROPERTY(''Servername'')) AS Server, ''?'' as DatabaseName,[?] sysfiles.size, [?] sysfiles.status, [?] sysfiles.name, [?] sysfiles.filename,convert(sysname,DatabasePropertyEx(''?'' ,''Status'')) as Status, 2 – The SQL Server landscape 38 convert(sysname,DatabasePropertyEx(''?'',''Updateability'')) as Updateability, convert(sysname,DatabasePropertyEx(''?'',''UserAccess'')) as User_Access, convert(sysname,DatabasePropertyEx(''?'',''Recovery'')) as Recovery From [?] sysfiles ' Listing 2.2: Placing database information into a temporary table. The second query, shown in Listing 2.3, simply selects from the HoldforEachDB temporary table. SELECT [Server] ,[DatabaseName] ,[Size] ,[File_Status] ,[Name] ,[Filename] ,[Status] ,[Updateability] ,[User_Access] ,[Recovery] FROM [tempdb].[dbo].[HoldforEachDB] Listing 2.3: Selecting data from the temporary table, HoldForEachDB. The output of this query can be seen in Figure 2.2, which displays the server name, as well as the database name, size, filename and recovery model. Figure 2.2: Output of database info query. 2 – The SQL Server landscape 39 Database backups Having backup information is critical for the DBA, especially when working with a large infrastructure. Knowing where the full, differential or log backups are located is more than helpful; it is essential. This type of information can easily be gathered directly from the MSDB database, which has not changed substantially from SQL Server 2000 to 2008. Listing 2.4 shows the driving query for gathering from MSDB the vital database backup information that you need for each server, including information such as backup start date, end data and size. Notice, in the WHERE clause, that this query actually retrieves 30 days worth of history. SELECT CONVERT(char(100), SERVERPROPERTY('Servername')) AS Server, msdb.dbo.backupmediafamily.logical_device_name, msdb.dbo.backupmediafamily.physical_device_name, msdb.dbo.backupset.expiration_date, msdb.dbo.backupset.name, msdb.dbo.backupset.description, msdb.dbo.backupset.user_name, msdb.dbo.backupset.backup_start_date, msdb.dbo.backupset.backup_finish_date, CASE msdb backupset.type WHEN 'D' THEN 'Database' WHEN 'L' THEN 'Log' END AS backup_type, msdb.dbo.backupset.backup_size, msdb.dbo.backupset.database_name, msdb.dbo.backupset.server_name AS Source_Server FROM msdb.dbo.backupmediafamily INNER JOIN msdb.dbo.backupset ON msdb.dbo.backupmediafamily.media_set_id = msdb.dbo.backupset.media_set_id WHERE ( CONVERT(datetime, msdb.dbo.backupset.backup_start_date, 102) >= GETDATE() - 30 ) Listing 2.4: Query to gather database backup information. Figure 2.3 shows the output of this backup history query. Figure 2.3: Gathering database backup information. . CONVERT(CHAR(100), SERVERPROPERTY('SqlCharSetName')) AS SqlCharSetName, CONVERT(CHAR(100), SERVERPROPERTY('SqlSortOrder')) AS SqlSortOrderID, CONVERT(CHAR(100), SERVERPROPERTY('SqlSortOrderName')). CONVERT(CHAR(100), SERVERPROPERTY('SqlSortOrderName')) AS SqlSortOrderName ORDER BY CONVERT(CHAR(100), SERVERPROPERTY('Servername')) Listing 2.1: Server information. As you can. of the SERVERPROPERTY function to return the required data. Figure 2.1: Collecting server information. 2 – The SQL Server landscape 36 NOTE All of the various properties of the SERVERPROPERTY