Click here to monitor SSC
Av rating:
Total votes: 25
Total comments: 0


Fabiano Amorim
Showplan Operator of the week - Assert
14 April 2010

As part of his mission to explain the Query Optimiser in practical terms, Fabiano attempts the feat of describing, one week at a time, all the major Showplan Operators used by SQL Server's Query Optimiser to build the Query Plan. He starts with Assert

Well my friends, I was wondering how to help you to understand execution plans in a practical way. So I decided that I'd talk about the Showplan Operators.

Showplan Operators are used by the Query Optimizer (QO) to build the query plan in order to perform a specified operation. A query plan will consist of many physical operators. The Query Optimizer uses a simple language that represents each physical operation by an operator, and each operator is represented in the graphical execution plan by an icon.

I’ll try to talk about one operator every week, but so as to avoid having to continue to write about these operators for years, I’ll mention only of those that are more common: The first being the Assert.

The Assert is used to verify a certain condition, it validates a Constraint on every row to ensure that the condition was met. If, for example, our DDL includes a check constraint which specifies only two valid values for a column, the Assert will, for every row, validate the value passed to the column to ensure that input is consistent with the check constraint.

Assert  and Check Constraints:

Let’s see where the SQL Server uses that information in practice. Take the following T-SQL:

IF OBJECT_ID('Tab1'IS NOT NULL
  
DROP TABLE Tab1
GO
CREATE TABLE Tab1(ID IntegerGender CHAR(1)) 
GO 
ALTER TABLE TAB1 ADD CONSTRAINT ck_Gender_M_F CHECK(Gender IN('M','F')) 
GO
INSERT INTO Tab1(IDGenderVALUES(1,'X')
GO

To the command above the SQL Server has generated the following execution plan:

As we can see, the execution plan uses the Assert operator to check that the inserted value doesn’t violate the Check Constraint. In this specific case, the Assert applies the rule, ‘if the value is different to “F” and different to “M” than return 0 otherwise returns NULL’.

The Assert operator is programmed to show an error if the returned value is not NULL; in other words, the returned value is not a “M” or “F”.

Assert checking Foreign Keys

Now let’s take a look at an example where the Assert is used to validate a foreign key constraint. Suppose we have this  query:

ALTER TABLE Tab1 ADD ID_Genders INT
GO 
IF OBJECT_ID('Tab2'IS NOT NULL
  
DROP TABLE Tab2
GO
CREATE TABLE Tab2(ID Integer PRIMARY KEYGender CHAR(1)) 
GO 
INSERT INTO Tab2(IDGenderVALUES(1'F')
INSERT INTO Tab2(IDGenderVALUES(2'M')
INSERT INTO Tab2(IDGenderVALUES(3'N')
GO 
ALTER TABLE Tab1 ADD CONSTRAINT fk_Tab2 FOREIGN KEY (ID_GendersREFERENCES Tab2(ID)
GO 
INSERT INTO Tab1(IDID_GendersGenderVALUES(14'X')

Let’s look at the text execution plan to see what these Assert operators were doing. To see the text execution plan just execute SET SHOWPLAN_TEXT ON before run the insert command.

|--Assert(WHERE:(CASE WHEN NOT [Pass1008] AND [Expr1007] IS NULL THEN (0) ELSE NULL END)) 
     
|--Nested Loops(Left Semi Join, PASSTHRU:([Tab1].[ID_Genders] IS NULL), OUTER REFERENCES:([Tab1].[ID_Genders]), DEFINE:([Expr1007] = [PROBE VALUE]))
          |--Assert(WHERE:(CASE WHEN [Tab1].[Gender]<>'F' AND [Tab1].[Gender]<>'M' THEN (0) ELSE NULL END))
          
|    |--Clustered Index Insert(OBJECT:([Tab1].[PK]), SET:([Tab1].[ID] = RaiseIfNullInsert([@1]),[Tab1].[ID_Genders] = [@2],[Tab1].[Gender] = [Expr1003]), DEFINE:([Expr1003]=CONVERT_IMPLICIT(char(1),[@3],0)))
          
|--Clustered Index Seek(OBJECT:([Tab2].[PK]), SEEK:([Tab2].[ID]=[Tab1].[ID_Genders]) ORDERED FORWARD)

Here we can see the Assert operator twice, first (looking down to up in the text plan and the right to left in the graphical plan) validating the Check Constraint. The same concept showed above is used, if the exit value is “0” than keep running the query, but if NULL is returned shows an exception.

The second Assert is validating the result of the Tab1 and Tab2 join. It is interesting to see the “[Expr1007] IS NULL”. To understand that you need to know what this Expr1007 is, look at the Probe Value (green text) in the text plan and you will see that it is the result of the join. If the value passed to the INSERT at the column ID_Gender exists in the table Tab2, then that probe will return the join value; otherwise it will return NULL. So the Assert is checking the value of the search at the Tab2; if the value that is passed to the INSERT is not found  then Assert will show one exception.

If the value passed to the column ID_Genders is NULL than the SQL can’t show a exception, in that case it returns “0” and keeps running the query.

If you run the INSERT above, the SQL will show an exception because of the “X” value, but if you change the “X” to “F” and run again, it will show an exception because of the value “4”. If you change the value “4” to NULL, 1, 2 or 3 the insert will be executed without any error.

Assert checking a SubQuery:

The Assert operator is also used to check one subquery. As we know, one scalar subquery can’t validly return more than one value: Sometimes, however, a  mistake happens, and a subquery attempts to return more than one value . Here the Assert comes into play by validating the condition that a scalar subquery returns just one value.

Take the following query:

INSERT INTO Tab1(ID_TipoSexoSexoVALUES((SELECT ID_TipoSexo FROM Tab1), 'F'
  
INSERT INTO Tab1(ID_TipoSexoSexoVALUES((SELECT ID_TipoSexo FROM Tab1), 'F'
|--Assert(WHERE:(CASE WHEN NOT [Pass1016] AND [Expr1015] IS NULL THEN (0) ELSE NULL END))  
       |--Nested Loops(Left Semi Join, PASSTHRU:([tempdb].[dbo].[Tab1].[ID_TipoSexo] IS NULL), OUTER REFERENCES:([tempdb].[dbo].[Tab1].[ID_TipoSexo]), DEFINE:([Expr1015] = [PROBE VALUE])) 
            
|--Assert(WHERE:([Expr1017])) 
            |    |--Compute Scalar(DEFINE:([Expr1017]=CASE WHEN [tempdb].[dbo].[Tab1].[Sexo]<>'F' AND [tempdb].[dbo].[Tab1].[Sexo]<>'M' THEN (0) ELSE NULL END)) 
            
|         |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[Tab1].[PK__Tab1__3214EC277097A3C8]), SET:([tempdb].[dbo].[Tab1].[ID_TipoSexo] = [Expr1008],[tempdb].[dbo].[Tab1].[Sexo] = [Expr1009],[tempdb].[dbo].[Tab1].[ID] = [Expr1003])) 
            
|              |--Top(TOP EXPRESSION:((1))) 
            
|                   |--Compute Scalar(DEFINE:([Expr1008]=[Expr1014], [Expr1009]='F')) 
            
|                        |--Nested Loops(Left Outer Join) 
            
|                             |--Compute Scalar(DEFINE:([Expr1003]=getidentity((1856985942),(2),NULL))) 
            
|                             |    |--Constant Scan 
            
|                             |--Assert(WHERE:(CASE WHEN [Expr1013]>(1) THEN (0) ELSE NULL END)) 
            
|                                  |--Stream Aggregate(DEFINE:([Expr1013]=Count(*), [Expr1014]=ANY([tempdb].[dbo].[Tab1].[ID_TipoSexo]))) 

            |                                       |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[Tab1].[PK__Tab1__3214EC277097A3C8])) 
            
|--Clustered Index Seek(OBJECT:([tempdb].[dbo].[Tab2].[PK__Tab2__3214EC27755C58E5]), SEEK:([tempdb].[dbo].[Tab2].[ID]=[tempdb].[dbo].[Tab1].[ID_TipoSexo]) ORDERED FORWARD) 

You can see from this text showplan that SQL Server as generated a Stream Aggregate to count how many rows the SubQuery will return, This value is then passed to the Assert which then does its job by checking its validity.

Is very interesting to see that  the Query Optimizer is smart enough be able to avoid using assert operators when they are not necessary. For instance:

INSERT INTO Tab1(ID_TipoSexoSexoVALUES((SELECT ID_TipoSexo FROM Tab1 WHERE ID 1), 'F')

INSERT INTO Tab1(ID_TipoSexoSexoVALUES((SELECT TOP 1 ID_TipoSexo FROM Tab1), 'F'

For both these INSERTs, the Query Optimiser is smart enough to know that only one row will ever be returned, so there is no need to use the Assert.

Well, that’s all folks, I see you next week with more “Operators”.



This article has been viewed 5143 times.
Fabiano Amorim

Author profile: Fabiano Amorim

Fabiano is fascinated by the SQL Server Query Processor and the way it works to optimize queries, procedures and functions. He graduated as a Technical Processor from Colégio Bezerra de Menezes, SP- Brazil, and has worked for several years with SQL Server, focusing in creating Data Warehouses and optimizing T-SQL codes for many companies in Brazil and Argentina. Fabiano is a SQL Server MVP, MCP for SQL Server 2000, MCTS and MCITP Data Base Developer for SQL Server 2005 and 2008. He also is actively involved in SQL Server community though forums such as MSDN and TechNet Brazil, writes articles for Simple-Talk and SQL Server Magazine Brazil, and he also presents online Webcasts and In-Person events for Microsoft Brazil. His blog is on http://blogfabiano.com

Search for other articles by Fabiano Amorim

Rate this article:   Avg rating: from a total of 25 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.
 










Phil Factor
Automated Script-generation with Powershell and SMO
 In the first of a series of articles on automating the process of building, modifying and copying SQL Server... Read more...



 View the blog
Using SQL Test Database Unit Testing with TeamCity Continuous Integration
 With database applications, the process of test and integration can be frustratingly slow because so... Read more...

SQL Source Control: The Development Story
 Often, there is a huge difference between software being easy to use, and easy to develop. When your... Read more...

How to Import Data from HTML pages
 It turns out that there are plenty of ways to get data into SQL Server from websites, whether the data... Read more...

SQL Scripts Manager: An Appreciation
 SQL Scripts Manager is Simple-Talk's present to its readers. William Brewer was an enthusiastic... Read more...

Hosted Team Foundation Server 2010 Review
 Team Foundation Server (TFS) has expanded its remit to support the whole software development process,... Read more...

Beginning SQL Server 2005 Reporting Services Part 1
 Steve Joubert begins an in-depth tour of SQL Server 2005 Reporting Services with a step-by-step guide... Read more...

Ten Common Database Design Mistakes
 Database design and implementation is the cornerstone of any data centric project (read 99.9% of... Read more...

Reading and Writing Files in SQL Server using T-SQL
 SQL Server provides several "standard" techniques by which to read and write to files but, just... Read more...

Beginning SQL Server 2005 Reporting Services Part 2
 Continuing his in-depth tour of SQL Server 2005 Reporting Services, Steve Joubert demonstrates the most... Read more...

Creating CSV Files Using BCP and Stored Procedures
 Nigel Rivett demonstrates some core techniques for extracting SQL Server data into CSV files, focussing... Read more...

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk