Archive for the ‘Uncategorized’ Category

Return SQL Server Context Settings from sys.query_context_settings

October 30, 2017 1 comment

Today, I attended Erin Stellato's (b|t) precon session Solving Common Performance Problems Using Query Store. In her presentation, Erin showed that the same query text can return multiple results in Query Store if the context settings for the queries are different. The context settings can be found in the sys.query_context_settings DMV in the set_options column. set_options is stored as a hex value. The following query will return the hex and integer value for each row in the table (NOTE: Query store must be enabled for the database to return values):

USE YourQueryStoreDatabase;

SELECT set_options, CONVERT(INT, set_options) AS IntSetOptions
FROM sys.query_context_settings;

The set_options value represents a bit mask, with each binary digit representing a specific set option. The full list of values can be found here. I created stored procedure ReturnSetOptions to take the IntSetOptions from the query above and return the set options represented. The code for the procedure is listed below.

/* Change database context if you want to create the procedure in
a database other than master */
USE master;


/* =============================================
-- Author:		Frank Gill
-- Create date: 2017-10-30
-- Description:	Stored Procedure to return SET options for a INT context_settings value
-- Sample Execution: EXEC ReturnSetOptions @ContextSettings = 251;
-- =============================================*/
@ContextSettings INT
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.

DROP TABLE IF EXISTS #binaryvalues;

/* Create temp table to hold each binary position and its associated
context setting */
CREATE TABLE #binaryvalues
,BinaryValue INT
,BinaryFlag BIT
,SetOption SYSNAME);

/* Insert context setting information into temp table
Values found here*/
INSERT INTO #binaryvalues
(2,0,N'Parallel Plan'),
--SELECT * FROM #binaryvalues;

/* Declare local variables */

@BinaryValue INT,
@ModValue INT,
@DivValue INT,
@OddValue INT,
@MaxRowId INT,
@BinaryChar CHAR(1),
@BinaryString VARCHAR(MAX) = '';

/* Determine if input value is odd
Used for the one position of the binary value */
SELECT @OddValue = @ContextSettings % 2;

/* Return the largest value and row id from the temp table
less than the input value */
SELECT @RowId = RowId, @BinaryValue = BinaryValue
FROM #binaryvalues
WHERE BinaryValue <= @ContextSettings;

/* Loop backwards through the rows in the temp table
chacking to see if the binary position should be set */
WHILE @RowId > 1

/* Calculate the modulus and dividend for the largest binary value */
SELECT @ModValue = @ContextSettings % @BinaryValue;
SELECT @DivValue = @ContextSettings / @BinaryValue;

/* If the dividend is 1, the current binary position is set to 1*/
IF @DivValue = 1

UPDATE #binaryvalues
SET BinaryFlag = 1
WHERE RowId = @RowId;


/* Update the input value to the modulus for use in the next calculation */
SELECT @ContextSettings = @ModValue;

/* If @ContextSettings is greater than 1 get the previous values from the
temp table
Otherwise, short circuit the loop */
IF @ContextSettings > 1

SELECT @RowId = RowId, @BinaryValue = BinaryValue
FROM #binaryvalues
WHERE BinaryValue <= @ContextSettings;


SELECT @RowId = 1;



/* If the input value is odd, set the 1 position to 1 */
IF @OddValue = 1

UPDATE #binaryvalues
SET BinaryFlag = 1
WHERE BinaryValue = 1;


/* Get the max row with a binary position set */
SELECT @MaxRowId = MAX(RowId)
FROM #binaryvalues
WHERE BinaryFlag = 1;

/* Loop through and concatenate the binary flag values
to generate the binary number equivalent to the input value */
WHILE @MaxRowId >= 1

SELECT @BinaryChar = CAST(BinaryFlag AS CHAR(1))
FROM #binaryvalues
WHERE RowId = @MaxRowId

SELECT @BinaryString = CONCAT(@BinaryString, @BinaryChar)

SELECT @MaxRowId -= 1;


/* Select the binary number */
SELECT @BinaryString AS BinaryValue;

/* Select the set options indicated by the binary number */
SELECT SetOption
FROM #binaryvalues
WHERE BinaryFlag = 1;


Thanks to Erin for an excellent and informative training session.


August 21, 2017 1 comment

Yesterday, I was running a health assessment for a client. They are running a weekly maintenance plan that is shrinking all of their data files. After I picked myself up off the floor, I searched the web for "Paul Randal shrink" and hit on Paul's excellent post Why you should not shrink your data files. In the post, Paul (b|t) demonstrates the effect of DBCC SHRINKDATABASE on index fragmentation. After the demo script, Paul writes, "As well as introducing index fragmentation, data file shrink also generates a lot of I/O, uses a lot of CPU and generates *loads* (emphasis Paul's) of transaction log."

This led me to ask the question, "How much is *loads*?". To find an answer, I made the following modification to Paul's script:

SELECT @maxlsn = CONCAT(N'0x',MAX([Current LSN])) FROM fn_dblog(NULL,NULL);

-- Shrink the database

SELECT SUM([Log Record Length]) AS ShrinkLogSpace
FROM fn_dblog(@maxlsn,NULL);

My additions use the table-valued function fn_dblog to return information from the transaction log.

SELECT @maxlsn = CONCAT(N'0x',MAX([Current LSN])) FROM fn_dblog(NULL,NULL);

The code above returns the log sequence number (LSN) of the last record in the transaction log and stores it in variable @maxlsn.

SELECT SUM([Log Record Length]) AS ShrinkLogSpace
FROM fn_dblog(@maxlsn,NULL);

This code uses @maxlsn from the first query to return all log records resulting from the DBCC SHRINKDATABASE command, and return the total log space used in bytes.  In Paul's demo script, there is 10MB of space available to shrink when DBCC SHRINKDATABASE runs.  The total log space used by the shrink is 16MB.

So, in this example, *loads* is 60% of the space you are reclaiming with the shrink operation.  Yet another reason to not shrink your data files.

Many thanks to Paul for his post.

Categories: Uncategorized

My SQL Saturday Season Begins

January 26, 2017 Leave a comment

We are nearly a month into the new year and my SQL Saturday season is about to start. I will be presenting Creating an Azure SQL Database Using PowerShell.  This will be my third year in Cleveland and I am looking forward to another great time. You can find more information and register here.

SQL Saturday Chicago is taking place Saturday, March 11. I will be helping organize and run the event, so I will not be presenting. I did that a few years ago, and it is just too many balls in the air. The event is already at capacity, but you can add yourself to the waitlist here.  If you sign up for either pre-conference session, you can send your registration receipt to and you will be registered for SQL Saturday. The pre-conference sessions are Tuning Your Biggest Queries with Adam Machanic and Complete Primer for SQL Server Infrastructure and the Cloud.

Finally, SQL Saturday Madison takes place Saturday, April 8. This will be my fifth straight time in Madison and they put on an amazing event. The venue is in the heart of the University of Wisconsin campus and is one of the more beautiful SQL Saturdays I've been to. You can find more information and register here.

If these events aren't geographically desirable, you can find other SQL Saturday events worldwide here. If you do make it to any of the three, be sure to say hello. Meeting new people is one of the best parts of SQL Saturdays.

Categories: Uncategorized