Pages

Sunday, May 25, 2014

Database Information

Also look at 

USE master
GO

IF OBJECT_ID('sp_dbinfo') IS NOT NULL
 DROP PROCEDURE sp_dbinfo
GO

CREATE PROCEDURE sp_dbinfo @sort CHAR(1) = 'n'
 ,@include_instance_name CHAR(1) = 'n'
AS
/***************************************************************************
@sort accept 4 values: 'n' (default), 'd', 'l' and 'r'.
@include_server_name accept 2 values, 'y' and 'n'.
It specifies the sort order (name, data allocated, log allocated, rollup only).
***************************************************************************/
SET NOCOUNT ON

DECLARE @sql NVARCHAR(2000)
 ,@db_name SYSNAME
 ,@recovery_model VARCHAR(12)
 ,@crlf CHAR(2)

SET @crlf = CHAR(13) + CHAR(10)

--Create tables to hold space usage stats from commands
CREATE TABLE #logspace (
 database_name SYSNAME NOT NULL
 ,log_size REAL NOT NULL
 ,log_percentage_used REAL NOT NULL
 )

CREATE TABLE #dbcc_showfilestats (
 database_name SYSNAME NULL
 ,file_id_ INT NOT NULL
 ,file_group INT NOT NULL
 ,total_extents BIGINT NOT NULL
 ,used_extents BIGINT NOT NULL
 ,name_ SYSNAME NOT NULL
 ,file_name_ NVARCHAR(3000) NOT NULL
 )

--Create table to hold final output
CREATE TABLE #final_output (
 database_name SYSNAME
 ,data_allocated INT
 ,data_used INT
 ,log_allocated INT
 ,log_used INT
 ,is_sum BIT
 )

--Populate log space usage
INSERT INTO #logspace (
 database_name
 ,log_size
 ,log_percentage_used
 )
SELECT instance_name AS 'Database Name'
 ,MAX(CASE 
   WHEN counter_name = 'Log File(s) Size (KB)'
    THEN cntr_value / 1024.
   ELSE 0
   END) AS 'Log Size (MB)'
 ,MAX(CASE 
   WHEN counter_name = 'Percent Log Used'
    THEN cntr_value
   ELSE 0
   END) AS 'Log Space Used (%)'
FROM master..sysperfinfo
WHERE counter_name IN (
  'Log File(s) Size (KB)'
  ,'Percent Log Used'
  )
 AND instance_name != '_total'
GROUP BY instance_name

----Populate data space usage 
DECLARE db CURSOR
FOR
SELECT NAME
FROM sys.databases
WHERE state_desc = 'ONLINE'

OPEN db

WHILE 1 = 1
BEGIN
 FETCH NEXT
 FROM db
 INTO @db_name

 IF @@FETCH_STATUS <> 0
  BREAK

 SET @sql = 'USE ' + QUOTENAME(@db_name) + ' DBCC SHOWFILESTATS WITH NO_INFOMSGS'

 INSERT INTO #dbcc_showfilestats (
  file_id_
  ,file_group
  ,total_extents
  ,used_extents
  ,name_
  ,file_name_
  )
 EXEC (@sql)

 UPDATE #dbcc_showfilestats
 SET database_name = @db_name
 WHERE database_name IS NULL
END

CLOSE db

DEALLOCATE db

--Result into final table
INSERT INTO #final_output (
 database_name
 ,data_allocated
 ,data_used
 ,log_allocated
 ,log_used
 ,is_sum
 )
SELECT CASE 
  WHEN d.database_name IS NOT NULL
   THEN d.database_name
  ELSE '[ALL]'
  END AS database_name
 ,ROUND(SUM(CAST((d.data_alloc * 64.00) / 1024 AS DECIMAL(18, 2))), 0) AS data_allocated
 ,ROUND(SUM(CAST((d.data_used * 64.00) / 1024 AS DECIMAL(18, 2))), 0) AS data_used
 ,ROUND(SUM(CAST(log_size AS NUMERIC(18, 2))), 0) AS log_allocated
 ,ROUND(SUM(CAST(log_percentage_used * 0.01 * log_size AS NUMERIC(18, 2))), 0) AS log_used
 ,GROUPING(d.database_name) AS is_sum
FROM (
 SELECT database_name
  ,SUM(total_extents) AS data_alloc
  ,SUM(used_extents) AS data_used
 FROM #dbcc_showfilestats
 GROUP BY database_name
 ) AS d
INNER JOIN #logspace AS l ON d.database_name = l.database_name
INNER JOIN sys.databases AS sd ON d.database_name = sd.NAME
GROUP BY d.database_name
WITH ROLLUP

--Output result
SET @sql = '
SELECT f.database_name, f.data_allocated, f.data_used, f.log_allocated, f.log_used, d.recovery_model_desc' + CASE @include_instance_name
  WHEN 'y'
   THEN ', @@SERVERNAME AS instance_name'
  ELSE ''
  END + @crlf + 'FROM #final_output AS f LEFT OUTER JOIN sys.databases AS d ON f.database_name = d.name' + @crlf + CASE 
  WHEN @sort = 'r'
   THEN 'WHERE f.database_name = ''[ALL]'''
  ELSE ''
  END + @crlf + 'ORDER BY is_sum' + @crlf + CASE 
  WHEN @sort = 'n'
   THEN ', database_name'
  WHEN @sort = 'd'
   THEN ', data_allocated DESC'
  WHEN @sort = 'l'
   THEN ', log_allocated DESC'
  ELSE ''
  END

--PRINT @sql
EXEC (@sql)
 --Test execution
 /*
EXEC sp_dbinfo
EXEC sp_dbinfo 'n'
EXEC sp_dbinfo 'd'
EXEC sp_dbinfo 'l'
EXEC sp_dbinfo 'r'
EXEC sp_dbinfo 'n', 'y'
EXEC sp_dbinfo 'd', 'y'
*/
GO

EXEC sp_MS_marksystemobject 'sp_dbinfo'

Also look at 

Thursday, May 15, 2014

Find the Instance Name , Port on which SQL Server is Listening

SQL Server is sometimes installed as named instances and sometimes will be made to sit on different ports.

The host name , Server name and the instance name can be identified by the SQL functions

SELECT SERVERPROPERTY('servername') ServerName
 ,SERVERPROPERTY('InstanceName') instancename
 ,SERVERPROPERTY('MachineName') AS HOSTNAME

For the Port number you can read it from the Registry or the ErrorLog

Registry:

SET NOCOUNT ON

DECLARE @key VARCHAR(1000)
 ,@PortNumber VARCHAR(20)

IF charindex('\', CONVERT(CHAR(40), SERVERPROPERTY('servername')), 0) <> 0
BEGIN
 SET @key = 'SOFTWARE\MICROSOFT\Microsoft SQL Server\' + @@servicename + '\MSSQLServer\Supersocketnetlib\TCP'
END
ELSE
BEGIN
 SET @key = 'SOFTWARE\MICROSOFT\MSSQLServer\MSSQLServer\Supersocketnetlib\TCP'
END

EXEC master..xp_regread @rootkey = 'HKEY_LOCAL_MACHINE'
 ,@key = @key
 ,@value_name = 'Tcpport'
 ,@value = @PortNumber OUTPUT

Error Log:
USE master
GO

xp_readerrorlog 0
 ,1
 ,N'Server is listening on'
GO

Wednesday, May 7, 2014

Quick Summary for Minimally Logged Operations ( Inserts and Merge)

INSERT PERFORMANCE:
Below is the summary of options that we can use for improving insert performance.
This is assuming the database is in simple or Bulk Recovery model.

Table Indexes
Rows in table
Hints
Without TF 610
With TF 610
Concurrent possible
Heap
Any
TABLOCK
Minimal
Minimal
Yes
Heap
Any
None
Full
Full
Yes
Heap + Index
Any
TABLOCK
Full
Depends (3)
No
Cluster
Empty
TABLOCK, ORDER (1)
Minimal
Minimal
No
Cluster
Empty
None
Full
Minimal
Yes (2)
Cluster
Any
None
Full
Minimal
Yes (2)
Cluster
Any
TABLOCK
Full
Minimal
No
Cluster + Index
Any
None
Full
Depends (3)
Yes (2)
Cluster + Index
Any
TABLOCK
Full
Depends (3)
No
Table 1: Summary of minimal logging conditions



(1) If you are using the INSERT … SELECT method, the ORDER hint does not have to be specified, but the rows must be in the same order as the clustered index. If using BULK INSERT the order hint must be used.
(2) Concurrent loads only possible under certain conditions. Also, only rows written to newly allocated pages are minimally logged.
(3) Depending on the plan chosen by the optimizer, the non-clustered index on the table may either be fully- or minimally logged.

MERGE PERFORMANCE:

The Transact-SQL MERGE command offers minimal logging of the inserted rows. Unless you run MERGE on a heap, this minimal logging ability will only be used if trace flag 610 is enabled.

Internals of Clustered Column Store Index

A great link for getting to know the design and architecture of the Clustered Columnstore Index.

SQL Server Column Store Indexes - Microsoft Research


All the sections are interesting, the Batch mode processing updates and how updates and deletes are handled gives you an idea of how it is entirely different from the traditional B-Tree row store.

Other Links in the Clustered Column Store Series:
"Clustered Columnstore Indexes" Limitations
Clustered Columnstore Index - Delta Store explained

Tuesday, May 6, 2014

Move User Database Files in SQL Server

As part of Database maintenance , we might have to move the database files to different disk drives/volumes. This could be because of a variety of reasons like the database has grown and the current drive cannot hold it etc.

This cane be accomplished using atleast two ways:

  • Take the database Offline, move the file and alter the master data, bring it online
  • Detach and Attach

Offline Approach:
  1. Run the following statement.
    ALTER DATABASE database_name SET OFFLINE;
    
  2. Move the file or files to the new location.
  3. For each file moved, run the following statement.
    ALTER DATABASE database_name MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
    
  4. Run the following statement.
    ALTER DATABASE database_name SET ONLINE;
    
  5. Verify the file change by running the following query.
    SELECT name, physical_name AS CurrentLocation, state_desc
    FROM sys.master_files
    WHERE database_id = DB_ID(N'<database_name>');
This approach has a few problems when many active users are connected to the database.
There are recommendations to put it in single user mode
ALTER DATABASE <dbname> SET SINGLE_USER WITH ROLLBACK IMMEDIATE
This can be troublesome if another admin gets hold of the session before you do.
Also, sometimes processes might block you from becoming the single user , "SQL server Transact Intellisense" is one weird one that I encountered once.

Attach/Detach Approach:

This is very simple,

  1. Detach the database , tick the DROP Connections Check Box to disconnect any user connections.
  2. Move the file(s)
  3. Attach the database, select the appropriate files.



Thursday, May 1, 2014

"Clustered Columnstore Indexes" Limitations

Clustered Columnstore indexes is one of the best things that happened to SQL server for Data Warehousing applications. But having said that there are a few limitations that have to be kept in mind:

Columns that use any of the following data types cannot be included in a columnstore index.
  • ntext, text, and image
  • varchar(max) and nvarchar(max)
  • rowversion (and timestamp)
  • sql_variant
  • CLR types (hierarchyid and spatial types)
  • xml
Columnstore indexes cannot be combined with the following features:
  • Page and row compression, and vardecimal storage format (A columnstore index is already compressed in a different format.)
  • Replication
  • Change tracking
  • Change data capture
  • Filestream

Clustered Columnstore Index - Delta Store explained

SQL Server 2014 is using a combination of data structures for storing the data of a Clustered Columnstore Index. As depicted in the below diagram it has two parts:
  • Compressed Column Segments
  • Delta Store



Now let us see how the delta stores work.
Lets start with creating a simple table:


CREATE TABLE Int_Table (
 ID INT IDENTITY(1, 1)
 ,EmpId INT
 ,PinCode INT
 )
Insert a million records into the table:
SET NOCOUNT ON

DECLARE @Counter INT = 0

WHILE (@Counter < 1000000)
BEGIN
 INSERT INTO Int_Table (
  EmpId
  ,PinCode
  )
 SELECT ABS(CHECKSUM(NewId())) % 1000000
  ,ABS(CHECKSUM(NewId())) % 100000

 SET @Counter = @Counter + 1
END
The important tables that give the details of Columnstore indexes are:

SELECT *
FROM sys.column_store_segments

SELECT *
FROM sys.column_store_dictionaries

SELECT *
FROM sys.column_store_row_groups


You will not find anything in these as yet since we have not created a CCI (Clustered Columnstore Index)
Lets create a CCI now:

CREATE CLUSTERED COLUMNSTORE INDEX IDX ON INT_TABLE
 WITH (DROP_EXISTING = ON)

You will find 3 segments in column_store_segments - basically one for each column.
And nothing will be found in the column_store_dictionaries as we are dealing with integer columns here , no string types.
When the column_store_row_groups is queried the following is shown :




This things to notice here are:
  • the delta_store_hobt_id is NULL - This says the entire data is residing as a compressed column store.
  • the deleted_rows is 0. This is because SQL Server maintains a delete bitmap for the column store we will discuss about this a little more
Inserts:

When new data is inserted the data is not instantly ,compressed on the fly, put into the column store. 
The data goes into the Delta Store.SQL server runs a deamon periodically , the tuple mover, which moves the data from Delta Store. 
 Lets check that by inserting an additional 1000 rows. we use the same script as above but this time we will insert only 1000 rows.

Now if you query the row_groups you will see :



You will find an additional row with a non-NULL delta_store_hobt_id with the newly inserted 1000 rows which is our DeltaStore.

Deletes: 
The Deletes are tracked in a separate data structure called the DeleteBitMapdata
This is can be partially demonstrated.
Lets delete a bunch of rows. Since we have an ID column which is sequential, lets delete the last 1100 rows 

DELETE
FROM Int_table
WHERE id > 999900
Now when you query row_groups you will find 1100 in the deleted_rows column


Updates = deletes + inserts
For the Columnstore index case the updates are handled as Deletes + Inserts

UPDATE Int_Table
SET EmpID = ABS(CHECKSUM(NewId())) % 1000000
WHERE id > 900000
You will notice the number of rows updated will now be in the Delta store ( 99900) and also the deleted count will increase by the same amount:

Move things from Delta Store:
Now the tuple mover moves the data only when the delta store is big enough ( May be a million rows big).
If you want to force the SQL server to move the data to a column store you can re-build the index.

Other Links in the Clustered Column Store Series:
Internals of Clustered Column Store Index
"Clustered Columnstore Indexes" Limitations