Procure SQL is proud to be speaking and sponsoring SQL Saturday Jacksonville this weekend. Join us and several other amazing community speakers as we all share our knowledge with the community. The event will be on Saturday, May 14th, 2022, at University of North Florida. You can sign up for free here. We look forward to seeing you there!

SQL Saturday Jacksonville 2022

Today I heard a great analogy, and it inspired this post. Execution plans are like flight plans, there are multiple flight paths that can be taken to get from where you are to your ultimate destination. Some plans are more efficient than others, and we want to use those whenever possible. How can I make sure I am using the plan I want to use? Let’s talk about air traffic control, the Query Store.

What is the Query Store?

Starting in SQL Server 2016, Query Store is a native tool that tracks query execution data including execution statistics and execution plans. Query store gives you insight to SQL Server performance issues. It provides graphical views in SSMS and dynamic management views to assist in identifying problem queries.

Reasons to give it a try?

  • Knowing what queries are the most expensive
  • Seeing all query executions and whether or not they have regressed based on code changes
  • Ability to force specific plans for problem queries

Turning it on is a no brainer for many environments, but if your workload is mostly ad hoc you may not see the same benefit as environments that focus more on stored procedure execution

Getting Started with Query Store

Installation

Since this is a database level setting and not a server level setting, you will have to perform these actions on each database to enable the Query Store. After the initial work to get this enabled, you do have the option to enable it on the model system database and every new database that gets created will also get this setting enabled so you don’t have to worry about doing it later. There are two ways to get Query Store enabled in your environment.

The fastest way to get this enabled on a long list of databases would be to use T-SQL or PowerShell. For PowerShell, I highly recommend utilizing dbatools.io. It’s free and full of amazing documentation.

ALTER DATABASE [StackOverflow2010] SET QUERY_STORE = ON; 
ALTER DATABASE [StackOverflow2013] SET QUERY_STORE = ON; 
Enable Query Store with T-SQL
T-SQL
Set-DbaDbQueryStoreOption -SqlInstance ServerA -Database StackOverflow2010, StackOverflow2013 -State ReadWrite
Enabling Query Store with DBATools.io and PowerShell.
PowerShell using dbatools.io/Set-DbaDbQueryStoreOption

You can also use the user interface to enable this feature. Right click the database and then open the properties. From here, change the operation mode from off to read write. We will go over additional settings to consider in the section below that can also be updated in this view.

Enabling Query Store with SSMS Graphical interface.
UI

Settings to Consider

To implement any of the below settings, you can use the UI like the screenshot above, dbatools for PowerShell, or T-SQL (which I will include below)

Max Size is exactly what it sounds like, a limit to how large you want your query store to grow. Most of the settings below will affect how much space is required, but a good default to work from would be 1GB, adjusting based on the below settings. If the query store runs out of space, it will go from read write to read only and stop gathering query metrics.

Data Flush Interval is used to control how often Query Store goes from memory and gets persisted to disk. When considering this setting, think about how much data you are willing to lose from query store if you have a DR situation arise.

Statistics Collection Interval decides how often to collect metrics. The more often you collect affects how much space you need, but also provides greater levels of granularity when looking into issues in the environment.

Stale Query Threshold controls how often to clean up old data and inactive queries. This setting directly impacts how much space is required for query store.

Query Store Capture Mode determines what queries are captured in query store. Typically this is set to All if you want to capture every query or to Auto if you are ok with SQL ignoring insignificant queries based on execution counts, compile and run times.

Well it’s on, now what?

Stay tuned for additional blogs in my Query Store series to cover basic built in reporting, using query store to assist in tuning, and more advanced features!

The post Query Store 101 appeared first on dbWonderKid.

Quoted Identifier Set Incorrectly?! What Is Going On?

Quoted Identifier Set Incorrectly getting you down? I recently ran into an issue when I set up a job to collect information from an extended event. I wanted to write that to a table in my scratch DBA database. This allowed the customer and I to slice and dice data with ease. This seemed easy enough. I am no stranger to creating these tables and pushing information to them via SQL Server Agent jobs. My job was failing though with the error below saying I have the incorrect SET option for QUOTED_IDENTIFIER.

QUOTED_IDENTIFIER error message on SQL Server Agent Job
QUOTED_IDENTIFIER Error Message

Backing Up A Step, What Is QUOTED IDENTIFIER?

Set to ON by default, QUOTED_IDENTIFIER allows use any word as an object identifier so long as it is delimited by quotes (“) or brackets ([]). If set to OFF, restricted keywords can’t be used as an identifier (such as name, description, etc). There are a few cases where you need to have QUOTED_IDENTIFIER set to ON, but the one we are going to focus on for this blog is “SET QUOTED_IDENTIFIER must be ON when you invoke XML data type methods.” Extended event data is stored in XEL files (which is just a variant of XML), so QUOTED_IDENTIFIER must be set to ON.

QUOTED IDENTIFIER Back To The Investigation!

So the setting is ON by default but best to not assume, I’d hate to make an ASS out of U and ME. There are a couple ways to check to make sure your setting is on for your target table. The easiest way to find out is right clicking the table and going to the properties. You will see the SET options under the Options section. You can also script the table to see the SET option for QUOTED_IDENTIFIER.

Table properties highlighting QUOTED_IDENTIFIER settings
Table properties showing QUOTED_IDENTIFIER

The configuration is correct, but we still receive the same error. I tried dropping and recreating the table a couple of times but it didn’t fix the issue. In a swing for the fences effort, I tried to explicitly call out the SET operation. Different articles in my research called it out before statements as they wanted to show examples of using the setting. I set QUOTED_IDENTIFIER to ON in-line on the SQL Server Agent job code right below table creation and setting variables but before the INSERT statement. The below code would allow you to create a table if it doesn’t exist, delete data that is older than 30 days, and insert new items into the table.

IF NOT EXISTS (SELECT 1 FROM DBA_Admin.sys.objects WHERE name = 'TestTable')
BEGIN
	CREATE TABLE DBA_Admin.dbo.TestTable
	(
		[ts] [datetime],
		[event_name] [nvarchar](256),
		[username] [nvarchar](1000),
		[client_hostname] [nvarchar](1000),
		[client_app_name] [nvarchar](1000),
		[database_name] [nvarchar](300),
		[sql] [nvarchar](max)
	)
END

select min(ts), max(ts) from DBA_Admin.dbo.TestTable

DELETE FROM DBA_Admin.dbo.TestTable
WHERE ts < DATEADD(day,-30,GETDATE())

DECLARE @MaxDate DATETIME

SELECT @MaxDate = MAX(ts) 
FROM DBA_Admin.dbo.TestTable

SELECT CAST(event_data as xml) AS event_data 
INTO #cte
FROM sys.fn_xe_file_target_read_file('ExtendedEventName*.xel', null, null, null)

SET QUOTED_IDENTIFIER ON

INSERT INTO DBA_Admin.dbo.TestTable
SELECT ts = event_data.value(N'(event/@timestamp)[1]', N'datetime')
	,event_name  = event_data.value(N'(event/@name)[1]', N'nvarchar(256)')
	,[username] = event_data.value(N'(event/action[@name="username"]/value)[1]', N'nvarchar(1000)')
	,[client_hostname] = event_data.value(N'(event/action[@name="client_hostname"]/value)[1]', N'nvarchar(1000)')
	,[client_app_name] = event_data.value(N'(event/action[@name="client_app_name"]/value)[1]', N'nvarchar(1000)')
	,[database_name] = event_data.value(N'(event/action[@name="database_name"]/value)[1]', N'nvarchar(300)')
	,[sql] = 
		CASE 
			WHEN event_data.value(N'(event/data[@name="statement"]/value)[1]', N'nvarchar(max)') IS NULL 
				THEN event_data.value(N'(event/data[@name="batch_text"]/value)[1]', N'nvarchar(max)') 
			ELSE event_data.value(N'(event/data[@name="statement"]/value)[1]', N'nvarchar(max)') 
		END
FROM #cte
	CROSS APPLY #cte.event_data.nodes(N'/event') AS x(ed)
WHERE event_data.value(N'(event/@timestamp)[1]', N'datetime') > @MaxDate

QUOTED IDENTIFIER Set Incorrectly Conclusion

This issue was a testament to not giving up on difficult troubleshooting. You need to dot all of the I’s and cross all T’s and not throw away an idea before trying it. I could not find an article anywhere where someone had my exact problem. Every article was showing things at a more basic level of someone having the setting OFF instead of ON. I hope this helps someone else and saves them the hours of a headache! If you have questions or even an explanation for why I experienced this issue, I would love to hear from you!

The post QUOTED_IDENTIFIER Set Incorrectly?! appeared first on dbWonderKid.

Getting Started with Performance Tuning
Watch! Getting Started with Performance Tuning

Have slow queries and want to get down to the basics of learning some fundamentals for performance tuning? If so, John Sterrett is giving an talk at the Nashville User Group (Online) tomorrow. Feel free to join us. If you are local to Nashville make sure you sign up for their future meetings.

Join us virtually on Thursday, April 14, 2022 from 10:45 AM to 12:00 PM CDT

Tuning select statements is less complicated than tuning update statements because your code changes do not change data in your tables. Tuning update statements on the other hand can be tricky as you are changing data while you are testing. See multiple ways on how you can streamline tuning update statements to quickly get to your starting point to make sure you are comparing apples to apples.

You are out doing your scheduled sweeps of the environment and you notice a huge I/O consumer is one of your update procedures. Your initial thought may be to panic because how can you test data that is changing? You have several methods to tuning queries that adjust data that allow data to revert between tests.

NOTE: We always recommend tuning all queries in non-production until we have our final tested solution to implement in production.

Tuning Update Statement with Rollback Transaction

One of the more basic methods for executing queries without changing data is to wrap the statements are you running in a BEGIN TRANSACTION and ROLLBACK TRANSACTION. This allows everything to execute and give you the performance statistics you are looking for, while leaving the data intact to allow for repeat testing.

Tuning Update Statements using Explicit Transactions. This is BEGIN TRANSACTION and ROLLBACK or COMMIT TRANSACTION.
Using rollback to revert a change made to data

Tuning Update Statements using Database Backups

You have a couple options with your standard SQL Server backups to assist in reverting changes made to the testing environment and the same concepts are applied to your basic DR strategy. If the test is short, you can take a full backup before making a change and restore that backup to restart a test.

Tuning Update Statements with Restoring Full Backups. You can use a full backup to revert a change while tuning.
Using a full backup to revert a data change

If you are making a series of changes over a period of time and don’t want to completely restart your test, you can utilize point in time recovery. Once your database is in full recovery, you can start with a full backup and take log backups at set intervals or before different pieces of your change. This allows you to restore to a specific point in your test.

Tuning Update Statements with using Point in time recovery with full and log backups for tuning
Using a full backup with transaction log backups for Point in Time Recovery

Tuning Update Statements with Database Snapshot

Database Snapshots are a great way to quickly revert large databases to an initial state. Database Snapshots keep a record of database pages as they change and the copy of the page so those extents can be reverted instead of restoring the whole database. Therefore, they require the associated source database as it is not a full copy of the database. Snapshots contain the original version of files of the source database and a pointer file that shows the changes since the snapshot was taken, called a sparse file. The source database must be available for snapshots to be usable. This makes them not an ideal DR solution, but great for analysis in auditing and in tuning situations.

Tuning Update Statements while using snapshots to revert a data change for tuning
Using 2016 Database Snapshots to revert a change

Summary

There are many ways to quickly get back to your original state to allow for quick repetitive testing for queries that change data. A huge benefit of the methods I listed today allow you to also document where transactions and backups occur so you can open a .sql file and have all the needed steps in one document. Do you like other ways to revert changes? Maybe temporal tables? I’d be happy to cover that in a post in the near future. Tell me all about your methods in the comments below!

The post Tuning Basics – Updating an Update appeared first on dbWonderKid.