Database programmers sometimes encounter puzzling query issues that other programmers may already have resolved. This section lists some of the common challenges and guidelines that improve query performance.
When writing Transact-SQL statements, batches, stored procedures, and triggers, use the programming features in Microsoft® SQL Server™ 2000 to create efficient code.
SQL Server 2000 has a better chance of reusing execution plans of Transact-SQL statements if they are written following these guidelines.
For example, do not code this SELECT:
SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = 3
Instead, using ODBC as an example, use the SQLBindParameter ODBC function to bind the parameter marker (?) to a program variable and code the SELECT statement as:
SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = ?
In a Transact-SQL script, stored procedure, or trigger, use sp_executesql to execute the SELECT statement:
DECLARE @IntVariable INT
DECLARE @SQLString NVARCHAR(500)
DECLARE @ParmDefinition NVARCHAR(500)
/* Build the SQL string. */
SET @SQLString =
N'SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = @ShipID'
/* Specify the parameter format once. */
SET @ParmDefinition = N'@ShipID int'
/* Execute the string. */
SET @IntVariable = 3
EXECUTE sp_executesql @SQLString, @ParmDefinition,
@ShipID = @IntVariable
For more information, see Execution Plan Caching and Reuse and Building Statements at Run Time.
When multiple concurrent applications will be executing the same batch with a known set of parameters, implement the batch as a stored procedure that will be called by the applications.
When an ADO, OLE DB, or ODBC application will be executing the same batch multiple times, use the PREPARE/EXECUTE model of executing the batch. Use parameter markers bound to program variables to supply all needed input values, such as the expressions used in an UPDATE VALUES clause or in the predicates in a search condition.
When calling a stored procedure from an ADO, OLE DB, or ODBC application, use the ODBC { CALL procedure_name } escape sequence instead of the Transact-SQL EXECUTE statement. For more information, see Calling a Stored Procedure.
Do not keep a transaction outstanding for long periods of time. A long-standing transaction can reduce throughput by holding locks on rows for long times, preventing other connections from accessing the rows in a timely manner.
Do not keep a result set outstanding for a long period of time. After executing a Transact-SQL batch, fully process or cancel all result sets from the batch as quickly as possible.
Minimize the number of rows returned from a SELECT statement by using the WHERE and HAVING clauses to select only the rows needed.
Minimize the use of not equal operations, <>, or !=. SQL Server has to scan a table or index to find all values to see if they are not equal to the value given in the expression. Try rephrasing the expression using ranges:
WHERE KeyColumn < 'TestValue' AND KeyColumn > 'TestValue'
Reduce roundtrips between the application and the server by:
For ODBC applications, consider using a fast forward-only cursor with the autofetch option. For more information, see Fast Forward-Only Cursors (ODBC).
Use advanced features available in Transact-SQL to perform work in one batch on the server instead of pulling the results to the application and then using them to send another Transact-SQL statement to SQL Server:
Within a batch, keep all data definition language (DDL) statements for a temporary table together. For example:
/* Example 1. */
CREATE TABLE #temp1 (ColA INT NOT NULL)
CREATE UNIQUE INDEX MyIndex ON #temp1(ColA)
INSERT INTO #temp1 SELECT IntCol FROM SomeTable
SELECT * FROM #temp1
GO
/* Example 2. */
CREATE TABLE #temp1 (ColA INT UNIQUE NOT NULL)
INSERT INTO #temp1 SELECT IntCol FROM SomeTable
SELECT * FROM #temp1
GO
Do not code:
/* Example 3. */
CREATE TABLE #temp1 (ColA INT NOT NULL)
INSERT INTO #temp1 SELECT IntCol FROM SomeTable
CREATE UNIQUE INDEX MyIndex ON #temp1(ColA)
SELECT * FROM #temp1
GO
Each time a DDL operation is performed on a temporary table, all batches that refer to it must be recompiled. The query optimizer ensures that the CREATE statements in Examples 1 and 2 are done in one operation and the batches are recompiled only once. In Example 3, the INSERT statement between the CREATE statements forces a separate recompile for each CREATE statement.
Minimize the use of temporary tables as places to store intermediate results in a series of Transact-SQL statements. Some logic is too complex to perform in a single Transact-SQL statement. In these cases, you must code multiple Transact-SQL statements and use temporary tables to pass the results of one statement to the next. Creating and maintaining the temporary tables requires overhead; if possible, consider coding the operation as a single, more complex Transact-SQL statement.
In SQL Server 2000, use of temporary tables in stored procedures and triggers may cause the stored procedure or trigger to be recompiled every time it is used. To avoid such recompilation, stored procedures or triggers that use temporary tables must meet the following requirements:
To prevent issues with the interpretation of centuries in dates, do not specify years using two digits. For example:
/* Do this. */
SELECT *
FROM Northwind.dbo.Orders
WHERE OrderDate > '12/31/1997'
/* Do not do this. */
SELECT *
FROM Northwind.dbo.Orders
WHERE OrderDate > '12/31/97'