Showing posts with label SQL Performance. Show all posts
Showing posts with label SQL Performance. Show all posts

Tuesday, 7 February 2012

Getting started with SQL Server Management Objects (SMO)

Getting started with SQL Server Management Objects (SMO)

 

ProblemSQL Server 2005 and 2008 provide SQL Server Management Objects (SMO), a collection of namespaces which in turn contain different classes, interfaces, delegates and enumerations, to programmatically work with and manage a SQL Server instance. SMO extends and supersedes SQL Server Distributed Management Objects (SQL-DMO) which was used for SQL Server 2000. In this tip, I am going to discuss how you can get started with SMO and how you can programmatically manage a SQL Server instance with your choice of programming language.

SolutionAlthough SQL Server Management Studio (SSMS) is a great tool to manage a SQL Server instance there might be a need to manage your SQL Server instance programmatically.
For example, consider you are developing a build deployment tool, this tool will deploy the build but before that it needs to make sure that the SQL Server and SQL Server Agent services are running, a database is available and online. For this kind of work, you can use SMO, a SQL Server API object model.
The SMO object model represents SQL Server as a hierarchy of objects. On top of this hierarchy is the Server object, beneath it resides all the instance classes.
SMO classes can be categorized into two categories:
  • Instance classes - SQL Server objects are represented by instance classes. It forms a hierarchy that resembles the database server object hierarchy. On top of this hierarchy is Server and under this there is a hierarchy of instance objects that include: databases, tables, columns, triggers, indexes, user-defined functions, stored procedures etc. I am going to demonstrate the usage of a few instance classes in this tip in the example section below.
  • Utility classes - Utility classes are independent of the SQL Server instance and perform specific tasks. These classes have been grouped on the basis of its functionalities. For example Database scripting operations, Backup and restore databases, Transfer schema and data to another database etc. I will discussing the utility classes in my next tip.
How SMO is different from SQL-DMO
SMO object model is based on managed code and implemented as .NET Framework assemblies. It provides several benefits over traditional SQL-DMO along with support for new features introduced with SQL Server 2005 and SQL Server 2008.
For example
  • It offers improved performance by loading an object only when it is referenced, even the objects properties are loaded partially on object creation and left over objects are loaded only when they are directly referenced.
  • It groups the T-SQL statements into batches to improve network performance.
  • It now supports several new features like table and index partitioning, Service Broker, DDL triggers, Snapshot Isolation and row versioning, Policy-based management etc.
An exhaustive list of the comparisons between SQL-DMO and SMO can be found here.
Example
Before you start writing your code using SMO, you need to take reference of several assemblies which contain different namespaces to work with SMO. To add a reference of these assemblies, go to Solution Browser - > References -> Add Reference. Add these commonly used assemblies. 
  • Microsoft.SqlServer.ConnectionInfo.dll
  • Microsoft.SqlServer.Smo.dll
  • Microsoft.SqlServer.SmoEnum.dll
  • Microsoft.SqlServer.SqlEnum.dll
  • Microsoft.SqlServer.Management.Sdk.Sfc.dll // on SQL Server/VS 2008 only
 
There are a couple of other assemblies which contain namespaces for certain tasks, but few of them are essential to work with SMO. Some of the frequently used namespaces and their purposes are summarized in the below table, other namespaces are used for specific tasks like working with SQL Server Agent where you would reference Microsoft.SqlServer.Management.Smo.Agent etc.
Namespaces Purpose
Microsoft.SqlServer.Management.Common It contains the classes which you will require to make a connection to a SQL Server instance and execute Transact-SQL statements directly.
Microsoft.SqlServer.Management.Smo This is the basic namespace which you will need in all SMO applications, it provides classes for core SMO functionalities. It contains utility classes, instance classes, enumerations, event-handler types, and different exception types.
Microsoft.SqlServer.Management.Smo.Agent  It provides the classes to manage the SQL Server Agent, for example to manage Job, Alerts etc.
Microsoft.SqlServer.Management.Smo.Broker It provides classes to manage Service Broker components using SMO.
Microsoft.SqlServer.Management.Smo.Wmi  It provides classes that represent the SQL Server Windows Management Instrumentation (WMI). With these classes you can start, stop and pause the services of SQL Server, change the protocols and network libraries etc.
C# Code Block 1Here I am using the Server instance object to connect to a SQL Server. You can specify authentication mode by setting the LoginSecure property. If you set it to "true", windows authentication will be used or if you set it to "false" SQL Server authentication will be used.
With Login and Password properties you can specify the SQL Server login name and password to be used when connecting to a SQL Server instance when using SQL Server authentication.
C# Code Block 1 - Connecting to server
Server myServer = new Server(@"ARSHADALI\SQL2008");
//Using windows authenticationmyServer.ConnectionContext.LoginSecure = true;
myServer.ConnectionContext.Connect();
////
//Do your work
////
if (myServer.ConnectionContext.IsOpen)
myServer.ConnectionContext.Disconnect();
//Using SQL Server authenticationmyServer.ConnectionContext.LoginSecure = false;
myServer.ConnectionContext.Login = "SQLLogin";
myServer.ConnectionContext.Password = "entry@2008";
C# Code Block 2Once a connection has been established to the server, I am enumerating through the database collection to list all the database on the connected server. Then I am using another instance class Database which represents the AdventureWorks database. Next I am enumerating through the table, stored procedure and user-defined function collections of this database instance to list all these objects. Finally I am using the Table instance class which represents the Employee table in the AdventureWorks database to enumerate and list all properties and corresponding values.
C# Code Block 2 - retrieving databases, tables, SPs, UDFs and Properties
//List down all the databases on the server
foreach (Database myDatabase in myServer.Databases)
{
Console.WriteLine(myDatabase.Name);
}
Database myAdventureWorks = myServer.Databases["AdventureWorks"];
//List down all the tables of AdventureWorks
foreach (Table myTable in myAdventureWorks.Tables)
{
Console.WriteLine(myTable.Name);
}
//List down all the stored procedures of AdventureWorksforeach (StoredProcedure myStoredProcedure in myAdventureWorks.StoredProcedures)
{
Console.WriteLine(myStoredProcedure.Name);
}
//List down all the user-defined function of AdventureWorks
foreach (UserDefinedFunction myUserDefinedFunction in myAdventureWorks.UserDefinedFunctions)
{
Console.WriteLine(myUserDefinedFunction.Name);
}
//List down all the properties and its values of [HumanResources].[Employee] tableforeach (Property myTableProperty in myServer.Databases["AdventureWorks"].Tables["Employee", 
"HumanResources"].Properties)
{
Console.WriteLine(myTableProperty.Name + " : " + myTableProperty.Value);
}
C# Code Block 3This demonstrates the usage of SMO to perform DDL operations.
First I am checking the existence of a database, if it exists dropping it and then creating it.
Next I am creating a Table instance object, then creating Column instance objects and adding it to the created Table object. With each Column object I am setting some property values.
Finally I am creating an Index instance object to create a primary key on the table and at the end I am calling the create method on the Table object to create the table.
C# Code Block 3 - Creating a database and table
//Drop the database if it existsif(myServer.Databases["MyNewDatabase"] != null)
myServer.Databases["MyNewDatabase"].Drop();
//Create database called, "MyNewDatabase"Database myDatabase = new Database(myServer, "MyNewDatabase");
myDatabase.Create();
//Create a table instanceTable myEmpTable = new Table(myDatabase, "MyEmpTable");
//Add [EmpID] column to created table instanceColumn empID = new Column(myEmpTable, "EmpID", DataType.Int);
empID.Identity = true;
myEmpTable.Columns.Add(empID);
//Add another column [EmpName] to created table instanceColumn empName = new Column(myEmpTable, "EmpName", DataType.VarChar(200));
empName.Nullable = true;
myEmpTable.Columns.Add(empName);
//Add third column [DOJ] to created table instance with default constraintColumn DOJ = new Column(myEmpTable, "DOJ", DataType.DateTime);
DOJ.AddDefaultConstraint(); // you can specify constraint name here as well
DOJ.DefaultConstraint.Text = "GETDATE()";
myEmpTable.Columns.Add(DOJ);
// Add primary key index to the tableIndex primaryKeyIndex = new Index(myEmpTable, "PK_MyEmpTable");
primaryKeyIndex.IndexKeyType = IndexKeyType.DriPrimaryKey;
primaryKeyIndex.IndexedColumns.Add(new IndexedColumn(primaryKeyIndex, "EmpID"));


myEmpTable.Indexes.Add(primaryKeyIndex);
//Unless you call create method, table will not created on the server myEmpTable.Create();
Result:
 
The complete code listing (created using SQL Server 2008 and Visual Studio 2008, although there is not much difference if you are using SQL Server 2005 and Visual Studio 2005) can be found in the below text box.

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Smo;
namespace LearnSMO2008
{
    class Program
    {
        static void Main(string[] args)
        {
            Server myServer = new Server(@"ARSHADALI\SQL2008");
            try
            {
                //Using windows authentication
                myServer.ConnectionContext.LoginSecure = true;
                //Using SQL Server authentication
                //myServer.ConnectionContext.LoginSecure = false;
                //myServer.ConnectionContext.Login = "SQLLogin";
                //myServer.ConnectionContext.Password = "entry@2008";
                myServer.ConnectionContext.Connect();
                //AccessingSQLServer(myServer);
                DatabaseObjectCreation(myServer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (myServer.ConnectionContext.IsOpen)
                    myServer.ConnectionContext.Disconnect();
                Console.ReadKey();
            }
           
        }
        private static void AccessingSQLServer(Server myServer)
        {
            //List down all the databases on the server
            foreach (Database myDatabase in myServer.Databases)
            {
                Console.WriteLine(myDatabase.Name);
            }
            Database myAdventureWorks = myServer.Databases["AdventureWorks"];
            //List down all the tables of AdventureWorks
            foreach (Table myTable in myAdventureWorks.Tables)
            {
                Console.WriteLine(myTable.Name);
            }
            //List down all the stored procedures of AdventureWorks
            foreach (StoredProcedure myStoredProcedure in myAdventureWorks.StoredProcedures)
            {
                Console.WriteLine(myStoredProcedure.Name);
            }
            //List down all the user-defined function of AdventureWorks
            foreach (UserDefinedFunction myUserDefinedFunction in myAdventureWorks.UserDefinedFunctions)
            {
                Console.WriteLine(myUserDefinedFunction.Name);
            }
            //List down all the properties and its values of [HumanResources].[Employee] table
            foreach (Property myTableProperty in myServer.Databases["AdventureWorks"].Tables["Employee",
                "HumanResources"].Properties)
            {
                Console.WriteLine(myTableProperty.Name + " : " + myTableProperty.Value);
            }
        }
        private static void DatabaseObjectCreation(Server myServer)
        {
            //Drop the database if it exists
            if(myServer.Databases["MyNewDatabase"] != null)
                myServer.Databases["MyNewDatabase"].Drop();
            //Create database called, "MyNewDatabase"
            Database myDatabase = new Database(myServer, "MyNewDatabase");
            myDatabase.Create();
            //Create a table instance
            Table myEmpTable = new Table(myDatabase, "MyEmpTable");
            //Add [EmpID] column to created table instance
            Column empID = new Column(myEmpTable, "EmpID", DataType.Int);
            empID.Identity = true;
            myEmpTable.Columns.Add(empID);
            //Add another column [EmpName] to created table instance
            Column empName = new Column(myEmpTable, "EmpName", DataType.VarChar(200));
            empName.Nullable = true;
            myEmpTable.Columns.Add(empName);
            //Add third column [DOJ] to created table instance with default constraint
            Column DOJ = new Column(myEmpTable, "DOJ", DataType.DateTime);
            DOJ.AddDefaultConstraint(); // you can specify constraint name here as well
            DOJ.DefaultConstraint.Text = "GETDATE()";
            myEmpTable.Columns.Add(DOJ);
            // Add primary key index to the table
            Index primaryKeyIndex = new Index(myEmpTable, "PK_MyEmpTable");
            primaryKeyIndex.IndexKeyType = IndexKeyType.DriPrimaryKey;
            primaryKeyIndex.IndexedColumns.Add(new IndexedColumn(primaryKeyIndex, "EmpID"));
            myEmpTable.Indexes.Add(primaryKeyIndex);
            //Unless you call create method, table will not created on the server           
            myEmpTable.Create();
        }


    }
}


Notes:
  • If you have an application written in SQL-DMO and want to upgrade it to SMO, that is not possible, you will need to rewrite your applications using SMO classes.
  • SMO assemblies are installed automatically when you install Client Tools.
  • Location of assemblies in SQL Server 2005 is C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies folder.
  • Location of assemblies in SQL Server 2008 is C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies folder.
  • SMO provides support for SQL Server 2000 (if you are using SQL Server 2005 SMO it supports SQL Server 7.0 as well) but a few namespaces and classes are not supported in prior versions.

Hiding System Objects in Object Explorer in SQL Server Management Studio

Hiding System Objects in Object Explorer in SQL Server Management Studio

 

Problem

While looking through the new features and improvements in SQL Server Management Studio (SSMS) we found a potentially interesting one to Hide System Objects in Object Explorer in SQL Server Management Studio. In this tip we will take a look at how to Hide System Objects in Object Explorer.

Solution

You may have noticed that once you are connected to a SQL Server Instance using SQL Server Management Studio, in the Databases node of Object Explorer you can see system objects such as the system databases as shown in the snippet below.
how to hide system objects in object explorer in ssms
You can hide system objects in Object Explorer by following the below mentioned steps:
1. In SQL Server Management Studio, under Tools menu, click Options as shown in the snippet below.
go to tools in ssms
2. In the Options dialog box, expand Environment and then select the General tab as shown in the snippet below. Select Hide system objects in Object Explorer and then click OK.
select the genral tab in ssms
3. In the Microsoft SQL Server Management Studio dialog box, click OK to acknowledge that the changes will come into effect once you restart SQL Server Management Studio.
you must restart sql server management studio
4. Next, go ahead and close SQL Server Management Studio. When you reopen SQL Server Management Studio once you are connected to SQL Server Instance you will not see the System Objects in Object Explorer in SQL Server Management Studio as shown in the snippet below.
when you reopen ssms  once you are connected to sql server instance it will look like the snippet below

 

SSMS keyboard shortcuts

SSMS keyboard shortcuts

 

Problem

As DBA and Developer responsibilities grow on a daily basis, we how can we improve our productivity? Due to the time it takes to use the toolbar, menu bar or mouse, do I have any other options? Are there keyboard shortcut for SQL Server Management Studio? Could these help me improve my productivity? Check out this tip to learn more.

Solution

We often overlook different SSMS shortcut keys which provide a boost in DBA and Developer productivity. In the second tip of this series (SQL Server Management Studio keyboard shortcuts - Part 1), I am going to further explain shortcut keys for managing Intellisence, debugging, running your code and many more. Let's jump right in.

SQL Server Management Studio Intellisense

SQL Server 2008 and later versions include IntelliSense which let's you know about objects in the database, supports T-SQL syntax, gives the parameter info of the stored procedures or functions. Although IntelliSense works by default (you can enable or disable it as and when required), there are times when you want to manually list a set of objects or members. This can be accomplished by pressing CTRL+J as shown below:
sql server management studio intellisense
Stored procedures and functions normally accept parameters, when you want to execute a stored procedure it is good to know the name, data type and number of parameters to pass it for execution. To accomplish this, press CTRL+SHIFT+SPACE after the stored procedure name to view the list parameters as shown below:
ssms shortcut ctrl+shift+space
To minimize the typing effort, you can use the auto complete feature of IntelliSense. Simply press ALT+RIGHT ARROW and it will complete your word by the best and first matching member list. If it finds more than one item it shows a list as shown below to choose one. You can also press CTRL+SPACE to bring up this list.
use the auto complete feature of intellisense
When we first connect to the database, list members are queried and stored in the local cache for better performance. Unfortunately, if someone creates additional objects, those new objects will not be listed. When this occurs, you need to refresh the local cache. Refreshing the local cache can be accomplished by pressing the CTRL+SHIFT+R keys or you can also perform this actions using the menu bar (Edit | IntelliSense | Refresh Local Cache) shown below:
refreshing the local cache in ssms

Execute Scripts in SQL Server Management Studio

Now lets move on and see how to execute scripts using the shortcut keys. If you want to parse all the scripts without executing them in the current query window, simply press CTRL+F5 or you can select some lines of code to be parsed then press CTRL+F5. If you want to execute all the scripts in the current query window simply press F5 or you can select some lines of code to be executed then press F5. After the execution a result pane appears on the bottom section of SSMS. You can press CTRL+R to toggle the result pane as shown below. As you can notice in the image below it has a Results tab which shows the result sets returned after execution of the query and a Messages tab which shows all the messages generated/printed during execution, for example number of records affected etc.
execute scripts in sql sever management studio
By default the result set is displayed in the grid format, but you can change it to display as text or send the results directly to a file. To change the setting to display results as text, simply press CTRL+T before execution and result would display as shown below next time when you execute your script. To send the results to a file press CTRL+SHIFT+F before script execution. If you want to revert the setting back to return the results in a grid you can press CTRL+D before script execution and next time you will see your results in the grid.
changing the results display

Query Execution Plans in SQL Server Management Studio

During query analysis you might need to know the execution plan of a query even before executing it. To find this out, press CTRL+L to display the estimated query execution plan as shown below without actually running the whole query (in fact it runs for top 1 row).
query execution plans in ssms
On the other hand, you might need more detailed information and review the query execution plan with the query execution results. To capture this information, press CTRL+M to include the actual execution plan as shown below with the query results. You can even press SHIFT+ALT+S to include the client statistics with the query results.
review the query execution plan with the query execution results
If for some reason you would like to cancel a long running query, for that hit ALT+BREAK to cancel the current query execution.

SQL Server Management Studio Debugging

In SQL Server Management Studio, you can debug scripts. To start debugging from the first line press ALT+F5. To toggle a breakpoint on the line simply traverse to that line and press F9. To delete all the breakpoints from the query window at once, press CTRL+SHIFT+F9. A breakpoint is denoted by a tiny red circle placed on the left side of the query window as shown below:
ssms debugging
To manage all the breakpoints in one place, SSMS has Breakpoint window as similar to the Bookmark window. To launch the Breakpoint window press CTRL+ALT+B and you will see one window as shown below:
ssms has breakpoint window
While debugging you can press F11 to step into the called module or F10 to step over the called module. You can also press F5 to go to the next breakpoint instead of going one statement at a time.
Sometimes when you are working on a long script in the SSMS query window, it might appear small for you, in that case you can hit SHIFT+ALT+ENTER to toggle the query window in full screen mode as you can see below:
a long script in the ssms query window might appear small
FFor help, you can select the object and press ALT+F1 to display the meta information about that object stored in the database as shown below.
press alt+f1 to display the meta information
If you are looking for help in Books Online you can select the keyword and press F1 to launch BOL with information about that selected keyword if it exists.

Accessing common windows in SQL Server Management Studio

Here are the shortcut keys for common SSMS windows:
  • F8 - Object Explorer
  • CTRL+ALT+T - Template Explorer
  • CTRL+ALT+L - Solution Explorer
  • F4 - Properties window
  • CTRL+ALT+G - Registered Servers explorer

Standard shortcut keys

Please note apart from the above mentioned shortcut keys here are some standard shortcut keys which work fine in SSMS:
  • CTRL+A - Select all the text in the current query window
  • CTRL+C - Copy text in the current query window
  • CTRL+V - Paste text in the current query window
  • CTRL+X - Cut text in the current query window
  • DEL - Delete text in the current query window
  • CTRL+P - Launch the print dialog box
  • CTRL+HOME - Top of the current query window
  • CTRL+END - End of the query window
  • CTRL+SHIFT+HOME - Select all the text from the current location to the beginning of the query window
  • CTRL+SHIFT+END - Select all the text from the current location to the end of the query window
  • ALT+F4 - Close the SSMS application

 

Simple script to backup all SQL Server databases

ProblemSometimes things that seem complicated are much easier then you think and this is the power of using T-SQL to take care of repetitive tasks.  One of these tasks may be the need to backup all databases on your server.   This is not a big deal if you have a handful of databases, but I have seen several servers where there are 100+ databases on the same instance of SQL Server.  You could use Enterprise Manager to backup the databases or even use Maintenance Plans, but using T-SQL is a much simpler and faster approach.
SolutionWith the use of T-SQL you can generate your backup commands and with the use of cursors you can cursor through all of your databases to back them up one by one.  This is a very straight forward process and you only need a handful of commands to do this. 
Here is the script that will allow you to backup each database within your instance of SQL Server.  You will need to change the @path to the appropriate backup directory and each backup file will take on the name of "DBnameYYYDDMM.BAK".
DECLARE @name VARCHAR(50-- database name  DECLARE @path VARCHAR(256-- path for backup files  DECLARE @fileName VARCHAR(256-- filename for backup  DECLARE @fileDate VARCHAR(20-- used for file name
SET @path 'C:\Backup\' 
SELECT @fileDate CONVERT(VARCHAR(20),GETDATE(),112)
DECLARE db_cursor CURSOR FOR 
SELECT 
name FROM master.dbo.sysdatabases WHERE name NOT IN ('master','model','msdb','tempdb'
OPEN db_cursor   FETCH NEXT FROM db_cursor INTO @name  
WHILE @@FETCH_STATUS 0   BEGIN  
       SET 
@fileName @path @name '_' @fileDate '.BAK' 
       
BACKUP DATABASE @name TO DISK = @fileName 

       
FETCH NEXT FROM db_cursor INTO @name   END  

CLOSE 
db_cursor   DEALLOCATE db_cursor
In this script we are bypassing the system databases, but these could easily be included as well.  You could also change this into a stored procedure and pass in a database name or if left NULL it backups all databases.  Any way you choose to use it, this script gives you the starting point to simply backup all of your databases.

Wednesday, 25 January 2012

Using Indexed Computed Columns to Improve Performance

Using Indexed Computed Columns to Improve Performance


I recently read a blog post on doing case-insensitive text searches on SQL Server 2005. The post said that an index on a computed column might be used even if the computed column itself wasn't used in the WHERE clause. I was curious to test that and see how far I might take it. Years ago I worked on a case-sensitive application and I vividly remember all the headaches that caused me. I was also curious to see if I could use that for datetime columns to strip off the time portion and easily do a "date-only" search.
I started with a copy of a table from AdventureWorks.
USE [AdventureWorks]
GO
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES 
   WHERE TABLE_NAME = 'NewContact'
   AND TABLE_SCHEMA = 'Person')
 DROP TABLE [Person].[NewContact]
GO
CREATE TABLE [Person].[NewContact]  (
 [ContactID] [int] PRIMARY KEY NOT NULL,
 [Title] [nvarchar](8) COLLATE SQL_Latin1_General_CP1_CS_AS NULL,
 [FirstName] nvarchar(50) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL,
 [LastName] nvarchar(50) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL,
 [Suffix] [nvarchar](10) COLLATE SQL_Latin1_General_CP1_CS_AS NULL,
 [EmailAddress] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CS_AS NULL,
 [ModifiedDate] [datetime] NOT NULL  DEFAULT (getdate())  )
GO
INSERT INTO [Person].[NewContact] ( [ContactID], [Title], [FirstName],
 [LastName], [Suffix], [EmailAddress], [ModifiedDate] )
select ContactID, Title, FirstName, LastName, Suffix, EmailAddress, 
 ModifiedDate = DATEADD(mi, ContactID, ModifiedDate)
FROM Person.Contact
GO
CREATE INDEX IX_NewContact_LastName ON Person.NewContact(LastName);
GO
CREATE INDEX IX_NewContact_ModifiedDate ON Person.NewContact(ModifiedDate) 
GO
This creates a copy of the contacts table with all the NVARCHAR columns set to case-sensitive. It also creates indexes on the LastName column and DateModified column. I tested a series of SELECT statements against the table to establish a baseline.
The SELECT statements and the results are:


Statement Base index Upper Index CI Index
1. SELECT * FROM Person.NewContact WHERE LastName = 'Mcanich' Index seek, query cost = 0.007, returned one row Index seek, query cost = 0.007, returned one row Index seek, query cost = 0.007, returned one row
2. SELECT * FROM Person.NewContact WHERE LastName = 'mcanich' Index seek, query cost = 0.007, returned zero rows Index seek, query cost = 0.007, returned zero rows Index seek, query cost = 0.007, returned zero rows
3. SELECT * FROM Person.NewContact WHERE LastName = 'MCANICH' COLLATE SQL_Latin1_General_CP1_CI_AI Index scan, query cost = .127, returned one row Index scan, query cost = 0.129, returned one row Index seek of CI index, query cost = 0.007, returned one row
4. SELECT * FROM Person.NewContact WHERE UPPER(LastName) = UPPER('McAnich') Index scan, query cost = 0.08, returned one row Index seek of "Upper" index, query cost = 0.007, returned one row Index seek of "Upper" index, query cost = 0.007, returned one row
5. SELECT * FROM Person.NewContact WHERE LastNameUpper = UPPER('McAnich') (Failed) Index seek of "Upper" index, query cost = 0.007, returned one row Index seek of "Upper" index, query cost = 0.007, returned one row
6. SELECT * FROM Person.NewContact WHERE LastNameCI = 'mcanICh' (Failed) (Failed) Index seek of CI index, query cost = 0.007, returned one row
The first set of tests is in the "Base Index" column. When I compared the column to a scalar value it did an index seek (1 and 2). Query #3 specified a case-insensitive search using the COLLATE clause and that resulted in an very slow index scan. Query #4 converted them both to upper case and then compared them. This also resulted in a query scan but not nearly as bad. Still this had a cost ten times higher than the base query. One of the things I discuss in my performance tuning presentations is that any time you wrap a function around an indexed column it probably won't use the index efficiently. This seems to confirm that.
Next I created a computed column with the UPPER function and then built an index on it.
ALTER TABLE Person.NewContact
  ADD LastNameUpper AS UPPER(LastName)
GO
CREATE INDEX IX_NewContact_LastNameUpper ON Person.NewContact(LastNameUpper)
GO
 
The results of this are in the "Upper Index" column above. In Query #5 when we use the computed column it does an index seek on the new index just like I'd hoped it would. The really interesting result is Query #4. That also uses the new index and does an index seek even though we aren't using the new computed column. What the blog post says and what seems to be happening is that SQL Server is checking for a computed column that matches the WHERE clause. It finds it, finds that it's indexed and uses the index. Pretty cool if you ask me!
I also tested this by creating a case-insensitve computed column and testing that.
ALTER TABLE Person.NewContact
  ADD LastNameCI AS LastName COLLATE SQL_Latin1_General_CP1_CI_AI
GO
CREATE INDEX IX_NewContact_LastNameCI ON Person.NewContact(LastNameCI)
GO   
The results of this test are in the "CI Index" column in the table above. This performed just like the other computed column. Query #6 which explicitly used the column performed very well. But so did Query #3 which didn't explicitly use the new column.
I also wanted to test this on datetime functions to see if I could easily query just on the date portion of the column. The queries I tested are:


Statement Base index Date Only Index
1. SELECT * FROM Person.NewContact WHERE ModifiedDate >= '5/1/2003' AND ModifiedDate < '5/2/2003' Index seek, query cost = 0.03, eleven rows returned Index seek, query cost = 0.03, eleven rows returned
2. SELECT * FROM Person.NewContact WHERE ModifiedDate = '5/1/2003' Index seek, query cost = 0.006, zero rows returned Index seek, query cost = 0.006, zero rows returned
3. SELECT * FROM Person.NewContact WHERE CONVERT(VARCHAR(10), ModifiedDate, 101) = '05/01/2003' Index scan, query cost = 0.07, eleven rows returned Index scan, query cost = 0.07, eleven rows returned
4. SELECT * FROM Person.NewContact WHERE CONVERT(DATETIME, CONVERT(VARCHAR(10), ModifiedDate, 101), 101) = '5/1/2003' Index scan, query cost = 0.07, eleven rows returned Index seek on new index, query cost = 0.04, eleven rows returned
5. SELECT * FROM Person.NewContact WHERE ModifiedDateOnly = '5/1/2003' (Failed) Index seek on new index, query cost = 0.04, eleven rows returned
6. SELECT * FROM Person.NewContact WHERE CAST(CONVERT(VARCHAR(10), ModifiedDate, 101) AS DATETIME) = '5/1/2003' Index scan, query cost = 0.07, eleven rows returned Index scan, query cost = 0.07, eleven rows returned
7. SELECT * FROM Person.NewContact WHERE CONVERT(DATETIME, CONVERT(VARCHAR(10), ModifiedDate, 101)) = '5/1/2003' Index scan, query cost = 0.07, eleven rows returned Index scan, query cost = 0.07, eleven rows returned
Notice that in the script that created the NewContact table I added some minutes to each ModifiedDate. I also created an index on ModifiedDate. The first query is the preferred way of selecting one days worth of data. It does an index seek and is the fastest way to return data. The second query is a little faster but doesn't return any data. The comparison of a date-only value to a date with time value with always fail. The third query is the way I see this type of query written most frequenty. The column is converted to VARCHAR and then compared to the value. In the fourth query the column is converted back to datetime.
Next I added a computed column and an index on that column.
ALTER TABLE Person.NewContact
 ADD ModifiedDateOnly AS CONVERT(DATETIME, CONVERT(VARCHAR(10), ModifiedDate, 101), 101) 
GO
CREATE INDEX XI_NewContact_ModifiedDateOnly ON Person.NewContact(ModifiedDateOnly)
GO
This created a computed column that had the time removed from ModifiedDate. When I converted back to DATETIME I had to specifiy the format number (101) or SQL Server complained that the function wasn't deterministic. Deterministic functions always return the same value from the same input value and database state. For example, GETDATE() isn't deterministic.
The biggest improvement came in Query #4. Since the function around ModifiedDate matched the computed column it used the computed column and its index. It didn't use it in Query #6 where the outer CONVERT was replaced with a CAST function. When test Query #4 but added a bunch of white space into the WHERE clause it still used the index. That tells it isn't doing a simple hash of the text in the WHERE clause. When I changed from VARCHAR(10) to any other length it no longer used the new index. Query #7 also didn't use the new index. The only difference in that query was the outer CONVERT function didn't have a format number.
If you have existing application code that wraps functions around an indexed column and then doesn't use the index you may be able to use this approach to improve those queries. If the functions are consistent you should be able to add computed columns and realize immediate benefits.

 

 

  Post By Bill Graziano

 

SQL Server Version

I'm continually trying to track down what service packs are installed on various SQL Servers I support. I can never find the right support page on Microsoft's site. So here's an article with all the SQL Server version information I can track down. If you know of any older versions or can help me fill out any missing data, please post in the comments and I'll update the article.
Article Body
SQL Server 2008 R2
10.50.2789.0 SQL Server 2008 R2 SP1 CU3 17 Oct 2011
10.50.2772.0 SQL Server 2008 R2 SP1 CU2 16 Aug 2011
10.50.2769.0 SQL Server 2008 R2 SP1 CU1 16 Sep 2011
10.50.2500.0 SQL Server 2008 R2 SP1 11 Jul 2011
10.50.1807.0 SQL Server 2008 R2 CU10 19 Oct 2011
10.50.1804.0 SQL Server 2008 R2 CU9 23 Aug 2011
10.50.1797.0 SQL Server 2008 R2 CU8 16 Sep 2011
10.50.1777.0 SQL Server 2008 R2 CU7 16 Jun 2011
10.50.1765.0 SQL Server 2008 R2 CU6 21 Feb 2011
10.50.1753.0 SQL Server 2008 R2 CU5 20 Dec 2010
10.50.1746.0 SQL Server 2008 R2 CU4 18 Oct 2010
10.50.1734.0 SQL Server 2008 R2 CU3 17 Aug 2010
10.50.1720.0 SQL Server 2008 R2 CU2 25 Jun 2010
10.50.1702.0 SQL Server 2008 R2 CU1 18 May 2010
10.50.1600.1 SQL Server 2008 R2 RTM 12 Apr 2010
SQL Server 2008
10.00.5768 SQL Server 2008 SP3 CU2 22 Nov 2011
10.00.5766 SQL Server 2008 SP3 CU1 18 Oct 2011
10.00.5500 SQL Server 2008 SP3 6 Oct 2011
10.00.4323 SQL Server 2008 SP2 CU7 21 Nov 2011
10.00.4321 SQL Server 2008 SP2 CU6 20 Sep 2011
10.00.4316 SQL Server 2008 SP2 CU5 18 Jul 2011
10.00.4285 SQL Server 2008 SP2 CU4 16 May 2011
10.00.4279 SQL Server 2008 SP2 CU3 21 Mar 2011
10.00.4272 SQL Server 2008 SP2 CU2 17 Jan 2011
10.00.4266 SQL Server 2008 SP2 CU1 15 Nov 2010
10.00.4000 SQL Server 2008 SP2 29 Sep 2010
10.00.2850 SQL Server 2008 SP1 CU16 19 Sep 2011
10.00.2847 SQL Server 2008 SP1 CU15 18 Jul 2011
10.00.2816 SQL Server 2008 SP1 CU13 22 Mar 2011
10.00.2812 SQL Server 2008 SP1 CU14 16 May 2011
10.00.2808 SQL Server 2008 SP1 CU12 17 Jan 2011
10.00.2804 SQL Server 2008 SP1 CU11 15 Nov 2010
10.00.2799 SQL Server 2008 SP1 CU10 21 Sep 2010
10.00.2789 SQL Server 2008 SP1 CU9 19 Jul 2010
10.00.2775 SQL Server 2008 SP1 CU8 17 May 2010
10.00.2766 SQL Server 2008 SP1 CU7 15 Mar 2010
10.00.2757 SQL Server 2008 SP1 CU6 18 Jan 2010
10.00.2746 SQL Server 2008 SP1 CU5 24 Nov 2009
10.00.2734 SQL Server 2008 SP1 CU4 22 Sep 2009
10.00.2723 SQL Server 2008 SP1 CU3 21 Jul 2009
10.00.2714 SQL Server 2008 SP1 CU2 18 May 2009
10.00.2710 SQL Server 2008 SP1 CU1 16 Apr 2009
10.00.2531 SQL Server 2008 SP1 7 Apr 2009
10.00.1835 SQL Server 2008 RTM CU10 15 Mar 2010
10.00.1828 SQL Server 2008 RTM CU9 18 Jan 2009
10.00.1823 SQL Server 2008 RTM CU8 16 Nov 2009
10.00.1818 SQL Server 2008 RTM CU7 21 Sep 2009
10.00.1812 SQL Server 2008 RTM CU6 21 Jul 2009
10.00.1806 SQL Server 2008 RTM CU5 18 May 2009
10.00.1798 SQL Server 2008 RTM CU4 17 Mar 2009
10.00.1787 SQL Server 2008 RTM CU3 19 Jan 2009
10.00.1779 SQL Server 2008 RTM CU2 17 Nov 2008
10.00.1763 SQL Server 2008 RTM CU1 22 Sep 2008
10.00.1600 SQL Server 2008 RTM 6 Aug 2008
SQL Server 2005
9.00.5266 SQL Server 2005 SP4 CU3 21 Mar 2011
9.00.5259 SQL Server 2005 SP4 CU2 22 Feb 2011
9.00.5254 SQL Server 2005 SP4 CU1 20 Dec 2010
9.00.5000 SQL Server 2005 SP4 17 Dec 2010
9.00.4325 SQL Server 2005 SP3 CU15 21 Mar 2011
9.00.4317 SQL Server 2005 SP3 CU14 21 Feb 2011
9.00.4315 SQL Server 2005 SP3 CU13 20 Dec 2010
9.00.4311 SQL Server 2005 SP3 CU12 18 Oct 2010
9.00.4309 SQL Server 2005 SP3 CU11 17 Aug 2010
9.00.4305 SQL Server 2005 SP3 CU10 23 Jun 2010
9.00.4294 SQL Server 2005 SP3 CU9 19 Apr 2010
9.00.4285 SQL Server 2005 SP3 CU8 16 Feb 2010
9.00.4273 SQL Server 2005 SP3 CU7 21 Dec 2009
9.00.4266 SQL Server 2005 SP3 CU6 19 Oct 2009
9.00.4230 SQL Server 2005 SP3 CU5 17 Aug 2009
9.00.4226 SQL Server 2005 SP3 CU4 16 Jun 2009
9.00.4220 SQL Server 2005 SP3 CU3 21 Apr 2009
9.00.4211 SQL Server 2005 SP3 CU2 17 Feb 2009
9.00.4207 SQL Server 2005 SP3 CU1 20 Dec 2008
9.00.4053 SQL Server 2005 SP3 GDR (Security Update) 13 Oct 2009
9.00.4035 SQL Server 2005 SP3 16 Dec 2008
9.00.3356 SQL Server 2005 SP2 CU17 21 Dec 2009
9.00.3355 SQL Server 2005 SP2 CU16 19 Oct 2009
9.00.3330 SQL Server 2005 SP2 CU15 18 Aug 2009
9.00.3328 SQL Server 2005 SP2 CU14 16 Jun 2009
9.00.3325 SQL Server 2005 SP2 CU13 21 Apr 2009
9.00.3315 SQL Server 2005 SP2 CU12 17 Feb 2009
9.00.3310 SQL Server 2005 SP2 Security Update 10 Feb 2009
9.00.3301 SQL Server 2005 SP2 CU11 15 Dec 2008
9.00.3294 SQL Server 2005 SP2 CU10 20 Oct 2008
9.00.3282 SQL Server 2005 SP2 CU9 18 Aug 2008
9.00.3257 SQL Server 2005 SP2 CU8 16 Jun 2008
9.00.3239 SQL Server 2005 SP2 CU7 14 Apr 2008
9.00.3233 SQL Server 2005 QFE Security Hotfix 8 Jul 2008
9.00.3228 SQL Server 2005 SP2 CU6 18 Feb 2008
9.00.3215 SQL Server 2005 SP2 CU5 17 Dec 2007
9.00.3200 SQL Server 2005 SP2 CU4 15 Oct 2007
9.00.3186 SQL Server 2005 SP2 CU3 20 Aug 2007
9.00.3175 SQL Server 2005 SP2 CU2 28 Jun 2007
9.00.3161 SQL Server 2005 SP2 CU1 15 Apr 2007
9.00.3152 SQL Server 2005 SP2 Cumulative Hotfix 7 Mar 2007
9.00.3077 SQL Server 2005 Security Update 10 Feb 2009
9.00.3054 SQL Server 2005 KB934458 5 Apr 2007
9.00.3042.01 SQL Server 2005 "SP2a" 5 Mar 2007
9.00.3042 SQL Server 2005 SP2 1 Feb 2007
9.00.2047 SQL Server 2005 SP1
9.00.1399 SQL Server 2005 RTM 1 Nov 2005
SQL Server 2000
8.00.2039 SQL Server 2000 SP4
8.00.0760 SQL Server 2000 SP3
8.00.0534 SQL Server 2000 SP2
8.00.0384 SQL Server 2000 SP1
8.00.0194 SQL Server 2000 RTM
SQL Server 7
7.00.1063 SQL Server 7 SP4
7.00.0961 SQL Server 7 SP3 15 Dec 2000
7.00.0842 SQL Server 7 SP2 20 Mar 2000
7.00.0699 SQL Server 7 SP1 15 Jul 1999
7.00.0623 SQL Server 7 RTM
SQL Server 6.5
6.50.416 SQL Server 6.5 SP5a
6.50.415 SQL Server 6.5 SP5
6.50.281 SQL Server 6.5 SP4
6.50.258 SQL Server 6.5 SP3
6.50.240 SQL Server 6.5 SP2
6.50.213 SQL Server 6.5 SP1
6.50.201 SQL Server 6.5 RTM
You can determine what version SQL Server is running by running
Select @@version
@@Version is a system level variable that holds the current version. On my computer this returns
Microsoft SQL Server  2000 - 8.00.384 (Intel X86) 
 May 23 2001 00:02:52 
 Copyright (c) 1988-2000 Microsoft Corporation
 Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 2)
The main version number is 8.00.384 which corresponds to SQL Server 2000 SP1. See below for a complete list of versions. It will also tell us the version of the operating system we're running. In this case I'm running Windows 2000 (aka NT 5.0) Service Pack 2. You can find this same information in Enterprise Manager by right clicking on a server and choosing Properties. The version information is displayed in the General tab.
This information is pulled from the system extended procedure xp_msver. You can call this stored procedure like
exec master..xp_msver
and it returns
Index  Name                             Internal_Value Character_Value                     

------ -------------------------------- -------------- ------------------------------
1      ProductName                      NULL           Microsoft SQL Server
2      ProductVersion                   524288         8.00.384
3      Language                         1033           English (United States)
4      Platform                         NULL           NT INTEL X86
5      Comments                         NULL           NT INTEL X86
6      CompanyName                      NULL           Microsoft Corporation
7      FileDescription                  NULL           SQL Server Windows NT
8      FileVersion                      NULL           2000.080.0384.00
9      InternalName                     NULL           SQLSERVR
10     LegalCopyright                   NULL           © 1988-2000 Microsoft ...
11     LegalTrademarks                  NULL           Microsoft® is a registered ...
12     OriginalFilename                 NULL           SQLSERVR.EXE
13     PrivateBuild                     NULL           NULL
14     SpecialBuild                     25165824       NULL
15     WindowsVersion                   143851525      5.0 (2195)
16     ProcessorCount                   1              1
17     ProcessorActiveMask              1              00000001
18     ProcessorType                    586            PROCESSOR_INTEL_PENTIUM
19     PhysicalMemory                   255            255 (267902976)
20     Product ID                       NULL           NULL
This is quite a bit of additional information. There really isn't anything exciting in here that I can find but it's there if you need it.