How SQL Server Page Allocations Works

How SQL Server Page Allocations Works?

This post helps us in understanding “How SQL Server Page Allocations Works”. When I drop an object SQL Server does not immediately re-use those extents for a new object. This is because the storage engine is optimized for speed to continue allocating new extents from available free space in the file rather than always going back in to routines to identify and reclaim previously dropped extents.

 The storage engine maintains a pointer in the FCB for each file to point to the next available (uniform or mixed extent).  It continues to move this pointer along the file as objects and extents are allocated.  The storage engine does not constantly evaluate to move this pointer back to previously dropped extents  until it reaches the end of the file and is faced with an auto-grow or out of space.  Then routines will kick in to aggressively start going back through the file to reclaim available extents.  The reason why the storage engine does the allocations this way is primarily for speed and performance.  If the storage engine was always trying to go back through the file to look for previously available extents then allocation of new extents would be much slower and fragmented rather than continuing along the available space in the file.  The storage engine will only get aggressive to reclaim space when it has no other alternative and space at the end of the file is not sufficient and then go back through to see where it can find space to handle the new allocations.

 As an example, say my database file looks like the following with free available extents at the end of the file and my FCB points to extent 101 as the next available uniform extent: Now we will see how SQL Server Page Allocations Works.

How SQL Server Page Allocations Works

I then create a new table and insert some data.  The storage engine allocated extents 101-103 for my new data.  The FCB pointer was moved along and now points to extent 104 as the next available uniform extent:

How SQL Server Page Allocations Works

 Then I do what I needed to do with that object data and decide to drop the object.  Extents 101-103 are no longer in use (there are other cleanup routines that go through and mark extents as available).  Note that the FCB pointer continues to point to extent 104 as the next available uniform extent:

How SQL Server Page Allocations Works

I then decide to create another table and insert some data.  The storage engine looks at the pointer for the next uniform extent and starts allocation for my new object at extent 104.  Extents 105 and 105 are allocated for my new object and the FCB next available uniform extent pointer is moved along to 106:

How SQL Server Page Allocations Works

Posted in SQL Development, SQL Server DBA | Tagged , , , , | Leave a comment

Ctrl N short cut is not working in SQL 2008

Ctrl N short cut is not working in SQL 2008

SQL Server 2008 got released and there are lot of new features that takes SQL Server to the next level for sure. This post helps sql server developers in solving a shortcut problem. For any SQL Developer or DBA the most common short cuts are Ctrl N and Ctrl+R. Unfortunately Ctrl N short cut is not working in SQL 2008 with the default installation.

Now we’ll see how to resolve this issue. Ctrl+N Ctrl+N short cut is not working in SQL 2008 SSMS.  We can easily fix it by changing the KeyBoard to SQL 2000 from Standard. To Change it

Tools –> Options –> Environment –> Keyboard –> Keyboard Schema Change it to SQL Server 2000.

Once this change is completed now you find that Ctrl+N is working. When we press Cntr+N a new query window will be opened.

Along with this there is an other short cut that will be very helpful for formatting T-SQL code which is  Shift + K+ \.

 When you opened a stored procedure and pasted it on Query Editor it look likes a bulky element and obviously difficult to understand. Just select the entire code and Press

Shift + K+ \

 

Posted in SQL Development | Tagged , , , , | Leave a comment

Automating SQL Server instance and Database Inventory preparation

Automating SQL Server instance and Database Inventory preparation

In our environment we have a requirement for Automating SQL Server instance and Database Inventory preparation. We tried different methods and came-up with the T-SQL scripts that helped us in creating the environment map. This script will capture the internal sql server details and stores on a centralized sql server database from where we can build a SSRS report that showcases the database environment map.

We need to create tables and corresponding procedures. Table creation:

  • Environment: Store data about available environments. Ex: Dev, Test, Prod etc
  • Servers: Host information Ex: Windows servers
  • Instance_Inventory: SQL Server instance details
  • DB Inventory: Database information on each instance

Script for Table Creation:

--Script to create Inventory Tables:

/****** Object:  Table [dbo].[Environment]    Script Date: 01/04/2011 06:30:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Environment](
    [ID] [tinyint] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](30) NOT NULL,
    [Deleted] [bit] NOT NULL DEFAULT ((0)),
PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH(PAD_INDEX  =OFF,STATISTICS_NORECOMPUTE  =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS  =ON,ALLOW_PAGE_LOCKS  =ON)ON [PRIMARY]
)ON [PRIMARY]
GO

SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[Servers]    Script Date: 01/04/2011 06:30:30 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Servers](
    [ID] [smallint] IDENTITY(1,1) NOT NULL,
    [Server_Name] [varchar](100) NOT NULL,
    [OS_Version] [varchar](100) NULL,
    [EnvironmentID] [tinyint] NULL,
    [IPAddress] [varchar](20) NULL,
    [Deleted] [bit] NOT NULL DEFAULT ((0)),
PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH(PAD_INDEX  =OFF,STATISTICS_NORECOMPUTE  =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS  =ON,ALLOW_PAGE_LOCKS  =ON)ON [PRIMARY]
)ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[InstanceInventory]    Script Date: 01/04/2011 06:30:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[InstanceInventory](
    [ID] [smallint] IDENTITY(1,1) NOT NULL,
    [ServerID] [smallint] NOT NULL,
    [InstanceName] [varchar](100) NOT NULL,
    [Version] [varchar](100) NULL,
    [Edition] [varchar](100) NULL,
    [InsertedOn] [datetime] NOT NULL DEFAULT (getdate()),
    [Disabled] [bit] NOT NULL DEFAULT ((0)),
PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH(PAD_INDEX  =OFF,STATISTICS_NORECOMPUTE  =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS  =ON,ALLOW_PAGE_LOCKS  =ON)ON [PRIMARY]
)ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[DBInventory]    Script Date: 01/04/2011 06:30:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[DBInventory](
    [ID] [smallint] IDENTITY(1,1) NOT NULL,
    [InstanceInventoryID] [smallint] NOT NULL,
    [DBName] [varchar](100) NOT NULL,
    [CompatabilityLevel] [tinyint] NULL,
    [InsertedOn] [datetime] NOT NULL DEFAULT (getdate()),
    [DBSize] [varchar](50) NULL
)ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  ForeignKey [FK__DBInvento__Insta__173876EA]    Script Date: 01/04/2011 06:30:20 ******/
ALTER TABLE [dbo].[DBInventory]  WITH CHECK ADD FOREIGN KEY([InstanceInventoryID])
REFERENCES [dbo].[InstanceInventory] ([ID])
GO
/****** Object:  ForeignKey [FK__InstanceI__Serve__145C0A3F]    Script Date: 01/04/2011 06:30:25 ******/
ALTER TABLE [dbo].[InstanceInventory]  WITH CHECK ADD FOREIGN KEY([ServerID])
REFERENCES [dbo].[Servers] ([ID])
GO
/****** Object:  ForeignKey [FK__Servers__Environ__108B795B]    Script Date: 01/04/2011 06:30:30 ******/
ALTER TABLE [dbo].[Servers]  WITH CHECK ADD FOREIGN KEY([EnvironmentID])
REFERENCES [dbo].[Environment] ([ID])
GO


--Login Script toCreate a uniqueloginonall the Servers:


SET NOCOUNT ON

DECLARE @exec_stmt NVARCHAR(625)
DECLARE @name      SYSNAME

USE MASTER

IF NOT EXISTS(SELECT 1 FROM SYSLOGINS WHERE [name]='Dba_maintenance')
BEGIN
EXEC sp_addlogin Dba_maintenance,testpwd@1
PRINT'LoginId: Dba_maintenance created'
END

CREATE TABLE #spdbdesc
(
 dbname sysname,
)

INSERT INTO #spdbdesc (dbname)
  SELECT NAME FROM master.dbo.sysdatabases


DECLARE ms_crs_c1 cursor global for
SELECT dbname from #spdbdesc
OPEN ms_crs_c1
FETCH ms_crs_c1 INTO @name
WHILE @@FETCH_STATUS <> 0
BEGIN
    SELECT @exec_stmt = 'use '+@name+Char(10)+' if not exists(select 1 from sysusers where name=''Dba_maintenance'')'+Char(10)+' begin'+Char(10)+'

EXEC sp_adduser ''Dba_maintenance'',''Dba_maintenance'''+Char(10)+' print ''User created at '+@name+''''+Char(10)+'end'

EXECUTE(@exec_stmt)
FETCH ms_crs_c1 INTO @name
END

DEALLOCATE ms_crs_c1   

DROP TABLE #spdbdesc

 

Script for Stored Procedure Creation:

Usp_Inventory_Databases: To capture database information for the given inventory

Usp_Inventory_Instances: To capture instance information from the network

GO

------------------------------------------------------------------------------------------------      
------------------------------------------------------------------------------------------------      
--GETTING DFATABASE DETAILS FOR THE GIVEN INSTANCE
--Parameter @InstanceID - ID column from InstanceInventory
------------------------------------------------------------------------------------------------      
------------------------------------------------------------------------------------------------      
CREATE PROC usp_Inventory_Databases(@InstanceID SMALLINT)
AS
BEGIN
SET NOCOUNT ON

/*** DECLARATION SECTION ***/
DECLARE @InstanceName VARCHAR(100)    
SELECT @InstanceName=InstanceName FROM InstanceInventory WHERE ID=@InstanceID    
DECLARE @String VARCHAR(1000)
DECLARE @Low NVARCHAR(11)
DECLARE @USER VARCHAR(50)
DECLARE @PWD VARCHAR(10)
DECLARE @i TINYINT
DECLARE @Count TINYINT
DECLARE @DBName VARCHAR(100)
DECLARE @Edition TABLE(Version VARCHAR(50),Edition VARCHAR(50))

/*** Assigning UserID and PWD to the local variables ***/

SET @USER='Dba_maintenance'
SET @PWD='RnMqp@1'

/*** Updates the Edition and Version information for the given instance ***/

SET @String =' SELECT Version,Edition from OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+'Dba_maintenance'+';PWD='+'RnMqp@1'+''','' SELECT LEFT(@@VERSION,38) AS Version,CAST(SERVERPROPERTY(''''productlevel'''') AS VARCHAR) + '''' ('''' + CAST(SERVERPROPERTY(''''edition'''') AS VARCHAR) + '''')'''' AS Edition'')'

INSERT INTO @Edition (Version,Edition)Execute(@String)

UPDATE InstanceInventory
SET
Version=(SELECT Version FROM @Edition),
Edition=(SELECT Edition FROM @Edition) WHERE ID=@InstanceID

SET @String=''

/*** Captures the database_name and db compatibility level into #Databases ***/

IF OBJECT_ID('tempdb..#Databases') IS NULL
BEGIN
    CREATE TABLE #Databases (ID INT IDENTITY,Name VARCHAR(100),CmptLevel TINYINT,DBSize VARCHAR(50),State INT )    
END

SET @String =' INSERT INTO #Databases (Name,Cmptlevel) SELECT Name, Cmptlevel from OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''', master.dbo.sysdatabases )'

Execute(@String)
SET @STRING=''

/*** Calculates the Database size and updates at #Databases ***/
/*** While updating details first it verifies the state of the database. ***/
/*** If it is not accessable it skips that db and continue to the next DB ***/

SELECT @Low =CONVERT(VARCHAR(11),Low)FROM Master.dbo.spt_values WHEREtype= N'E'and number = 1    
SELECT @Count=COUNT(1)FROM #Databases
SET @i=1
SET @String=''
WHILE(@Count<=@i)
BEGIN
    SELECT @DBName=Name FROM #Databases WHERE ID=@i
    SET @String= 'UPDATE #Databases
        SET State= (SELECT S FROM OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''',''SELECT has_dbaccess('''''+@DBName+''''') As S'')) WHERE ID='+CONVERT(VARCHAR,@i)

    EXECUTE(@String)
    SET @STRING=''
    IF((SELECT State FROM #Databases WHERE ID=@i)=1)
    BEGIN
        SET @String= 'UPDATE #Databases
        SET DBSize = (SELECT STR(CONVERT(dec(15),sum(size))* '+ @Low +'/ 1048576,10,2)+ N'' MB'' FROM OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''','
         +QUOTENAME(@DBName, N'[')+ N'.dbo.sysfiles)) WHERE ID='+CONVERT(VARCHAR,@i)+' AND State=1'
  
        EXECUTE(@String)
    END
    SET @i=@i+1
END

/*** Inserts the Database details into ***/

INSERT INTO DBInventory (InstanceInventoryID,DBName,CompatabilityLevel,DBSize)SELECT @InstanceID,Name,CmptLevel,DBSize FROM #Databases
DROP TABLE #Databases

END

GO
------------------------------------------------------------------------------------------------      
------------------------------------------------------------------------------------------------      
/*** GETTING INSTANCE DETAILS FOR THE GIVEN SERVERNAME ***/
/*** Parameter - @ServerName***/
------------------------------------------------------------------------------------------------      
------------------------------------------------------------------------------------------------      
       
CREATE PROC usp_Inventory_Instances(@ServerName VARCHAR(50))      
AS      
BEGIN      
SET NOCOUNT ON      

/*** DECLARATION SECTION ***/

DECLARE @Instances TABLE(ServerName VARCHAR(100))      
DECLARE @MachedInstances TABLE(ID INT IDENTITY,ServerName VARCHAR(50))      
DECLARE @TestConnection TABLE(Name VARCHAR(1000))
DECLARE @Counter SMALLINT      
DECLARE @I SMALLINT      
DECLARE @InstanceName VARCHAR(50)      
DECLARE @ServerID SMALLINT      
DECLARE @InstanceID SMALLINT 
DECLARE @String VARCHAR(1000)

/*** Checking the given server name againest the existing server list ***/
/*** If the name is not avilable it exit the process***/

SET @ServerID=0      
SELECT @ServerID=ID FROM Servers WHERE Server_Name=@ServerName      
IF(@ServerID=0)      
BEGIN      
RETURN 0      
END 
      
/*** Captures all the existing servers into tab variable @Instances ***/
/*** Get all the related server/instance names by maching the given servername***/
/*** A loop starts to get each individual instance details***/
/*** First it inserts the instancename and assigns the Identity value to @InstanceID ***/
/*** It test the connection using the unique userID and PWD***/
/*** If it is not able to connect to that instance it simply updates the column Disabled as 1***/
/*** If the instance is accessable calls the sp usp_Inventory_Databases by passing the instanceID***/
/*** That sp get all the database details and insert into DBInventory table ***/
/*** Loop continues till the last instance***/

INSERT INTO @Instances EXECXP_CMDSHELL'SQLCMD -L';      
INSERT INTO @MachedInstances (ServerName) SELECT LTRIM(RTRIM(ServerName))FROM @Instances WHERE ServerName LIKE'%'+@ServerName+'%'      
SELECT @Counter=COUNT(1)FROM @MachedInstances      
SET @i=1      
BEGIN TRAN
BEGIN TRY
WHILE(@Counter<=@i)      
BEGIN
SELECT @InstanceName=ServerName FROM @MachedInstances WHERE ID=@i      
  INSERT INTO InstanceInventory (ServerID,InstanceName) VALUES(@ServerID,@InstanceName)      
  SET @InstanceID=SCOPE_IDENTITY()
  SET @String='SQLCMD -S '+@InstanceName+' -U Dba_maintenance -P RnMqp@1 -q "USE MASTER;"'    
  DELETE @TestConnection
  INSERT INTO @TestConnection EXEC XP_CMDSHELL @String    
  IF EXISTS(SELECT 1 FROM @TestConnection WHERE Name LIKE'%Sqlcmd:%Error%' OR Name LIKE '%HResult%Level%' OR Name LIKE '%Login failed%')    
  BEGIN    
    UPDATE INSTANCEINVENTORY SET Disabled=1 WHERE ID=@InstanceID  
  END
  ELSE
  BEGIN
    EXEC usp_Inventory_Databases @InstanceID
  END
  SET @i=@i+1
  PRINT 'Instance :'+@InstanceName+' Inserted '
END      
END TRY      
BEGIN CATCH      
ROLLBACK TRAN      
PRINT 'Rollbacked'  
RETURN 0      
END CATCH      
COMMIT 
PRINT'All instances and respective database details Inserted Successfully'       
END

 

Posted in SQL Server DBA | Tagged , , , , | 2 Comments