Find the Buffer Pool usage per database in SQL Server

In this short article i will share a simple query that i wrote to find the number of pages of a database in the buffer pool and there usage of the buffer pool

SELECT CASE database_id WHEN 32767 THEN 'ResourceDb'
ELSE DB_NAME(database_id)
END AS [Database],COUNT(*) AS PageCount ,
( COUNT(*) * 8.0 ) / 1024 AS [SpaceOccupiedInBuffer-Mb]
FROM sys.dm_os_buffer_descriptors
GROUP BY database_id
ORDER BY 3 DESC

BF1

Advertisement

How to find and remove a query plan from the plan cache in SQL Server

In this article I will try to explain how we can obtain the plan of a SQL query from the plan cache and also how we can remove the plan from the cache keeping other plans intact. Before we see how we can achieve the above we will understand few basic things on plan cache.
Structure
1) What is a query plan: A query plan is an organized and ordered set of steps used by the SQL engine to fetch data. Every query has a query plan cached in the SQL memory
2) What is a plan cache: Apart from Buffer pool which is the biggest consumer of SQL Server memory, SQL Server allocates a proportion of this memory for use of storing the query plans. This storage of query plans in the SQL memory is called plan cache. The storage is a volatile storage and if the SQL server is restarted then all the plans are removed from this cache.
3) What is a plan handle: A plan handle is an unique identifier of the plan.

Let us now see how we can locate the query plan of an already executed query:
Step 1: In this example I have executed the below query on the Adventureworks2012 database.

SELECT [BusinessEntityID],[Title]
,[FirstName],[MiddleName]
,[LastName],[Suffix]
,[JobTitle],[PhoneNumber]
,[PhoneNumberType],[EmailAddress]
,[EmailPromotion],[AddressLine1]
,[AddressLine2],[City]
,[StateProvinceName],[PostalCode]
,[CountryRegionName],[AdditionalContactInfo]
FROM [AdventureWorks2012].[HumanResources].[vEmployee]
WHERE PostalCode = '98104'

Step 2: Now let us find the query in the plan cache. We will use the below query to find the plan of the query from the plan cache. Observe carefully that in line number 11 we have used a filter to search all the queries in the plan cache which has the word ‘PostalCode’ which is also a part of our executed code on the Adventureworks2012 database.

select a.plan_handle,query_plan,text,
execution_count,size_in_bytes,
creation_time,usecounts,
last_execution_time,execution_count,
total_elapsed_time,last_elapsed_time
from sys.dm_exec_cached_plans a
inner join sys.dm_exec_query_stats b
on a.plan_handle=b.plan_handle
cross apply sys.dm_exec_sql_text(b.sql_handle) as sql_text
cross apply sys.dm_exec_query_plan(a.plan_handle) as query_plan
where text LIKE '%PostalCode%'

Snapshot of the output below
Qp1
Step 3: The highlighted text is the query that we ran and you can see the the query plan associated with it on the second column. Click on the plan and it will open on a new window.
qp2
qp3
So now we know how we can find the plan of an already executed query from the cache. In short use the above mentioned query and use a string from the executed query as filter in the above given query.

Now let us see how we can remove the query plan from the plan cache
Step 1: Find the plan handle of the query plan.
qp4
Step 2: Execute the below code to drop the plan from the plan cache

DBCC freeproccache(0x06000100A8CEE12640BB20D50200000001000000000000000000000000000000000000000000000000000000)

This will only drop that particular plan from the plan cache.Be very careful that you do not practice this on a production environment without understanding the implications of the absence of the query plan which you dropped.

I hope this article was helpful in finding and removing query plans from the plan cache without impacting other plans.

Modified DMV’s in SQL Server 2008 R2 and SQL Server 2012

In this post i will discuss about two dynamic management views that has been enhanced with new columns in SQL Server 2008 R2 and SQL Server 2012. The DMV’s are

1) sys.dm_exec_query_stats
2) sys.dm_exec_sessions

Let us begin with [sys.dm_exec_query_stats]
So what does this view reflect. It reflects performance data for stored query plans in SQL Server plan cache. The data is aggregated and is helpful for performance analysis. When a query plan is eliminated from the plan cache, the related row is also removed. In SQL Server 2008 R2 onwards, 3 new columns have been added to this DMV. They are as follows as explained on Technet.

a) last_rows: The number of rows returned by the last execution of the query.
b) min_rows: Minimum number of rows returned by the query since last recompilation of the query and execution of the plan.
c) max_rows: Maximum number of rows returned by the query over the number of times that the plan has been executed since it was last compiled.

The next dmv that was modified with new columns are [sys.dm_exec_sessions]
This view returns a row per authenticated session on SQL Server. It shows information about all the active user connections and internal tasks that are being performed. The new columns that have been added to this view are as follows:

a) Database_id: iD of the current database for each session.
b) authenticating_database_id:
i) When the source of the session is a contained database, then the value is the db id of the authenticating database.
ii) The value will be 1 if it is a server scoped principal i.e a windows or SQL login.
iii) This will be Null if the session is that of an internal task.
c) open_transaction_count: Number of open transactions per session.

How to Remove Table Partitioning in SQL Server

In this article we will see how we can remove partitions from a table in a database in SQL server. In my previous post i had demonstrated how we can partition a table via T-SQL. Lets now remove the partitions and merge the data in a single partition. I will start from where we left off in my previous post of partitioning a table.

1) Run the below code to create a database named PartitionDB that would include a table that has been partitioned.

USE master
GO
CREATE DATABASE PartitionDB
ON PRIMARY (NAME = N'PartitionDB'
,FILENAME = N'D:\MSSQL\Data\PartitionDB.mdf'
,SIZE = 50MB, FILEGROWTH = 150MB)
LOG ON (
NAME = N'PartitionDB_log'
,FILENAME = N'D:\MSSQL\Logs\PartitionDB_log.ldf'
,SIZE = 10MB, FILEGROWTH = 100MB);
GO

ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG1;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG2;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG3;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG4;
GO

ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile1,
        FILENAME = 'D:\MSSQL\Data\PartitionFile1.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG1;
GO

ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile2,
        FILENAME = 'D:\MSSQL\Data\PartitionFile2.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG2;
GO

ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile3,
        FILENAME = 'D:\MSSQL\Data\PartitionFile3.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG3;
GO

ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile4,
        FILENAME = 'D:\MSSQL\Data\PartitionFile4.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG4;
GO

CREATE PARTITION FUNCTION PartFunc1 (int)
    AS RANGE LEFT FOR VALUES (10, 20, 30);
GO

CREATE PARTITION SCHEME PartScheme1
    AS PARTITION PartFunc1
    TO (PartitionFG1, PartitionFG2,PartitionFG3,PartitionFG4);
GO

USE [PartitionDB]
GO
CREATE TABLE PartitionTable
    (
    MyID int NOT NULL,
    MyDate datetime NULL,
    Name varchar(50) NULL
    )  ON PartScheme1(MyID)
GO
USE PartitionDB
go
CREATE UNIQUE CLUSTERED INDEX IX_PartitionTable
ON PartitionTable(MyID)
ON PartScheme1 (MyID);
GO

USE PartitionDB
go
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (1,GETDATE(),'Rooney');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (11,GETDATE(),'Van persie');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (22,GETDATE(),'De Gea');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (34,GETDATE(),'Moyes');
GO

Run the below code to see the details of the partitioned table

USE PartitionDB
GO
SELECT
OBJECT_NAME(idx.object_id) AS TableName ,
psh.name AS PartitionSchemeName ,
fnc.name AS PartitionFunctionName,
part.partition_number AS PartitionNumber ,
fg.name AS [Filegroup],
rows AS 'No of Records' ,
CASE boundary_value_on_right WHEN 1 THEN 'less than'
ELSE 'less than or equal to' END AS 'Condition',
value AS 'Range' ,
part.partition_id AS [Partition Id] FROM sys.partitions part
JOIN sys.indexes idx
ON part.object_id = idx.object_id
AND part.index_id = idx.index_id JOIN sys.partition_schemes psh
ON psh.data_space_id = idx.data_space_id
JOIN
sys.partition_functions fnc
ON fnc.function_id = psh.function_id LEFT
JOIN sys.partition_range_values prv
ON fnc.function_id = prv.function_id
AND part.partition_number = prv.boundary_id
JOIN sys.destination_data_spaces dds
ON dds.partition_scheme_id = psh.data_space_id
AND dds.destination_id = part.partition_number
JOIN sys.filegroups fg
ON dds.data_space_id = fg.data_space_id
JOIN (SELECT container_id, sum(total_pages) as total_pages
FROM
sys.allocation_units GROUP BY container_id) AS au
ON au.container_id = part.partition_id JOIN sys.tables t ON
part.object_id = t.object_id WHERE idx.index_id < 2
ORDER BY TableName,part.partition_number;
GO

Now let us understand how we can remove the partitions from this table.
The easiest way to do this is to drop the Clustered index from this table and recreate it on another filegroup.

Step 1: Drop the clustered index from the table

USE [PartitionDB]
GO
DROP INDEX [IX_PartitionTable] ON
[dbo].[PartitionTable] WITH ( ONLINE = OFF )
GO

Step 2: Re-create the clustered index on another Filegroup. We will use the primary FG as example

USE [PartitionDB]
GO
CREATE UNIQUE CLUSTERED INDEX [IX_PartitionTable] ON
[dbo].[PartitionTable]
(
 [MyID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
GO

Step 3: Verify the state of the partitions by running the below code. You will find that there is only one partition with all the 4 rows in it.

USE PartitionDB
go
SELECT * FROM sys.partitions
WHERE OBJECT_NAME(OBJECT_ID)='partitiontable';
GO

P3You can verify the same via SSMS by performing the following steps:
Step 1: Right click on the table
Step 2: Click on properties
Step 3: Click on Storage
Step 4 : Verify that “Table is partitioned” is false.
P4I hope this article was helpful in understanding how we can remove Partitioning from table.

How to Partition a table in SQL Server

In this article we will see how we can partition a table in a database using T-SQL. We will first understand the basics of table partitioning in SQL Server and then go through the scripts that we will use to build a partitioned table. Let us start with the basics of partitioning.

What is table partitioning in SQL Server?
Partitioning is dividing a large table and its indexes into smaller fragments called partitions.

Some of the Benefits?
Its helps performing maintenance operation fragment by fragment basis rather that performing on the entire table. Apart from that SQL queries can be redirected to proper partitions directly rather than scanning the entire table.SQL queries that are properly filtered on the partition column can perform better by making use of partition elimination and parallelism. Archiving data is another major benefit of partitioning. We will not deep dive in this article on the benefits of partitioning.

Components of Partitioning:
1) Partition Function(PF): PF defines which rows goes into what partition in a partitioned table based on a range.
2) Partition Scheme(PS):The partition scheme defines how partitions will be stored on filegroups. Creating a partition scheme assumes that your database already has filegroups.

Let us now understand with an example how we can partition a table in SQL server.
1) Creating a database:

USE master
GO
CREATE DATABASE PartitionDB
ON PRIMARY (NAME = N'PartitionDB'
,FILENAME = N'D:\MSSQL\Data\PartitionDB.mdf'
,SIZE = 50MB, FILEGROWTH = 150MB)
LOG ON (
NAME = N'PartitionDB_log'
,FILENAME = N'D:\MSSQL\Logs\PartitionDB_log.ldf'
,SIZE = 10MB, FILEGROWTH = 100MB);
GO

2) Adding 4 new filegroups to the PartitionDB database.

ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG1;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG2;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG3;
GO
ALTER DATABASE PartitionDB ADD FILEGROUP PartitionFG4;
GO

3) Adding files to the database filegroups.

 ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile1,
        FILENAME = 'D:\MSSQL\Data\PartitionFile1.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG1;
GO
 ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile2,
        FILENAME = 'D:\MSSQL\Data\PartitionFile2.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG2;
GO
 ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile3,
        FILENAME = 'D:\MSSQL\Data\PartitionFile3.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG3;
GO

 

 ALTER DATABASE PartitionDB
    ADD FILE
    (
        NAME = PartitionFile4,
        FILENAME = 'D:\MSSQL\Data\PartitionFile4.ndf',
        SIZE = 20MB, MAXSIZE = 50MB, FILEGROWTH = 5MB
    )
    TO FILEGROUP PartitionFG4;
GO

4) Creating a Partition Function(PF):

 CREATE PARTITION FUNCTION PartFunc1 (int)
    AS RANGE LEFT FOR VALUES (10, 20, 30);
GO

5) Creating a Partition Scheme(PS):

CREATE PARTITION SCHEME PartScheme1
    AS PARTITION PartFunc1
    TO (PartitionFG1, PartitionFG2,PartitionFG3,PartitionFG4);
GO

6) Create a Table:–

USE [PartitionDB]
GO
CREATE TABLE PartitionTable
	(
	MyID int NOT NULL,
	MyDate datetime NULL,
	Name varchar(50) NULL
	)  ON PartScheme1(MyID)
GO

7) Create Index on Partitioned Table

USE PartitionDB
go
CREATE UNIQUE CLUSTERED INDEX IX_PartitionTable
ON PartitionTable(MyID)
ON PartScheme1 (MyID);
GO

8) Insert Data into the Table

USE PartitionDB
go
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (1,GETDATE(),'Rooney');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (11,GETDATE(),'Van persie');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (22,GETDATE(),'De Gea');
INSERT INTO PartitionTable (MyID, MyDate,name)
VALUES (34,GETDATE(),'Moyes');
GO

9) Verify data in the table

SELECT * FROM dbo.partitiontable

10) Verify Rows Inserted in Partitions

USE PartitionDB
go
SELECT * FROM sys.partitions
WHERE OBJECT_NAME(OBJECT_ID)='partitiontable';
GO

P1
11) Run the below code to see the details of the partitioned table

USE PartitionDB
GO
SELECT
OBJECT_NAME(idx.object_id) AS TableName ,
psh.name AS PartitionSchemeName ,
fnc.name AS PartitionFunctionName,
part.partition_number AS PartitionNumber ,
fg.name AS [Filegroup],
rows AS 'No of Records' ,
CASE boundary_value_on_right WHEN 1 THEN 'less than'
ELSE 'less than or equal to' END AS 'Condition',
value AS 'Range' ,
part.partition_id AS [Partition Id] FROM sys.partitions part
JOIN sys.indexes idx
ON part.object_id = idx.object_id
AND part.index_id = idx.index_id JOIN sys.partition_schemes psh
ON psh.data_space_id = idx.data_space_id
JOIN
sys.partition_functions fnc
ON fnc.function_id = psh.function_id LEFT
JOIN sys.partition_range_values prv
ON fnc.function_id = prv.function_id
AND part.partition_number = prv.boundary_id
JOIN sys.destination_data_spaces dds
ON dds.partition_scheme_id = psh.data_space_id
AND dds.destination_id = part.partition_number
JOIN sys.filegroups fg
ON dds.data_space_id = fg.data_space_id
JOIN (SELECT container_id, sum(total_pages) as total_pages
FROM
sys.allocation_units GROUP BY container_id) AS au
ON au.container_id = part.partition_id JOIN sys.tables t ON
part.object_id = t.object_id WHERE idx.index_id &lt; 2
ORDER BY TableName,part.partition_number;
GO

P2 I hope this article was helpful in understanding how we can create table partitions via T-SQL.

Script to find the size of all Indexes in a database

Continuing with my interest with Indexes, i wrote this script that finds the size of all indexes in a database along with the table and the filegroup on which the index resides. I have used the AdventureWorks2012 database as an example. Please replace the DB name for which you want to find the information.

USE AdventureWorks2012
go
IF OBJECT_ID('tempdb..#Indexdata', 'U') IS NOT NULL
DROP TABLE #Indexdata

DECLARE
@SizeofIndex BIGINT, @IndexID INT,
@NameOfIndex nvarchar(200),@TypeOfIndex nvarchar(50),
@ObjectID INT,@IsPrimaryKey INT,
@FGroup VARCHAR(20)

create table #Indexdata (name nvarchar(50),
IndexID int, IndexName nvarchar(200),
SizeOfIndex int, IndexType nvarchar(50),
IsPrimaryKey INT,FGroup VARCHAR(20))
DECLARE Indexloop CURSOR FOR
SELECT idx.object_id, idx.index_id, idx.name, idx.type_desc
,idx.is_primary_key,fg.name
FROM sys.indexes idx
join sys.objects so
on idx.object_id = so.object_id JOIN sys.filegroups fg
ON idx.data_space_id = fg.data_space_id
where idx.type_desc != 'Heap'
and so.type_desc not in ('INTERNAL_TABLE','SYSTEM_TABLE')

OPEN Indexloop

FETCH NEXT FROM Indexloop
INTO @ObjectID, @IndexID, @NameOfIndex,
@TypeOfIndex,@IsPrimaryKey,@FGroup

WHILE (@@FETCH_STATUS = 0)
BEGIN
SELECT @SizeofIndex = sum(avg_record_size_in_bytes * record_count)
FROM sys.dm_db_index_physical_stats(DB_ID(),@ObjectID,
@IndexID, NULL, 'detailed')

insert into #Indexdata(name, IndexID, IndexName, SizeOfIndex,
IndexType,IsPrimaryKey,FGroup)
SELECT TableName = OBJECT_NAME(@ObjectID),
IndexID = @IndexID,
IndexName = @NameOfIndex,
SizeOfIndex = CONVERT(DECIMAL(16,1),(@SizeofIndex/(1024.0 * 1024))),
IndexType = @TypeOfIndex,
IsPrimaryKey = @IsPrimaryKey,
FGroup = @FGroup

FETCH NEXT FROM Indexloop
INTO @ObjectID, @IndexID, @NameOfIndex,
 @TypeOfIndex,@IsPrimaryKey,@FGroup
END
CLOSE Indexloop
DEALLOCATE Indexloop

select name as TableName, IndexName, IndexType,
SizeOfIndex AS [Size of index(MB)],
case when IsPrimaryKey = 1 then 'Yes' else 'No' End as [IsPrimaryKey]
,FGroup AS [File Group]
from #Indexdata order by SizeOfIndex DESC

Snapshot of the output below:
Indexsize

Find Top 5 executed queries ordered by execution count

I wrote this query that helps us find executed queries with most number of execution counts. The DMV that we have used in this script is sys.dm_exec_query_stats and the function that we have used is sys.dm_exec_sql_text.

SELECT TOP 5 SQLtxt.text AS 'SQL',
qstats.execution_count 
AS 'Execution Count',
qstats.total_logical_writes/DATEDIFF(second, qstats.creation_time,
GetDate()) AS 'Logical Writes Per Second',
qstats.total_physical_reads AS [Total Physical Reads],
qstats.total_worker_time/qstats.execution_count AS [Average WorkerTime],
qstats.total_worker_time AS 'Total Worker Time',
DATEDIFF(hour, qstats.creation_time,
GetDate()) AS 'TimeInCache in Hours'
FROM sys.dm_exec_query_stats AS qstats
CROSS APPLY sys.dm_exec_sql_text(qstats.sql_handle) AS SQLtxt
WHERE SQLtxt.dbid = db_id()
ORDER BY qstats.execution_count DESC

SQL Server 2012 – How to enable the “Lock Pages in Memory” Option

In this article I will discuss the feature “lock pages in memory” and demonstrate how we can enable the “Lock pages in Memory” option for SQL Server 2012 on the OS. The OS decides which accounts can utilize a process to retain data in the RAM. This would not allow the system to page out data to virtual memory on the physical disk. In SQL Server 2012, the memory architecture has been simplified for the usage of locked pages across all editions including processor. In SQL 2012 we enable this option by allowing the account which runs the SQL Server service to have this right. The below illustration shows the requirements for different editions of SQL server for Locking pages in memory. LP1If you have migrated your SQL server to SQL 2012 from SQL 2005 or SQL 2008 and if you have been using this option then you can remove the trace flag 845 from the start up parameters.

We will now see how to enable this option.

        1. Click Start, click on Run. Type gpedit.msc. Click on OK and you will find the Local group policy editor opening.
          LP2         LP3
        2. On the Local Group Policy Editor, expand Computer Configuration -> expand windows Settings -> Security settings
          LP4 LP5
        3. Open Security Settings, and then Open Local Policies and then open “User Rights Assignment” folder
          LP6
        4. Once you open the User Rights Assignment folder all the policies will be displayed. Locate “Lock pages in Memory”. Double Click on “Lock pages in memory”
          LP7LP8
        5. Click on “Add user or Group” and add the account that runs SQL Server service. Restart SQL server.
          LP9
          We can verify whether SQL Server 2012 is using “locked pages” by the following ways:
          1) Run the following query:

select od.node_id, os.memory_node_id, od.node_state_desc, os.locked_page_allocations_kb
from sys.dm_os_memory_nodes os
inner join sys.dm_os_nodes od on
os.memory_node_id = od.memory_node_id
where od.node_state_desc <> 'ONLINE DAC'

The locked_page_allocations_Kb will display a non-zero value.

LP10              2) DBCC MEMORYSTATUS will show the following:
LP11 I hope this article was helpful in understanding how we can enable this option for SQL Server 2012.

Optimize for adhoc workloads – Server Configuration Option – How it works

In this article I want to demonstrate with an example how this feature works when configured. let us begin by first understanding what is “Optimize for adhoc workloads” .

This feature helps improve the plan cache’s efficiency to handle workloads that contain many single use of ad hoc batches. When set to 1, the engine keeps a small compiled plan stub in the plan cache when an ad hoc  batch is compiled for the first time, instead of the full compiled plan. This helps in releasing memory pressure by not allowing the plan cache to become filled with compiled plans that will not be used again.

This compiled plan stub enables the SQL engine to decide that an ad hoc batch has been compiled previously but has only stored a compiled plan stub. So when this batch is compiled or executed again, the SQL engine compiles the batch, removes the compiled plan stub from the plan cache, and adds the full compiled plan to the plan cache. This will only affect new plans. Plans that are already in the plan cache are unaffected.

let us understand this with an example
Step 1: Clear the Buffer cache and plan cache
DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

Step 2: Run an Ad hoc query
USE AdventureWorks2012
GO
SELECT * FROM Production.Document WHERE DocumentLevel = 2
GO
WL1Step 3: Find if the query is cached
SELECT usecounts, cacheobjtype, objtype, text
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE usecounts > 0 AND
 text like ‘%SELECT * FROM Production.Document%’
ORDER BY usecounts DESC;
GO
WL2Step 4: Enable the “Optimize for ad hoc workloads”
SP_CONFIGURE ‘show advanced options’,1
RECONFIGURE
GO
SP_CONFIGURE ‘optimize for ad hoc workloads’,1
RECONFIGURE
GO

Step 5: Repeat Step 1
Step 6: Repeat Step 2
Step 7: Repeat Step 3
You will find that the query is not cached
WL3Step 8: Lets clear the cache and buffers again. Repeat step 1
Step 9: We need to run the ad hoc more than once now. Execute the below
SELECT * FROM Production.Document WHERE DocumentLevel = 2
GO 3
WL4Step 10: Now lets see if the query is cached
Step 11: Repeat step 3.
WL6 Yes we do see that when the same batch has multiple executions, the query is indeed cached.

To summarize the utility of this feature we can say that it helps SQL Server memory by not allowing to store Ad hoc query plans which are for single use only.

Create a report based on a parameter using SQL Server reporting services

In this demo we will create a report based on a parameter using SQL Server Reporting services. In my previous post i had shown a demo as to how to create a basic report using SQL Server reporting services.

Step 1: Open SQL Server Report Builder
Step 2: Create a new Datasource as below
R23Step 3: Create 3 new datasets as below
R24R29R30Step 4 : Right click on Parameters option and click on “Add parameter”
R25Step 5: Create a new parameter as below
R26Step 6: Click on “Available Values” and set the options as depicted
R27Step 7: Click on “Advanced” option and set the options as depicted. Click OK.
R28Step 8: Click on Insert tab and select “Insert matrix”.
R31Step 9: Design the matrix as shown below
R32Step 10: Save the report. Now the report is ready to be run. Click on the run button in the home tab. The IE should show you the below. Select a country or multiple countries from the drop down and click on “View Report”.
R33R34I hope this demo would help you to create a report based on a parameter.

Find Top 5 expensive Queries from a Write IO perspective

Recently i wrote this query that helps us find the most expensive queries from a write IO perspective. The DMV that we have used in this script is sys.dm_exec_query_stats and the function that we have used is sys.dm_exec_sql_text.

SELECT TOP 5 sqltxt.text AS ‘SQL’, qstats.total_logical_writes AS [Total Logical Writes],
qstats.total_logical_writes/DATEDIFF(second, qstats.creation_time, GetDate()) AS ‘Logical Writes Per Second’,
qstats.execution_count AS ‘Execution Count’,
qstats.total_worker_time AS [Total Worker Time],
qstats.total_worker_time/qstats.execution_count AS [Average Worker Time],
qstats.total_physical_reads AS [Total Physical Reads],
DATEDIFF(Hour, qstats.creation_time, GetDate()) AS ‘TimeInCache in Hours’,
qstats.total_physical_reads/qstats.execution_count AS ‘Average Physical Reads’,
db_name(sqltxt.dbid) AS DatabaseName
FROM sys.dm_exec_query_stats AS qstats
CROSS APPLY sys.dm_exec_sql_text(qstats.sql_handle) AS sqltxt
WHERE sqltxt.dbid = db_id()
ORDER BY qstats.total_logical_writes DESC

Find Top 5 expensive Queries from a Read IO perspective

The below written query helps us find the most expensive queries from a read IO perspective. The DMV that we have used in this script is sys.dm_exec_query_stats and the function that we have used is sys.dm_exec_sql_text.

SELECT TOP 5 qt.text AS ‘SQL’, qstats.total_physical_reads AS ‘Total Physical Reads’, qstats.total_physical_reads/qstats.execution_count AS ‘Average Physical Reads’,    qstats.execution_count AS ‘Execution Count’,
qstats.total_worker_time/qstats.execution_count AS ‘Average Worker Time’,
qstats.total_worker_time AS ‘Total Worker Time’,
DATEDIFF(Hour, qstats.creation_time, GetDate()) AS ‘AgeInCache In Hours’,  db_name(qt.dbid) AS ‘Database Name’
FROM sys.dm_exec_query_stats AS qstats
CROSS APPLY sys.dm_exec_sql_text(qstats.sql_handle) AS qt
WHERE qt.dbid = db_id()
ORDER BY qstats.total_physical_reads DESC

How to create a report using SQL Server 2012 reporting services

In this article i would like to give a demo as to how to create a report using SQL Server 2012 reporting services. I have been developing reports using this tool for some time and this attempt is to share my knowledge for the same. In this article i am assuming that SQL Server Reporting services is installed and is in a running state.

Step 1: Open the Report Manager Home page
R1Step 2: Click on the Report Builder tab.
R2
R3Step 3: Once the Report Builder opens, you will see the following screen -> Click on “New Report” -> “Blank Report”
R4Step 4: Once click on “Blank Report” you will find the below scree. This is the report builder where we will develop our first report. The while blank area is the body of the report where we will insert a matrix. On the left hand panel are the data properties based on which the report will function. Let us go through each of the terms that we see there.
a) Built-in-Fields: Built in fields help us render in built functionality on our report page. For example if we want page number on each of our pages in a report we can use the “Page Number” field from here. We will be using them in our demo.

b) Parameters : Parameters help us to build reports based on a certain input. For e.g if we want to extract some information based on 2 dates, then we need to define parameters based on which the report will pull data and display.

c) Images: This will help us add images to the report.

d) Data Sources: very important aspect of Report Builder(RB). Data source help us define and maintain the source from which we will pull data for the report. I will explain Data source with more clarity once we start developing the report.

e) Datasets: A dataset specifies a query, query parameters, filters, and a field collection. It might be a query, table or stored procedure.
R5Let start to build our report now.

Step 5: Right click on Data Sources -> Add Data Source
R6Step 6: You will see the below screen once you click on Data Source properties. Choose the below depicted options and click on the build button
R7Step 7: Once you click on the build button, the “Connection properties” dialog box will come up. In the server name field, type in the name of the SQL server from where you want to pull data. Once you feed the server name, select the name of the database from where you want to pull data. In our example we have used the [AdventureWorksDW_WroxSSRS2012] database. Click on “Test Connection” button to verify the connection. Click on OK.
R8R9Step 8: On you click on OK you will see the “Data Source Properties” window with the connection string embedded. Click on OK. Now your data source is ready
R10R11Step 9: Now we need to create a new dataset for our report to access.Right Click on “Datasets” -> The Data set properties dialog box pops up -> Under the name type in a name of the dataset. Choose “Use a dataset embedded in my report” option and choose Datasource1 from the dropdown. Select query type as “text” and paste the below query in the text box
SELECT  vResellerSalesProdTerrDate.CalendarYear  ,vResellerSalesProdTerrDate.CalendarQuarter
  ,vResellerSalesProdTerrDate.MonthNumberOfYear  ,vResellerSalesProdTerrDate.MonthName
  ,vResellerSalesProdTerrDate.Category  ,vResellerSalesProdTerrDate.Subcategory
  ,vResellerSalesProdTerrDate.ProductName  ,vResellerSalesProdTerrDate.SalesTerritoryGroup
  ,vResellerSalesProdTerrDate.SalesTerritoryRegion  ,vResellerSalesProdTerrDate.SalesTerritoryCountry
  ,vResellerSalesProdTerrDate.SalesAmt  ,vResellerSalesProdTerrDate.OrderQty
  ,vResellerSalesProdTerrDate.Frieght FROM
  AdventureWorksDW_WroxSSRS2012.dbo.vResellerSalesProdTerrDate

Click on the fields tab below the query tab and you find the fields populated. Click on Ok and you will find your dataset ready. Below images for illustration.
R13R14R15
Step 10: Now Click on the “Insert” tab -> Matrix Wizard
R16Step 11: Once you click on the Matrix Wizard you will find the dataset that you had created few steps back selected. Click on that dataset, click on next
R17Step 12 : Drag and drop the fields as shown in the below illustration. Click Next
R18R19Step 13: Select the Corporate Style in the Styles Tab -> Click Finish
R20Step 14: When you click on finish you will find a template which will have the fields you had selected.Give a name to your report.  Expand the Built-in Fields option and drag the “Page Number” property below the template. Save the report.
R21Now your report is ready. Click on the Run button and your report will open up in IE.
R22I hope this article was helpful in building a basic report from scratch. In my next post i will discuss how to build a report based on parameters.

SQL Server 2008 -How to troubleshoot error: Process 0:0:0 (0x1ebc) Worker 0x00000005393F61A0 appears to be non-yielding on Scheduler 15. Thread creation time: 13022573770856

A few days ago i encountered the below mentioned error message in SQL error logs in one of our production environments
Process 0:0:0 (0x1ebc) Worker 0x00000005393F61A0 appears to be non-yielding on Scheduler 15. Thread creation time: 13022573770856.

I found the following KB Article on MSDN to throw light on this issue. As per the article there was a stack dump that we found in the server.
StackWhat caused the schedulers to be non-responsive:
As per the article the cause of the issue is:
“This issue occurs because the profiling session is not closed correctly. The profiling session is used to calculate the cost of an object to be cached, and is based on memory usage and CPU usage”.

Resolution as Provided by MS in the Kb article for SQL server 2008 is:
Apply CU6 for SP 3.

Our patch level in the environment is beyond that and still this issue happened, so literally there is no solution provided by MS. We would wait and see if this error comes back too often and open a case with MS support.

SCOM -TSQL Script – Find active alerts in a SCOM managed environment

I had to write this query as SCOM dashboard doesn’t always give depict a proper view of the open active alerts in the system. This would be helpful for creating reports for SCOM or for monitoring purposes as to which alerts are open and from which management packs.

DROP TABLE #Base_State
Go
SELECT *
INTO #Base_State
from
(
SELECT aa.Id AS [AlertId],aa.TimeRaised AS [RaisedDateTime],
aa.Severity,aa.ResolutionState,
CASE WHEN aa.ResolutionState < 254 THEN
 'Open' ELSE 'closed' END AS Status
,mp.MPName AS [ManagementPack],
mp.ManagementPackId AS [MPID]
 FROM
OperationsManager.
dbo.AlertView (NOLOCK) aa
 LEFT JOIN
 OperationsManager.dbo.RuleView (NOLOCK) Rv
ON aa.MonitoringRuleId = rv.Id
JOIN OperationsManager.dbo.ManagementPack mp
ON rv.ManagementPackId = mp.ManagementPackId
WHERE aa.Severity = 2 AND
 aa.ResolutionState < 254
union
SELECT aa.Id AS [AlertId],aa.TimeRaised AS [RaisedDateTime],aa.Severity,aa.ResolutionState,
CASE WHEN aa.ResolutionState < 254
 THEN 'Open' ELSE 'closed' END AS STATUS,
mp.MPName AS [ManagementPack],
mp.ManagementPackId AS [MPID]
FROM
OperationsManager.dbo.AlertView (NOLOCK) aa JOIN OperationsManager.dbo.MonitorView (NOLOCK) Mv
ON aa.MonitoringRuleId = Mv.Id
 JOIN OperationsManager.dbo.ManagementPack mp
ON mv.ManagementPackId = mp.ManagementPackId
WHERE aa.Severity = 2 AND
 aa.ResolutionState < 254
UNION
SELECT aa.AlertGuid AS [AlertId],aa.RaisedDateTime,
aa.Severity,ars.ResolutionState,
CASE WHEN ars.ResolutionState < 254 THEN
 'Open' ELSE 'closed' END AS STATUS,
mp.ManagementPackSystemName AS [ManagementPack],
mp.ManagementPackVersionIndependentGuid AS [MPID]
FROM OperationsManagerDW.Alert.vAlert (NOLOCK) AA JOIN OperationsManagerDW.Alert.vAlertresolutionstate (NOLOCK) ARS
ON aa.AlertGuid = ARS.AlertGuid JOIN
OperationsManagerDW.[dbo].vRule (NOLOCK) Vr
ON aa.AlertProblemGuid =  Vr.RuleGuid JOIN OperationsManagerDW.dbo.vManagementPack (NOLOCK) Mp
ON vr.ManagementPackRowId =  mp.ManagementPackRowId
WHERE aa.Severity = 2 AND
ars.ResolutionState = 255
union
SELECT aa.AlertGuid AS [AlertId],aa.RaisedDateTime,
aa.Severity,
ars.ResolutionState,
CASE WHEN
 ars.ResolutionState < 254 THEN
 'Open' ELSE 'closed' END AS STATUS,
mp.ManagementPackSystemName AS [ManagementPack],
mp.ManagementPackVersionIndependentGuid AS [MPID]
FROM OperationsManagerDW.Alert.vAlert (NOLOCK) AA JOIN OperationsManagerDW.Alert.vAlertresolutionstate (NOLOCK) ARS
ON aa.AlertGuid = ARS.AlertGuid JOIN
OperationsManagerDW.[dbo].vMonitor (NOLOCK) Vm
ON aa.AlertProblemGuid =  Vm.MonitorGuid JOIN OperationsManagerDW.dbo.vManagementPack (NOLOCK) Mp
ON Vm.ManagementPackRowId =  mp.ManagementPackRowId
WHERE aa.Severity = 2 AND
 ars.ResolutionState = 255
) AS Base

SELECT bs.AlertId,bs.RaisedDateTime,
bs.ResolutionState,bs.Status,
bs.ManagementPack
FROM #Base_State BS
WHERE Bs.Status = 'Open'

 

Scom

%d bloggers like this: