Author Archives: nakulvachhrajani

Unknown's avatar

About nakulvachhrajani

Nakul Vachhrajani is a SQL Server MCTS, TOGAF certified Technical Architect with Capgemini having a total IT experience of more than 16 years. Nakul is an active blogger, and has been a guest columnist for SQLAuthority.com and SQLServerCentral.com. Nakul presented a webcast on the “Underappreciated Features of Microsoft SQL Server” at the Microsoft Virtual Tech Days Exclusive Webcast series (May 02-06, 2011) on May 06, 2011. He is also the author of a research paper on Database upgrade methodologies, which was published in a CSI journal, published nationwide. In addition to his passion about SQL Server, Nakul also contributes to the academia out of personal interest. He visits various colleges and universities as an external faculty to judge project activities being carried out by the students. Disclaimer: The opinions expressed herein are his own personal opinions and do not represent his employer’s view in anyway.

#0302 – SQL Server – Myths – ORDER BY does not impact performance


One of the most common mistakes that I see developers do is the use the ORDER BY clause in every stored procedure – irrespective of whether the application can sort the data independently or not. There have been many posts written on this topic, and I wanted to do my bit to spread awareness by busting this myth.

The truth is that the ORDER BY clause consumes precious server resources. As we know, the ORDER BY clause is applied after the select. What I mean by this is that once the result set of a query is generated, SQL Server will then perform the sort. In order to do the sort operation, SQL Server will need to use some temporary disk space from the tempdb and also consume processor time. Just imagine the amount of resources being consume to service hundreds of concurrent connections – each requiring a sorted output.

Here’s a simple example, executed by keeping the “Actual Execution plan” ON:

USE AdventureWorks2012;
GO
--Query without ORDER BY
SELECT hredh.BusinessEntityID,
       hre.JobTitle,
       hredh.DepartmentID,
       hrd.Name,
       hredh.StartDate,
       hredh.EndDate,
       hredh.ShiftID,
       hrs.Name
FROM HumanResources.Shift AS hrs 
INNER JOIN HumanResources.EmployeeDepartmentHistory AS hredh ON hredh.ShiftID = hrs.ShiftID
INNER JOIN HumanResources.Employee AS hre ON hredh.BusinessEntityID = hre.BusinessEntityID
INNER JOIN HumanResources.Department AS hrd ON hredh.DepartmentID = hrd.DepartmentID;
GO

USE AdventureWorks2012;
GO
--Query with ORDER BY
SELECT hredh.BusinessEntityID,
       hre.JobTitle,
       hredh.DepartmentID,
       hrd.Name,
       hredh.StartDate,
       hredh.EndDate,
       hredh.ShiftID,
       hrs.Name
FROM HumanResources.Shift AS hrs 
INNER JOIN HumanResources.EmployeeDepartmentHistory AS hredh ON hredh.ShiftID = hrs.ShiftID
INNER JOIN HumanResources.Employee AS hre ON hredh.BusinessEntityID = hre.BusinessEntityID
INNER JOIN HumanResources.Department AS hrd ON hredh.DepartmentID = hrd.DepartmentID
ORDER BY hredh.DepartmentID;
GO

image

As can be seen from the execution plan above, the query with the ORDER BY consumes more processing time (it increased from 46% to 54% of the batch) due to the introduction of the SORT operator.

Conclusion

Most applications only need pre-sorted output in about 20% of the cases. In all other cases, the sort operations should be avoided – SQL Server works best when processing the entire set of records, so why sort them?

Until we meet next time,

Be courteous. Drive responsibly.

#0301 – SQL Server – SSMS – Identify disabled indexes


As I was going through the forums and the #sqlhelp Twitter feed yesterday, I came across a very interesting question:

Is there a way to verify whether an index is disabled in SSMS?

The short and simple answer is: No direct indicator exists in the SQL Server Management Studio (SSMS) to indicate whether an index is enabled or disabled.

To cross-check, I used the disabled index scripts (refer Further Reading Section below) to disable a non-clustered and the clustered index on the Sales.Store table of the AdventureWorks2012 sample database.

The Index Properties window confirms that the indexes are indeed disabled.
image
image
This is what the Object Explorer looks like:

As can be seen from the screenshot below, the icons for the indexes are the same – before and after the indexes were disabled.

image
Even the Object Explorer Details window does not show any indication and/or column to indicate that the indexes are disabled
image

This is one of the many cases where T-SQL scripts prove to be way more powerful than the SSMS UI. While I do not know the real reason why this particular discrepancy exists, I believe it is for performance reasons (the more meta-data that is accessed and refreshed on the UI, the more resources it would take resulting into an overall decline in performance).

Further Reading

  • ALTER INDEX – Enable & Disable Indexes [Link]
  • Disable Clustered Index and Data Insert [Link]
  • Disable Index & Update Statistics [Link]

(*All the above mentioned links are from Pinal Dave, a.k.a. “SQLAuthority” [B|T]’s blog)

Until we meet next time,

Be courteous. Drive responsibly.

#0300 – SQL Server – Table Hints – WITH (NOLOCK) does not propagate through computed column expressions


There are many myths related to performance tuning in SQL Server. One of the top-ranking myths is that using the WITH (NOLOCK) table hint is a silver bullet to 80% of the performance problems that an application might be facing. However, the WITH (NOLOCK) may not even be in effect in all situations.


If a table contains a computed column, then the table hints on the table do not propagate through to the functions and expressions used to define that computed column. Let’s study this statement with the help of an example.


Demo


For the purposes of this demo, let us first create a table with a computed column. We will be using the [dbo].[ufnGetProductDealerPrice] scalar function available in the AdventureWorks2012 sample database to populate the computed column used in this demo.

USE AdventureWorks2012;
GO
SET NOCOUNT ON;

IF OBJECT_ID(‘dbo.ProductDealerInformationByOrder’,’U’) IS NOT NULL
DROP TABLE dbo.ProductDealerInformationByOrder;
GO

CREATE TABLE dbo.ProductDealerInformationByOrder
(ProductId INT,
OrderDate DATETIME,
DealerListPrice AS (dbo.[ufnGetProductDealerPrice](ProductId, OrderDate))
);
GO

–Populate some test data
INSERT INTO dbo.ProductDealerInformationByOrder (ProductId, OrderDate)
SELECT sod.ProductID,
soh.OrderDate
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID
WHERE soh.SalesOrderID = 51721;
GO


The scenario:


The scalar function – [dbo].[ufnGetProductDealerPrice] references two tables:



  • Production.Product
  • Production.ProductListPriceHistory

To check whether the NOLOCK hint is propagated through the computed column and affects the tables referred inside the function, we will be updating some of the values in the table: Production.ProductListPriceHistory  inside a transaction and then attempting to select data from our test table – dbo.ProductDealerInformationByOrder by using the WITH (NOLOCK) table hint. If the table hint does propagate through the scalar function to the Production.ProductListPriceHistory table, then the query should return some data (it would be uncommitted data).


Step 01: Run the update to Production.ProductListPriceHistory table


The script below performs the update to the Production.ProductListPriceHistory and lists out all the locks that have been acquired by the transaction. Notice that we have neither committed nor rolled back the transaction, so the locks continue to remain in effect.

USE AdventureWorks2012;
GO
— Note the session number!
BEGIN TRANSACTION LockProductListPriceHistory
UPDATE plph
SET plph.ListPrice = (plph.ListPrice + 10)
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID
INNER JOIN Production.ProductListPriceHistory AS plph ON sod.ProductID = plph.ProductID
WHERE soh.SalesOrderID = 51721;

–Check the nature of the locks applied
SELECT sdtl.request_type AS LockRequeust,
sdtl.request_status AS LockRequestStatus,
sdtl.request_mode AS LockMode,
sdtl.resource_type AS ObjectType,
DB_NAME(sdtl.resource_database_id) AS DatabaseName,
SCHEMA_NAME(so.schema_id) AS SchemaName,
so.name AS ObjectName,
sdtl.request_session_id AS SessionNumber
FROM sys.dm_tran_locks AS sdtl
INNER JOIN sys.objects AS so ON sdtl.resource_associated_entity_id = so.object_id;
–ROLLBACK TRANSACTION LockProductListPriceHistory


The output on my test server is shown below.


image


Step 02: Select Data from test table


The next step now is to select data from our test table, from a new query editor window in SSMS (so that we begin a new session).

/* Run this in a new SSMS query editor window. */
USE AdventureWorks2012;
GO
BEGIN TRANSACTION SelectWithNoLock
;WITH ProductInfoCTE (ProductId, OrderDate, DealerListPrice)
AS (SELECT pdio.ProductId,
pdio.OrderDate,
pdio.DealerListPrice
FROM dbo.ProductDealerInformationByOrder AS pdio WITH (NOLOCK)
)
SELECT DISTINCT sdtl.request_type AS LockRequest,
sdtl.request_status AS LockRequestStatus,
sdtl.request_mode AS LockMode,
sdtl.resource_type AS ObjecType,
DB_NAME(sdtl.resource_database_id) AS DatabaseName,
SCHEMA_NAME(so.schema_id) AS SchemaName,
so.name AS ObjectName,
sdtl.request_session_id AS SessionNumber,
pcte.ProductId,
pcte.OrderDate,
pcte.DealerListPrice
FROM sys.dm_tran_locks AS sdtl
INNER JOIN sys.objects AS so ON sdtl.resource_associated_entity_id = so.object_id
CROSS JOIN ProductInfoCTE AS pcte;
ROLLBACK TRANSACTION SelectWithNoLock
GO

What we observe is that the query continues to execute without any results being returned (Note down the session Id, in this case, 55):


image


Step 03: Examine the locks acquired


Clearly, the query is being locked by another process or task. Hence, we will open up another query editor window in SSMS and execute the query provided below:

/* Run this in a new SSMS query editor window (window #3). */
USE AdventureWorks2012;
GO
SELECT sdtl.request_type AS LockRequeust,
sdtl.request_status AS LockRequestStatus,
sdtl.request_mode AS LockMode,
sdtl.resource_type AS ObjectType,
DB_NAME(sdtl.resource_database_id) AS DatabaseName,
SCHEMA_NAME(so.schema_id) AS SchemaName,
so.name AS ObjectName,
sdtl.request_session_id AS SessionNumber,
sr.blocking_session_id AS BlockingSession
FROM sys.dm_tran_locks AS sdtl
INNER JOIN sys.objects AS so ON sdtl.resource_associated_entity_id = so.object_id
LEFT OUTER JOIN sys.dm_exec_requests AS sr ON sdtl.request_session_id = sr.session_id
ORDER BY sr.session_id;
GO

Here is the output from my test server:


image


As can be seen from the screenshot, we can see that the WITH (NOLOCK) query from the select statement from session #55 has requested Sch-S (Schema stability) locks on the dbo.ProductDealerInformationByOrder and the scalar function – dbo.ufnGetProductDealerPrice  indicating that the hint is in effect for the objects directly referenced on the query.


However, it is requesting IS (Intent Shared) locks on the Production.Product and Production.ProductListPriceHistory tables. Unfortunately, the Production.ProductListPriceHistory is being locked by an IX (Intent Exclusive) lock from the session #52 thereby blocking session #55.


Running the ROLLBACK TRANSACTION statement in session #52 unblocks session #55 and results are finally returned.


Hence proving that,



If a table has computed columns that are computed by functions and expressions that refer other objects, the table hints on the parent table are not propagated to the objects referred by these functions and expressions.


Further Reading



  • Myths – Using WITH (NOLOCK) also works on INSERT/UPDATE/DELETE statements [Link]
  • Books-On-Line/MSDN: Table Hints in SQL Server [Link]
  • Pinal Dave, a.k.a. “SQLAuthority” (B|T): What Kind of Lock WITH (NOLOCK) Hint Takes on Object? [Link]

Until we meet next time,


Be courteous. Drive responsibly.

#0299 – SQL Server – Myths – Using WITH (NOLOCK) also works on INSERT/UPDATE/DELETE statements


Today’s post was prompted by a recent code review experience. I was called upon to code review a data cleanup script. Coming directly to the interesting part, I noticed that the developer had put the WITH (NOLOCK) table hint when consuming the tables in an UPDATE statement. The resulting conversation is transcribed below:


Me: We will need to remove the WITH (NOLOCK) table hint from the UPDATE statements in this script.


Developer: Why? Isn’t the WITH (NOLOCK) supposed to acquire no locks on the database?


Me: That’s not true. For the SQL Server to create a query plan and execute it, SQL Server will have to request at least a Schema stability (Sch-S) lock. You can read about this from Pinal Dave, a.k.a. “SQLAuthority” (B|T)’s post on “What Kind of Lock WITH (NOLOCK) Hint Takes on Object?


Developer: Okay, I get it now. So, we can use to reduce the locking in our database with the WITH (NOLOCK) statement. Wouldn’t we want to reduce locking as much as possible always?


Me: Yes, we may want to keep locks to a minimum, but that’s only during SELECT statements. This is because systems are typically 80% read, 20% write systems. An update would require an exclusive lock, and hence the WITH (NOLOCK) would be ignored by SQL Server. Let me show you an example:


Below are two UPDATE queries – one which uses a NOLOCK statement, and the other which does not. To allow us to track the nature of the locks acquired, I have wrapped them inside of a transaction.

USE AdventureWorks2012;
GO
–Check the locks acquired during a normal UPDATE
BEGIN TRANSACTION NormalUpdate
UPDATE hredh
SET hredh.ShiftID = 2
FROM HumanResources.EmployeeDepartmentHistory AS hredh
WHERE hredh.BusinessEntityID = 215;

–Check the nature of the locks applied
SELECT sdtl.request_type,
sdtl.request_status,
sdtl.request_mode,
sdtl.resource_type,
DB_NAME(sdtl.resource_database_id) AS DatabaseName
FROM sys.dm_tran_locks AS sdtl;
ROLLBACK TRANSACTION NormalUpdate
GO

USE AdventureWorks2012;
GO
–Check the locks acquired during an UPDATE WITH (NOLOCK)
BEGIN TRANSACTION UpdateWithNolock
UPDATE hredh
SET hredh.ShiftID = 2
FROM HumanResources.EmployeeDepartmentHistory AS hredh WITH (NOLOCK)
WHERE hredh.BusinessEntityID = 215;

–Check the nature of the locks applied
SELECT sdtl.request_type,
sdtl.request_status,
sdtl.request_mode,
sdtl.resource_type,
DB_NAME(sdtl.resource_database_id) AS DatabaseName
FROM sys.dm_tran_locks AS sdtl;
ROLLBACK TRANSACTION UpdateWithNolock
GO


Comparing the result sets of both these queries shows us that the same number and nature of locks were acquired by SQL Server for both queries during the UPDATE.











































































































request_type request_status Qry1
request_mode
Qry2
request_mode
Qry1
resource_type
Qry2
resource_type
DatabaseName
LOCK GRANT S S DATABASE DATABASE AdventureWorks2012
LOCK GRANT S S DATABASE DATABASE AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT IX IX PAGE PAGE AdventureWorks2012
LOCK GRANT IX IX PAGE PAGE AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT IX IX PAGE PAGE AdventureWorks2012
LOCK GRANT X X KEY KEY AdventureWorks2012
LOCK GRANT IX IX OBJECT OBJECT AdventureWorks2012

In fact, the official MSDN/Books-On-Line page for the UPDATE statement [Link] clearly states:



“NOLOCK and READUNCOMMITTED are not allowed”


If you do specify the WITH (NOLOCK) table hint, it is conveniently ignored by the database engine.


Developer: I guess the understanding of the entire team was incorrect. Thank-you, Nakul for explaining the detailed reason behind the code review comment.


Me: You are most welcome! Please note that Microsoft has already marked the use of NOLOCK in INSERT, UPDATE and DELETE statements as a deprecated feature. Hence, while today the database engine ignores the hint, it will produce an error in one of the future versions.


Further Reading:



  • Pinal Dave, a.k.a. “SQLAuthority” (B|T): What Kind of Lock WITH (NOLOCK) Hint Takes on Object? [Link]
  • Books-On-Line/MSDN: Table Hints in SQL Server [Link]
  • Books-On-Line/MSDN: Update Statement [Link]

Important Note


[Added: 10/01/2013, 10:15AM IST]


Please note that using WITH (NOLOCK) on the target table in a complex UPDATE statement may cause corruption in the non-clustered indexes in SQL Server 2008. Please refer https://support.microsoft.com/kb/2878968/en-us for details. Thank-you, Chintak for drawing attention to this KB article.


Until we meet next time,


Be courteous. Drive responsibly.


[Edit: 09/30/2013, 10:55AM IST – A schema-stability lock is abbreviated as Sch-S, not Sch-M.]

#0298 – SQL Server – complex CHECK constraints


We were recently working on an implementation and had to introduce a couple of CHECK constraints to maintain logical integrity of the database with respect to the business rules. As we were going through the table design, one of my colleagues asked a very interesting question:

Most of our constraints are quite simple, mostly range checks which involve a maximum of two columns. Is it possible to have a complex CHECK constraint which involves more than two columns?

The answer, quite simply is Yes! CHECK constraints can be complex, provided they continue to be an expression that evaluates to a Boolean value.

Demo

I will take a sample table derived from my post earlier in the week:

USE tempdb ;
GO
--Safety Check
IF OBJECT_ID('dbo.ComplexCheckConstraint', 'U') IS NOT NULL 
    DROP TABLE dbo.ComplexCheckConstraint ;
GO

--Create the temporary table
CREATE TABLE dbo.ComplexCheckConstraint
    (
      StartDate DATE,
      StartTime TIME,
      EndDate DATE,
      EndTime TIME,
    ) ;
GO

Now, let us create a complex CHECK constraint on the table:

USE tempdb;
GO
--Add the complex constraint
ALTER TABLE dbo.ComplexCheckConstraint
    ADD CONSTRAINT chk_EndDate CHECK ( (EndDate > StartDate) 
                                       OR (EndDate = StartDate AND 
                                           EndTime > StartTime) 
                                     );
GO

Note that we have five operators (3 comparison operators and 2 logical operators) and four columns (EndDate, StartDate, EndTime and StartTime). To check if the constraint works or not, let us attempt to insert some invalid data into the test table:

USE tempdb;
GO
--Attempt to insert some invalid data
INSERT  INTO dbo.ComplexCheckConstraint
        (
          StartDate,
          StartTime,
          EndDate,
          EndTime
        )
VALUES  (
          '2013-09-19',
          '08:00',
          '2013-09-19',
          '07:00'
        ) ;
GO

We get the following error, proving that the complex table constraint was created and is in effect.

Msg 547, Level 16, State 0, Line 2

The INSERT statement conflicted with the CHECK constraint "chk_EndDate". The conflict occurred in database "tempdb", table "dbo.ComplexCheckConstraint".

The statement has been terminated.

Attempting to insert valid data works fine and does not produce a violation.

Summary

CHECK constraints help to maintain logical consistencies in the database. They can help validate data with respect to a given pattern and also help in ensuring that the data in a column is well within the acceptable limits as defined by the business – irrespective of the complexity of the requirement.

Further Reading:

  • Using Regular Expressions with CHECK constraints [Link]
  • Defining CHECK constraints on computed columns [Link]

Until we meet next time,

Be courteous. Drive responsibly.