Now that you know how to speed up your SQL Server database queries, you can start delving into some of the more advanced tuning options.

The best way to tune performance is to try to write your queries in a number of different ways and compare their reads and execution plans. Here are various techniques that you can use to try to optimise your database queries.

Use JOINs rather than subqueries

If possible I suggest using JOIN statements rather than subqueries to improve performance. When a subquery is used as criteria in a SELECT statement, the values returned from the subquery are distinct. Returning a distinct list of values requires additional processing, which can slow down your queries.

Use explicit transactions

When data manipulation occurs in the database, the actions are written to the transaction log. If your statements are executing many DML statements, it might be a good idea to place them inside of a transaction for performance purposes. Placing the statements inside of a transaction will prevent all of the statements from being written to the transaction log serially. For example, the following statement takes approximately seven seconds to execute on my laptop:

CREATE InsertTable
(
IDCol INT IDENTITY(1,1),
ColVal INT
)
GO
DECLARE @Counter INT
SET @Counter = 1

WHILE @Counter < 15000
BEGIN
INSERT INTO InsertTable(ColVal)
SELECT DATEPART(ms, GETDATE())

SET @Counter = @Counter + 1
END

If I wrap the INSERT statements inside of a transaction, it takes a little less than two seconds to execute. This is because the statements are inside of a transaction rather than committed to a transaction log until the transaction commits. This reduces the number of writes to the log.

DECLARE @Counter INT
SET @Counter = 1
BEGIN TRAN
WHILE @Counter < 15000
BEGIN
INSERT INTO InsertTable(ColVal)
SELECT DATEPART(ms, GETDATE())

SET @Counter = @Counter + 1
END
COMMIT TRAN

Note: I advise you to use this approach with care. If there are too many statements inside a transaction, it will increase the duration of the transaction, which increases the amount of time locks are held on the objects inside of the transaction.

Use UNION ALL instead of UNION

When you use the UNION clause to concatenate the results from two or more SELECT statements, duplicate records are removed. This duplicate removal requires additional computing to accomplish. If you are not concerned that your results may include duplicate records, use the UNION ALL clause, which concatenates the full results from the SELECT statements.

Use EXISTS when possible

When you need to check for the presence of certain conditions, it is usually faster to use the EXISTS function over COUNT(*). This is because COUNT(*) has to scan all records returned by the statement, while EXISTS will return a true value as soon as it finds a record that meets the criteria.

STATISTICS IO

There are different ways to determine the best way to write your queries. Two of my favorite methods are looking at the number of logical reads produced by the query and looking at graphical execution plans provided by SQL Server Management Studio. For determining the number of logical reads, you can turn the STATISTICS IO option ON. Consider this query:

SET STATISTICS IO ON
SELECT * FROM SalesHistory

The following is returned in the Messages window in SQL Server Management Studio:

Table 'SalesHistory'. Scan count 1, logical reads 33, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

There are several bits of data returned by STATISTICS IO, but I am really only concerned with the logical reads portion because it will tell me the number of pages read from the data cache. This is the most helpful to me because it will stay constant when I run the same query, which is important because there are sometimes external factors that might vary the execution time of my queries, such as locking by other queries.

When I'm tuning my queries, my goal is to get the number of logical reads as low as possible. Fewer logical reads typically leads to faster execution times.

Database Jumping This was published in Database Jumping, check every Thursday for more stories

Related links

Comments

1

Henk Schreij - 20/12/07

On the subject: Use JOINs rather than subqueries you say:
"The values returned from the subquery are distinct."
I agree on that subqueries can slow the query.
But the reason is that there is an assert looking for non distinct results, and your query stops with a warning-message when there is a non-distinct result in the subquery.
So I suggest:
the values returned from the subquery are checked for non-distinct values

» Report offensive content

2

Crispin Proctor - 20/12/07

A bit of clarification is needed on the subqueries: Inline nested queries are bad. Evil. Satin spawn. You get the point. :) They should not be used and I have never seend a valid reason to do one. Like you say, they have to be distinct which does add overhead. However, this is not their main reason for performance degradation in performance.

Subqueries, where two or more queries are joined together are not bad and have many good uses / reasons for using them.
See example below.

SELECT
*
FROM
(
Select
Col1,
Max(Col2)
From
Table1
Group By
Col1
) AS DVR1
JOIN
(
Select
Col1,
Max(Col2)
From
Table2
Group By
Col1
) AS DVR2 ON DVR1.Col1 = DVR2.Col1

» Report offensive content

3

Andy - 20/12/07

Please could you let me know why nested queries are so bad? I must admit I use them a lot!

» Report offensive content

4

Rick Foster - 21/12/07

I have found under some conditions, subqueries are faster than joins. If data from the subquery is not needed in the result set and data from the subquery is used to limit the result set, savings can be realized. When a subquery is used to limit results by comparing values in the where clause or on clause, the number of reads can be significantly reduced over a join. A join will intersect all the records instead of a subset when using the subquery. When dealing with a slow query on large tables, I look at ways to reduce the number of reads and at times that is to employ subqueries.

» Report offensive content

5

Prakash Heda - 22/12/07

Use explicit transactions.....Hats off for this....


add "table" after create word...just a small syntax error

» Report offensive content

6

Jeff Moden - 14/03/08

Heh... In the section titled "Use explicit transactions", you didn't mention the real problem. When you want to insert 15,000 rows of anything, you shouldn't do it using an explicit loop of any kind.

» Report offensive content

7

Anonymous - 30/03/08

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

7

Anonymous - 30/03/08

... more

6

Jeff Moden - 14/03/08

Heh... In the section titled "Use explicit transactions", you didn't mention the real problem. When you want to insert 15,000 ... more

5

Prakash Heda - 22/12/07

Use explicit transactions.....Hats off for this.... add "table" after create word...just a small syntax error ... more

Log in


Sign up | Forgot your password?

What's on?