Showing posts with label Database. Show all posts
Showing posts with label Database. Show all posts

Bugs in Oracle Query ?

I want to query a data with date condition. Guess what ? This similiar query give a different result.

1st query give the exact result that i wanted.
SELECT * FROM my_table
WHERE TO_CHAR(date_time, 'DD/MM/YYYY') BETWEEN '01/10/2007' and '6/11/2007'

2nd query result nothing
SELECT * FROM my_table
WHERE TO_CHAR(date_time, 'DD/MM/YYYY') BETWEEN '01/10/2007' and '06/11/2007'

The different is, the 1st query use 6-11-2007 instead of 06/11/2007.

FYI, i use Oracle database 9i and running query in Toad for Oracle 8.5.3.2

Any idea why this can happen ?

SQL Toolkit For DBA's Part 2


2nd posting about DBA toolkits, read first posting here

Text Processing

Text processing is a big part of SQL Server database and application development. Sometimes you need to transform, find patterns in, or just search text; but text processing is the bread and butter of many SQL-based applications.

In recognition of the importance of text processing on SQL Server, I've included several text processing tools in the toolkit. Some are re-writes of previously released tools, like the Sound Matching and Phonetic Toolkit. Others, like regular expression parsing, are new to the toolkit. This article focuses on regular expression tools in the toolkit; the next article will discuss the phonetic matching tools included.
Installation

Installation of the Toolkit is covered in Part One of this series. Basically, just copy the DLL's to your MSSQL\BINN directory and run the INSTALL.SQL script. Installation troubleshooting steps are covered in the README.RTF file included in the \DOCS directory.

In addition, there is a Windows Compiled Help file (DBATOOLKIT.CHM) included in the \DOCS directory. This Help File is included as your first line for online Q&A.
Regular Expression Tools

Regular expressions (regex's) are a precise notation for approximate matching. One of the most popular styles of regular expression syntax is the Perl syntax. The regular expression tools in the toolkit use the Boost::Regex++ library, which is compliant with Perl syntax. NOTE: Microsoft's regular expression syntax differs somewhat from Perl syntax, so .NET-specific regular expressions might require tweaking to use with these tools.
The Functions

SQL Toolkit For DBA's


HIPAA. SOX. FACTA. Every DBA needs to get compliant. However, not every DBA has the resources to migrate corporate databases to the new version of SQL Server overnight. Some of us need to be in compliance *yesterday*, but for one reason or another we won't be able to take advantage of the additional security and other high-speed functionality of SQL 2005 until after tax season.

Or after the start of the next fiscal year.

Or after the next presidential election.

You get the point.

So what do we do - those of us who are stuck with good old SQL 2000 with no foreseeable upgrade in sight? One option is to roll your own solutions. Or you could let someone like me do the dirty work for you.

This DBA Toolkit includes over two dozen extended stored procedures to help DBA's and developers who are committed to the SQL 2000 platform to some degree. Some of the tools are designed to add badly needed encryption to SQL 2000; others provide specific SQL Server functionality I've found useful over the years. All are designed to make SQL development and administration a little easier by extending the functionality of your SQL Server.

This series of articles provides the introduction to the Toolkit:
1. This first article will start with a look at the re-birth of SQL 2000 encryption.
2. The second article will discuss the addition of regular expressions to SQL Server via the Toolkit.
3. A third article will address phonetic matching tools in the Toolkit.
4. Finally, I will wrap up this series with a discussion of additional functionality in the toolkit that fits into none of the above categories.

Feel free to use these tools as you see fit. If you find them useful, please drop me a line and let me know which ones you use, how you use them, and what you find most useful.

Enjoy.

Download here http://www.sqlservercentral.com/downloads/DBAToolkit.zip

Date time best practise in SQL Server

Nice article for DBA's

When can you use arithmetic operators in date/time calculations in SQL Server? When are the date/time functions provided by Microsoft the better option? Follow a few scenarios that demonstrate when arithmetic operators are safe and when they are risky.

few months ago, I received a message from quite an experienced DBA and database programmer asking me whether I knew a nice trick for manipulating date/time data without using Microsoft SQL Server's date/time functions—something like GETDATE() + 10. It reminded me of a few internet forum discussions about the same subject in which participants were irked by the fact that Microsoft missed such a convenient feature. I decided that the topic deserved a deeper discussion. This article is the result.

How Is This Possible?
Try running the following example with the SQL Server DATEADD() function:


SELECT GETDATE();
SELECT DATEADD(dd, 1, GETDATE());
SELECT GETDATE() + 1;

Results:

2007-03-15 16:21:41.630
2007-03-16 16:21:41.630
2007-03-16 16:21:41.630

As you can see, both the DATEADD() function and an addition operation that uses a plus sign (+) produce exactly the same result: they add one day to a current day and time. You'd expect that from DATEADD(), but why does an addition operation produce the same result? For the answer, you must explore the SQL Server date/time storage internals.

As you know, the datetime data type occupies eight bytes: four to store the number of days before or after January 01, 1900 and four more for the number of 3.33ms clock ticks since midnight. The smalldatetime data type needs only four bytes as it is less precise than datetime; it stores the number of days since January 01, 1900 and the number of minutes since midnight. All the numbers are stored as integers. So when you run SELECT GETDATE() + 1, you actually add datetime data to the integer number. Since the datetime data type has higher precedence, the integer number 1 (as an addend with lower precedence) has to be implicitly converted to a data type of the addend with higher precedence.

The following example converts "1" to datetime and then treats it as one day since Jan 01, 1900:


SELECT CAST(1 as datetime);

Results:

1900-01-02 00:00:00.000

When you run SELECT GETDATE() + 1, you add two datetime values that internally are interpreted as integers. As a result an addition operation that uses a plus sign (+) becomes perfectly valid. For example, all the following statements will be correct:


SELECT GETDATE();
SELECT DATEADD(dd, 1, GETDATE());
SELECT GETDATE() + 1;
SELECT GETDATE() + 'Jan 02, 1900';

Results:

2007-03-16 23:01:37.420
2007-03-17 23:01:37.420
2007-03-17 23:01:37.420
2007-03-17 23:01:37.420

Notice that usage of the addition operator (+) in date/time calculations requires at least one datetime addend, which also has to be an addend with the highest precedence.

Try the following example:


SELECT DATEADD(dd, 1, 'Mar 17, 2007');
SELECT 'Mar 17, 2007' + 1;
SELECT 'Mar 17, 2007' + 'Jan 02, 1900';
SELECT GETDATE() + 1 + 'Jan 02, 1900';

As you can see, the first SELECT that uses SQL Server's DATEADD() function recognizes dates written as varchar and produces the correct result. The second SELECT fails with the error: "Conversion failed when converting the varchar value 'Mar 17, 2007' to data type int." This happens because varchar addend has lower precedence and must be implicitly converted to the integer data type. In this case such a conversion is impossible.

The third SELECT works, but doesn't make any sense. It just concatenates two varchar expressions. The forth SELECT is very interesting. Since second and third addends have lower precedence than the first one, they need to be implicitly converted to a datetime data type. Each of them will be interpreted as one day. Therefore two days will be added to the reult of the GETDATE() function.



First Pitfalls
At this point, you may start to believe that using addition or subtraction operators in date/time calculations is completely safe. It isn't. For example try to calculate the interval between two dates (in days) using the first and second approaches as follows:


DECLARE @dt1 datetime, @dt2 datetime;
SELECT @dt1 = 'Mar 17, 2007 09:09:00', @dt2 = 'Mar 17, 2007 22:09:00';
SELECT DATEDIFF(dd, @dt1, @dt2);
SELECT CAST((@dt2 - @dt1) as int);

Results:

0
1

The results calculated by SQL Server's DATEDIFF() function and those using the subtraction operator are different. The first (0) is correct, the second (1) is wrong. Why? Well, when you convert a datetime value to the integer, the result will be rounded to the nearest integer. How it will be rounded depends on time portion of the datetime value. Consider the following example:


DECLARE @dt3 datetime, @dt4 datetime;
SELECT @dt3 = 'Mar 17, 2007 11:59:59.994', @dt4 = 'Mar 17, 2007 11:59:59.997';
SELECT CAST(@dt3 AS int);
SELECT CAST(@dt4 AS int)
SELECT CAST(CAST(@dt3 AS int) AS datetime);
SELECT CAST(CAST(@dt4 AS int) AS datetime);

Results:

39156
39157
2007-03-17 00:00:00.000
2007-03-18 00:00:00.000

As you can see, two time stamps with a 3ms interval were taken just before noon. If you convert the two date values to integers and then reconvert the results back to date values, you'll see a difference of one day. Similarly, two datetime values representing different days (Mar 16 and Mar 17) can be incorrectly converted to the same date, like this:


DECLARE @dt3 datetime, @dt4 datetime;
SELECT @dt3 = 'Mar 16, 2007 12:00:01.000', @dt4 = 'Mar 17, 2007 11:59:59.994';
SELECT CAST(@dt3 AS int);
SELECT CAST(@dt4 AS int)
SELECT CAST(CAST(@dt3 AS int) AS datetime);
SELECT CAST(CAST(@dt4 AS int) AS datetime);

Results:

39156
39156
2007-03-17 00:00:00.000
2007-03-17 00:00:00.000

Now, consider one more example. Suppose you have a table where you store sales transactions that each have an ID, time, and amount. You want to find the total amount and number of transactions per day, which is a very common task. Since your table is very large, you decide to speed up your query by grouping your transactions by date interval, not by date. But instead of using the SQL Server DATEDIFF() function, you decide to use an "advanced" approach: converting datetime data to integer or calculating the interval using simple arithmetic subtraction.

Here's what can happen in this scenario:


SET NOCOUNT ON;
IF OBJECT_ID('sales', 'U') IS NOT NULL
DROP TABLE sales

CREATE TABLE sales(
transactionID int,
transactionTime datetime,
amount decimal(4,2));

INSERT INTO sales VALUES(1, 'Mar 17, 2007 08:00:23', 24.34);
INSERT INTO sales VALUES(2, 'Mar 17, 2007 10:33:23', 88.54);
INSERT INTO sales VALUES(3, 'Mar 17, 2007 12:00:44', 12.12);
INSERT INTO sales VALUES(4, 'Mar 17, 2007 14:23:23', 43.25);
INSERT INTO sales VALUES(5, 'Mar 17, 2007 16:45:22', 76.34);
INSERT INTO sales VALUES(6, 'Mar 17, 2007 17:11:22', 51.11);
INSERT INTO sales VALUES(7, 'Mar 17, 2007 19:45:23', 30.99);

SELECT COUNT(*) AS #Trans, SUM(amount) AS totalAmount
FROM sales
GROUP BY DATEDIFF(dd, 0, transactionTime);

SELECT COUNT(*) AS #Trans, SUM(amount) AS totalAmount
FROM sales
GROUP BY CAST(transactionTime AS int);

SELECT COUNT(*) AS #Trans, SUM(amount) AS totalAmount
FROM sales
GROUP BY CAST((transactionTime - 0) AS int);

Results:

#Trans totalAmount
----------- ---------------------------------------
7 326.69

#Trans totalAmount
----------- ---------------------------------------
2 112.88
5 213.81

#Trans totalAmount
----------- ---------------------------------------
2 112.88
5 213.81

SQL Server DATEDIFF() produces the correct result because it works with dates like a FLOOR() function. It cuts the time portion of each date and manipulates only days. If you chose the other approach of converting to integers or direct subtraction, each datetime value would be rounded to the nearest integer, taking into account the time portion of the datetime value. This is why the second and third SELECT statements produce the wrong results.



Arithmetic Operations on Time Portion of Date/Time Data
So far this article has discussed the arithmetic operations on the date portion of data/time values. So when you try to run SELECT GETDATE() + 1, the second addend is implicitly considered the number of days that have to be added to the value returned by the GETDATE() function. But what if you want to add one hour or one minute to the current datetime value? Using the SQL Server DATEADD() function you can do that very easily, but using addition or substraction operators will be tricky.

Let's examine how to do that. One day consists of 24 hours or 1,440 minutes or 86,400 seconds. So if you run the following statement:


SELECT GETDATE() + 1/24

You probably will get the current datetime plus one hour. In reallity, however, you cannot use just 1/24 because both dividend and divisor are integers, and the result of dividing them will be zero. You need to convert the integers to decimal or float data types as follows in order to get the correct result:


-- How to add one hour
---------------------------------------
SELECT GETDATE()
SELECT DATEADD(hh, 1, GETDATE())
SELECT GETDATE () + CAST(1 AS dec(9,4))/ CAST (24 AS dec(9,4))
SELECT GETDATE () + CAST(1 AS dec)/ CAST (24 AS dec)
SELECT GETDATE () + CAST (1 AS float)/ CAST (24 AS float)

Results:

2007-03-25 20:31:13.870
2007-03-25 21:31:13.870
2007-03-25 21:31:13.867
2007-03-25 21:31:13.870
2007-03-25 21:31:13.870


-- How to add one minute
---------------------------------------
SELECT GETDATE()
SELECT DATEADD(mi, 1, GETDATE())
SELECT GETDATE () + CAST(1 AS dec(9,4))/ CAST (1440 AS dec(9,4))
SELECT GETDATE () + CAST(1 AS dec(18,9))/ CAST (1440 AS dec(18,9))
SELECT GETDATE () + CAST (1 AS float)/ CAST (1440 AS float)

Results:

2007-03-25 20:35:15.127
2007-03-25 20:36:15.127
2007-03-25 20:36:15.123
2007-03-25 20:36:15.127
2007-03-25 20:36:15.127


-- How to add one second
---------------------------------------
SELECT GETDATE()
SELECT DATEADD(ss, 1, GETDATE())
SELECT GETDATE () + CAST(1 AS dec(9,4))/ CAST (86400 AS dec(9,4))
SELECT GETDATE () + CAST(1 AS dec(18,9))/ CAST (86400 AS dec(18,9))
SELECT GETDATE () + CAST(1 AS dec(24,18))/ CAST (86400 AS dec(24,18))
SELECT GETDATE () + CAST (1 AS float)/ CAST (86400 AS float)

Results:

2007-03-25 20:42:26.617
2007-03-25 20:42:27.617
2007-03-25 20:42:27.613
2007-03-25 20:42:27.613
2007-03-25 20:42:27.613
2007-03-25 20:42:27.617


-- How to add one second, using variables
------------------------------------------

DECLARE @dec1 dec(24,18), @dec2 dec(24,18), @dec3 dec(24,18), @dt datetime
SELECT @dec1 = 1, @dec2 = 86400, @dt = GETDATE();
SELECT @dec3 = @dec1 / @dec2;

SELECT @dt
SELECT DATEADD(ss, 1, @dt)
SELECT @dt + @dec3
SELECT @dt + CAST (1 AS float)/ CAST (86400 AS float)

Results:

2007-03-25 20:49:16.817
2007-03-25 20:49:17.817
2007-03-25 20:49:17.813
2007-03-25 20:49:17.817

As you can see from the last example, the SQL Server function DATEADD() works perfectly, but an addition operator may cause a problem. For example, when you try to add one hour or one minute, you need to find a sufficient precision for decimal data type. Otherwise, the result will be slightly inaccurate. However, when you try to add one second, applying an addition operator and decimal conversion, you won't be able to get the exact result at all.

On the other hand, the float conversion looks precise and safe for the time calculations with an addition operator, but if you start to use it you may run into a problem: duplicates and missing values. To illustrate and understand the problem, create and populate an auxillary table:


SET NOCOUNT ON;
DECLARE @max int, @cnt int;
SELECT @cnt = 10000;

IF EXISTS(SELECT * FROM sysobjects
WHERE ID = (OBJECT_ID('sequence')) AND xtype = 'U')
DROP TABLE sequence;

CREATE TABLE sequence(num int NOT NULL);
INSERT INTO sequence VALUES(1);
SELECT @max = 1;
WHILE(@max <= @cnt)
BEGIN
INSERT INTO sequence
SELECT @max + num FROM sequence;
SELECT @max = MAX(num) FROM sequence;
END

When you run this script, it will insert 16,384 sequential numbers into the table sequence. (The number 16,384 doesn't have any special meaning. It was selected for illustration purposes only.)

Now, generate the sequence of hours using the auxillary table and SQL Server's date/time function as follows:


IF EXISTS(SELECT * FROM sysobjects
WHERE id = OBJECT_ID('test'))
DROP TABLE test;

SELECT num, DATEADD(hh, num, 'Dec 31, 2006 23:00:00') dt
INTO test
FROM sequence;

SELECT * FROM test;

Results:

num dt
----------- -----------------------
1 2007-01-01 00:00:00.000
2 2007-01-01 01:00:00.000
3 2007-01-01 02:00:00.000
. . . . . . . . . . . . . . . . . .
3099 2007-05-10 02:00:00.000
3100 2007-05-10 03:00:00.000
. . . . . . . . . . . . . . . . . .
16381 2008-11-13 12:00:00.000
16382 2008-11-13 13:00:00.000
16383 2008-11-13 14:00:00.000
16384 2008-11-13 15:00:00.000

The function DATEADD() works as expected and generates a sequence of datetime values with one-hour intervals.

Now, try to roll up the sequence you just generated using the same the SQL Server DATEADD() function:


SELECT DISTINCT DATEADD(hh, -num, dt) FROM test

Results:

2006-12-31 23:00:00.000

The last result proves that SQL Server's date/time function generates date/time values properly. In order to test the solution that uses an arithmetic operator (+), run the following example:


DECLARE @time float
SELECT @time = CAST(1 as float)/CAST(24 as float)

IF EXISTS(SELECT * FROM sysobjects
WHERE id = OBJECT_ID('test'))
DROP TABLE test;

SELECT num, (CAST('Dec 31, 2006 23:00:00' AS datetime) + @time * num) dt
INTO test
FROM sequence;

SELECT * FROM test;

num dt
----------- -----------------------
1 2007-01-01 00:00:00.000
2 2007-01-01 01:00:00.000
3 2007-01-01 02:00:00.000
4 2007-01-01 03:00:00.000
5 2007-01-01 03:59:59.997
6 2007-01-01 05:00:00.000
7 2007-01-01 05:59:59.997
8 2007-01-01 07:00:00.000
9 2007-01-01 08:00:00.000
10 2007-01-01 08:59:59.997
. . . . . . . . . . . . . . . . . .
16380 2008-11-13 11:00:00.000
16381 2008-11-13 11:59:59.997
16382 2008-11-13 12:59:59.997
16383 2008-11-13 14:00:00.000
16384 2008-11-13 14:59:59.997

You will find that an addition operator produces inaccurate results. Sometimes they differ from the expected ones by 3 ms. If you try to roll up the sequence of generated date/time values, you will get more than one date/time "seed" value (as in the following example) and that is incorrect:


SELECT DISTINCT DATEADD(hh, -num, dt) FROM test

Results:

2006-12-31 22:59:59.997
2006-12-31 23:00:00.000

You may say that +/- 3 ms precision is acceptable for most applications, but look how that seemingly tiny problem can produce a bigger one:


SELECT CONVERT(varchar(100), dt, 100)
FROM test
ORDER BY num

Results:

Jan 1 2007 12:00AM
Jan 1 2007 1:00AM
Jan 1 2007 2:00AM
Jan 1 2007 3:00AM
Jan 1 2007 3:59AM
Jan 1 2007 5:00AM
Jan 1 2007 5:59AM
. . . . . . . . . .
Nov 13 2008 5:59AM
Nov 13 2008 6:59AM
Nov 13 2008 8:00AM
Nov 13 2008 8:59AM
Nov 13 2008 9:59AM
Nov 13 2008 11:00AM
Nov 13 2008 11:59AM
Nov 13 2008 12:59PM
Nov 13 2008 2:00PM
Nov 13 2008 2:59PM

This example uses a CONVERT() function to produce a different date/time format. As a result, inaccuracies in the generated values increased from 3 ms to 1 minute and became unacceptable. However, this is not the only problem. If you try to generate the sequences of minutes or seconds, things become even worse. Look at this example:


DECLARE @time float
SELECT @time = cast(1 as float)/cast(1440 as float)

IF EXISTS(SELECT * FROM sysobjects
WHERE id = OBJECT_ID('test'))
DROP TABLE test;

SELECT num, (CAST('Dec 31, 2006 23:59:00' AS datetime) + @time * num) dt
INTO test
FROM sequence;

SELECT * FROM test;

Results:

num dt
----------- -----------------------
1 2007-01-01 00:00:00.000
2 2007-01-01 00:01:00.000
. . . . . . . . . . . . . . . . . .
1579 2007-01-02 02:17:59.997
1580 2007-01-02 02:19:00.000
1581 2007-01-02 02:19:59.997
. . . . . . . . . . . . . . . . . .
16382 2007-01-12 09:01:00.000
16383 2007-01-12 09:01:59.997
16384 2007-01-12 09:03:00.000

As you can see, there are inaccuracies in the generated values again. In addition, when you try to convert these values to another format as follows, you will get duplicated or missing dates:


SELECT CONVERT(varchar(100), dt, 100)
FROM test
ORDER BY num

Results:

Jan 1 2007 12:00AM
Jan 1 2007 12:01AM
. . . . . . . . . .
Jan 2 2007 12:00AM
Jan 2 2007 12:00AM
Jan 2 2007 12:02AM
Jan 2 2007 12:02AM
Jan 2 2007 12:04AM
Jan 2 2007 12:04AM
. . . . . . . . . .
Jan 12 2007 9:00AM
Jan 12 2007 9:01AM
Jan 12 2007 9:01AM
Jan 12 2007 9:03AM

For instance, there are two values "Jan 02, 2007 12:02AM", but the value "Jan 02, 2007 12:03AM" is missing.

If you want to see the list of all duplicates, you can run the following query:


SELECT COUNT(*), CONVERT(varchar(100), dt, 100)
FROM test
GROUP BY CONVERT(varchar(100), dt, 100)
HAVING COUNT(*) > 1
ORDER BY 2

Finally, you can generate the sequence of seconds using the same approach:


DECLARE @time float
SELECT @time = cast(1 as float)/cast(86400 as float)

IF EXISTS(SELECT * FROM sysobjects
WHERE id = OBJECT_ID('test'))
DROP TABLE test;

SELECT num, (CAST('Dec 31, 2006 23:59:59' AS datetime) + @time * num) dt
INTO test
FROM sequence;

SELECT * FROM test;

Results:

num dt
----------- -----------------------
1 2007-01-01 00:00:00.000
2 2007-01-01 00:00:01.000
3 2007-01-01 00:00:02.000
4 2007-01-01 00:00:03.000
5 2007-01-01 00:00:03.997
6 2007-01-01 00:00:05.000
7 2007-01-01 00:00:06.000
8 2007-01-01 00:00:07.000
9 2007-01-01 00:00:08.000
10 2007-01-01 00:00:08.997
11 2007-01-01 00:00:09.997
. . . . . . . . . . . . . . . . . .
16382 2007-01-01 04:33:00.997
16383 2007-01-01 04:33:02.000
16384 2007-01-01 04:33:03.000


SELECT CONVERT(varchar(100), dt, 120)
FROM test
ORDER BY num

Results:

2007-01-01 00:00:00
2007-01-01 00:00:01
2007-01-01 00:00:02
2007-01-01 00:00:03
2007-01-01 00:00:03
2007-01-01 00:00:05
. . . . . . . . . .
2007-01-01 04:32:55
2007-01-01 04:32:56
2007-01-01 04:32:56
2007-01-01 04:32:58
2007-01-01 04:32:59
2007-01-01 04:33:00
2007-01-01 04:33:00
2007-01-01 04:33:02
2007-01-01 04:33:03

This last example has the same problems as the previous one. In addition, using arithmetic operators for date/time manipulations can lead to other errors, weird results, degradation in performance, and more problems than are discussed here. You can avoid all these problems by using the SQL Server's date/time functions.



Bonus Benefits Beyond Bad Results
SQL Server's date/time functions not only help you avoid the errors, but they also make the programmer's life much easier and more convenient. If, for example, you need to generate the fifth day of each month in the year 2007, you can do that easily using the DATEADD() function:


SELECT DATEADD(mm, num - 1, 'Jan 05, 2007') AS [5th_Day]
FROM sequence
WHERE num <= 12

Results:

5th_Day
-----------------------
2007-01-05 00:00:00.000
2007-02-05 00:00:00.000
2007-03-05 00:00:00.000
2007-04-05 00:00:00.000
2007-05-05 00:00:00.000
2007-06-05 00:00:00.000
2007-07-05 00:00:00.000
2007-08-05 00:00:00.000
2007-09-05 00:00:00.000
2007-10-05 00:00:00.000
2007-11-05 00:00:00.000
2007-12-05 00:00:00.000

The beauty of this query is in its convenience. You do not need to know how many days are in each specific month or in the year. The logic and calculations are already included into the SQL Server functions and are transparent to you. Trying to use the arithmetic operator (+) for the same task would require implementing that logic yourself, which would increase the development and debugging time and potentially raise the chance of errors.

So should you use arithmetic operators in date/time calculations in SQL Server? No. Although it's possible to use arithmetic operators with dates for very simple operations such as GETDATE() + 1—because SQL Server will convert the 1 to a date internally—you're better off avoiding using arithmetic operators with dates altogether. If you use them in more complex date/time calculations, you will need to be very careful and conscientious.

stored procedure tuning for sql server

more article to sql server database programming.


Whenever a client application needs to send Transact-SQL to SQL Server, send it in the form of a stored procedure instead of a script or embedded Transact-SQL. Stored procedures offer many benefits, including:

* Reduced network traffic and latency, boosting application performance.
* Stored procedure execution plans can be reused, staying cached in SQL Server's memory, reducing server overhead.
* Client execution requests are more efficient. For example, if an application needs to INSERT a large binary value into an image data column not using a stored procedure, it must convert the binary value to a character string (which doubles its size), and send it to SQL Server. When SQL Server receives it, it then must convert the character value back to the binary format. This is a lot of wasted overhead. A stored procedure eliminates this issue as parameter values stay in the binary format all the way from the application to SQL Server, reducing overhead and boosting performance.
* Stored procedures help promote code reuse. While this does not directly boost an application's performance, it can boost the productivity of developers by reducing the amount of code required, along with reducing debugging time.
* Stored procedures can encapsulate logic. You can change stored procedure code without affecting clients (assuming you keep the parameters the same and don't remove any result sets columns). This saves developer time.
* Stored procedures provide better security to your data. If you use stored procedures exclusively, you can remove direct SELECT, INSERT, UPDATE, and DELETE rights from the tables and force developers to use stored procedures as the method for data access. This saves DBA's time.

Keep in mind that just because you use a stored procedure does not mean that it will run fast. The code you use within your stored procedure must be well designed for both speed and reuse. [6.5, 7.0, 2000, 2005] Update 6-6-2005

*****

One of the biggest advantages of using stored procedures over not using stored procedures is the ability to significantly reduce network traffic. And the more network traffic that can be reduced, the better the overall performance of your SQL Server-based applications.

Here are some examples how stored procedures reduce network traffic:

* When an application executes a stored procedure, only a simple, small RPC (remote procedure call) is made from the client to SQL Server. But if the application is not using stored procedures, but sending Transact-SQL code directly from the client to SQL Server, network traffic cab often very high. For example, if the amount of Transact-SQL code is 500 lines (and this would not be all that unusual), then it would take hundreds of network packets to transmit the Transact-SQL code from the client to SQL Server. On the other hand, if the 500 lines of Transact-SQL code are in a stored procedure, this code never has to travel the network, as it is already located on the server.
* When an application needs to retrieve one or more rows from SQL Server and then takes some action on this data, such as INSERTing, UPDATing, or DELETing rows in the database based on the data retrieved, network traffic is significantly reduced if all this code is stored in a stored procedure. As before, it only takes a single RPC call to execute a stored procedure. But if all the code to perform these steps is not in a stored procedure, but located in the application, network traffic can be high. For example, first, the application has to send the Transact-SQL code to SQL Server (lots of potential network traffic). Then SQL Server has to return the result set back to the client, then the client has to use the data, and then send additional requests (INSERT, UPDATE, DELETE) to SQL Server, and then SQL Server has to respond back to the client, and so on, until the task is completed. As you can see, this can generate a lot of network traffic. But if all the work is being done from within a stored procedure, network traffic is greatly reduced.
* Along the same lines as above, putting the business logic of your application in stored procedures can help your application's performance. By locating virtually all of the processing on SQL Server, round-trip network traffic is greatly reduced, helping boost performance.

The goal should be to limit network traffic from the client to SQL Server to simple RPCs, and limit the traffic from SQL Server to the client as finished results. [6.5, 7.0, 2000] Updated 12-6-2005

*****

To help identify performance problems with stored procedures, use the SQL Server's Profiler Create Trace Wizard to run the "Profile the Performance of a Stored Procedure" trace to provide you with the data you need to identify poorly performing stored procedures. [7.0] Updated 12-6-2005

*****

By default, every time a stored procedure is executed, a message is sent from the server to the client indicating the number of rows that were affected by the stored procedure. Rarely is this information useful to the client. By turning off this default behavior, you can reduce network traffic between the server and the client, helping to boost overall performance of your server and applications.

There are two main ways to turn this feature off. You can also turn this feature off using a server trace setting, but it is unnecessary as there are easier ways, as described here.

To turn this feature off on at the stored procedure level, you can include the statement:

SET NOCOUNT ON

at the beginning of each stored procedure you write. This statement should be included in every stored procedure you write.

If you want this feature turned off for your entire server, you can do this by running these statements at your server:

SP_CONFIGURE 'user options', 512
RECONFIGURE

You may or may not want to do this for your entire server, as it affects every transaction on your server. For example, some application programs need the count information, otherwise they will not work. If this is the case, you don't want to turn this option for the entire server, but just for the stored procedures you write.

Most commonly, the first statement is used as it is more flexible, and only affects those stored procedures you write. [6.5, 7.0, 2000] Updated 12-6-2005

*****

Keep Transact-SQL transactions as short as possible within a stored procedure. This helps to reduce the number of locks, helping to speed up the overall performance of your SQL Server application.

Two ways to help reduce the length of a transaction are to: 1) break up the entire job into smaller steps (or multiple stored procedures) so each step can be committed as soon as possible; and 2) take advantage of SQL Server statement batches, which acts to reduce the number of round-trips between the client and server. [6.5, 7.0, 2000, 2005] Updated 6-6-2005

*****

When a stored procedure is first executed (and it does not have the WITH RECOMPILE option), it is optimized and a query plan is compiled and cached in SQL Server's buffer. If the same stored procedure is called again from the same connection, it will used the cached query plan instead of creating a new one, often saving time and boosting performance. This may or may not be what you want.

If the query in the stored procedure is exactly the same each time, and the query plan is the same each time, then this is a good thing. But if the query within the stored procedure is dynamic (for example, the WHERE clauses changes from one execution of the stored procedure to the next), then this may not be a good thing, as the query may not be optimized when it is run, and the performance of the query can suffer greatly. This can happen because changes in the query plan may occur, and if you run an incorrect cached query plan for what is essentially a new query, it may not be appropriate and it may cause performance to suffer greatly.

If you know that your query's query plan will vary each time it is run from a stored procedure, you will want to add the WITH RECOMPILE option when you create the stored procedure. This will force the stored procedure to be re-compiled each time it is run, ensuring the query is optimized with the correct query plan each time it is run. Yes, this will circumvent the reuse of cached query plans, hurting performance a little, but it is more desirable than reusing incorrect query plans. [6.5, 7.0, 2000] Updated 12-6-2005

*****

Many stored procedures have the option to accept multiple parameters. This in and of itself is not a bad thing. But what can often cause problems is if the parameters are optional, and the number of parameters varies greatly each time the stored procedure runs. There are two ways to handle this problem, the slow performance way and fast performance way.

If you want to save your development time, but don't care about your application's performance, you can write your stored procedure generically so that it doesn't care how many parameters it gets. The problem with this method is that you may end up unnecessarily joining tables that don't need to be joined based on the parameters submitted for any single execution of the stored procedure.

Another, much better performing way, although it will take you more time to code, is to include IF...ELSE logic in your stored procedure, and create separate queries for each possible combination of parameters that are to be submitted to the stored procedure. This way, you can be sure you query is as efficient as possible each time it runs. [6.5, 7.0, 2000] Updated 12-6-2005

*****

Here's another way to handle the problem of not knowing what parameters your stored procedure might face. In fact, it will probably perform faster than the tip listed above.

Although the above tip is a good starting point, it's not complete. The problem are the query plans, the pre-compilation of stored procedures, that SQL Server does for you. As you know, one of the biggest reasons to use stored procedures instead of ad-hoc queries is the performance gained by using them. The problem that arises with the above tip is that SQL Server will only generate a query-plan for the path taken through your stored procedure when you first call it, not all possible paths.

Let me illustrate this with an example. Consider the following procedure (pre-compilation doesn't really have a huge effect on the queries used here, but these are just for illustration purposes):

CREATE PROCEDURE dbo.spTest (@query bit) AS
IF @query = 0
SELECT * FROM authors
ELSE
SELECT * FROM publishers
GO

Suppose I make my first call to this procedure with the @query parameter set to 0. The query-plan that SQL Server will generate will be optimized for the first query ("SELECT * FROM authors"), because the path followed on the first call will result in that query being executed.

Now, if I next call the stored procedure with @query set to 1, the query plan that SQL Server has in memory will not be of any use in executing the second query, since the query-plan is optimized for the authors table, not the publishers table. Result: SQL Server will have to compile a new query plan, the one needed for the second query. If I next call the procedure with @query set to 0 again, the whole path will have to be followed from the start again, since only one query-plan will be kept in memory for each stored procedure. This will result in sub-optimal performance.

As it happens I have a solution, one that I've used a lot with success. It involves the creation of what I like to call a 'delegator'. Consider again spTest. I propose to rewrite it like this:

CREATE PROCEDURE dbo.spTestDelegator (@query bit) AS
IF @query = 0
EXEC spTestFromAuthors
ELSE
EXEC spTestFromPublishers
GO

CREATE PROCEDURE dbo.spTestFromAuthors AS
SELECT * FROM authors
GO

CREATE PROCEDURE dbo.spTestFromPublishers AS
SELECT * FROM publishers
GO

The result of this restructuring will be that there will always be an optimized query-plan for spTestFromAuthors and spTestFromPublishers, since they only hold one query. The only one getting re-compiled over and over again is the delegator, but since this stored procedure doesn't actually hold any queries, that won't have a noticeable effect on execution time. Of course re-compiling a plan for a simple 'SELECT *' from a single table will not give you a noticeable delay either (in fact, the overhead of an extra stored procedure call may be bigger then the re-compilation of "SELECT * FROM AnyTable"), but as soon as the queries get bigger, this method certainly pays off.

The only downside to this method is that now you have to manage three stored procedures instead of one. This is not that much of a problem though as the different stored procedures can be considered one single 'system', so it would be logical to keep all of them together in the same script, which would be just as easy to edit as a single stored procedure would be. As far as security is concerned, this method shouldn't give you any extra headaches either, as the delegator is the only stored procedure directly called by the client, this is the only one you need to manage permissions on. The rest will only be called by the delegator, which will always work as long as those stored procedures are owned by the same user as the delegator.

I've had large successes using this technique. Recently I developed a (partial full-text) search engine for our reports database, which resulted in a stored procedure that originally ran about 20 seconds. After employing the above technique, the stored procedure only took about 2 seconds to run, resulting in a ten-fold increase in performance! [6.5, 7.0, 2000] Contributed by Jeremy van Dijk. Updated 6-21-2004

*****

While temporary stored procedures can provide a small performance boost in some circumstances, using a lot of temporary stored procedures in your application can actually create contention in the system tables and hurt performance.

Instead of using temporary stored procedures, you may want to consider using the SP_EXECUTESQL stored procedure instead. It provides the same benefits on temporary stored procedures, but it does not store data in the systems tables, avoiding contention problems. [7.0, 2000] Updated 6-6-2005

*****

If you are creating a stored procedure to run in a database other than the Master database, don't use the prefix "sp_" in its name. This special prefix is reserved for system stored procedures. Although using this prefix will not prevent a user defined stored procedure from working, what it can do is to slow down its execution ever so slightly.

The reason for this is that by default, any stored procedure executed by SQL Server that begins with the prefix "sp_", is first attempted to be resolved in the Master database. Since it is not there, time is wasted looking for the stored procedure.

If SQL Server cannot find the stored procedure in the Master database, then it next tries to resolve the stored procedure name as if the owner of the object is "dbo". Assuming the stored procedure is in the current database, it will then execute. To avoid this unnecessary delay, don't name any of your stored procedures with the prefix "sp_". [6.5, 7.0, 2000] Updated 6-12-2006

*****

Before you are done with your stored procedure code, review it for any unused code that you may have forgotten to remove while you were making changes, and remove it. Unused code just adds unnecessary bloat to your stored procedures, although it will not negatively affect performance of the stored procedure. [6.5, 7.0, 2000] Updated 6-12-2006

*****

For best performance, all objects that are called within the same stored procedure should all be owned by the same object owner, preferably dbo, and should also be referred to in the format of object_owner.object_name.

If the object owner's are not specified for objects, then SQL Server must perform name resolution on the objects, which causes a performance hit.

And if objects referred to in the stored procedure have different owners, SQL Server must check object permissions before it can access any object in the database, which adds unnecessary overhead. Ideally, the owner of the stored procedure should own all of the objects referred to in the stored procedure.

In addition, SQL Server cannot reuse a stored procedure "in-memory plan" over if the object owner is not used consistently. If a stored procedure is sometime referred to with its object owner's name, and sometimes it is not, then SQL Server must re-execute the stored procedure, which also hinders performance. [7.0, 2000, 2005] Updated 6-6-2005

*****

When you need to execute a string of Transact-SQL, you should use the sp_executesql stored procedure instead of the EXECUTE statement. Sp_executesql offers two major advantages over EXECUTE. First, it supports parameter substitution, which gives your more options when creating your code. Second, it creates query execution plans that are more likely to be reused by SQL Server, which in turn reduces overhead on the server, boosting performance.

Sp_executesql executes a string of Transact-SQL in its own self-contained batch. When it is run, SQL Server compiles the code in the string into an execution plan that is separate from the batch that contained the sp_executesql and its string.

Learn more about how to use sp_executesql in the SQL Server Books Online. [7.0, 2000] Updated 6-12-2006

*****

* If you include a WITH RECOMPILE clause in a CREATE PROCEDURE or EXECUTE statement.
* If you run sp_recompile for any table referenced by the stored procedure.
* If any schema changes occur to any of the objects referenced in the stored procedure. This includes adding or dropping rules, defaults, and constraints.
* New distribution statistics are generated.
* If you restore a database that includes the stored procedure or any of the objects it references.
* If the stored procedure is aged out of SQL Server's cache.
* An index used by the execution plan of the stored procedure is dropped.
* A major number of INSERTS, UPDATES or DELETES are made to a table referenced by a stored procedure.
* The stored procedure includes both DDL (Data Definition Language) and DML (Data Manipulation Language) statements, and they are interleaved with each other.
* If the stored procedure performs certain actions on temporary tables.

[7.0, 2000] Updated 04-03-2006

*****

One hidden performance problem of using stored procedures is when a stored procedure recompiles too often. Normally, you want a stored procedure to compile once and to be stored in SQL Server's cache so that it can be re-used without it having to recompile each time it is used. This is one of the major benefits of using stored procedures.

But in some cases, a stored procedure is recompiled much more often than it needs to be recompiled, hurting your server's performance. In fact, it is possible for a stored procedure to have to be recompiled while it is executing!

Here are three potential problems you want to look out for when writing stored procedures.

Unnecessary Stored Procedure Recompilations Due to Row Modifications and Automated Statistics Update

If your database has the "Auto Update Statistics" database option turned on, SQL Server will periodically automatically update the index statistics. On a busy database, this could happen many times each hour. Normally, this is a good thing because the Query Optimizer needs current index statistics if it is to make good query plan decisions. One side effect of this is that this also causes any stored procedures that reference these tables to be recompiled. Again, this is normal, as you don't want a stored procedure to be running an outdated query plan. But again, sometimes stored procedures recompile more than they have to. Here are some suggestions on how to reduce some of the unnecessary recompilations:

* Use sp_executesql instead of EXECUTE to run Transact-SQL strings in your stored procedures.
* Instead of writing one very large stored procedure, instead break down the stored procedure into two or more sub-procedures, and call then from a controlling stored procedure.
* If your stored procedure is using temporary tables, use the KEEP PLAN query hint, which is used to stop stored procedure recompilations caused by more than six changes in a temporary table, which is the normal behavior. This hint should only be used for stored procedures than access temporary tables a lot, but don't make many changes to them. If many changes are made, then don't use this hint.

Unnecessary Stored Procedure Recompilations Due to Mixing DDL and DML Statements in the Same Stored Procedure

If you have a DDL (Data Definition Language) statement in your stored procedure, the stored procedure will automatically recompile when it runs across a DML (Data Manipulation Language) statement for the first time. And if you intermix both DDL and DML many times in your stored procedure, this will force a recompilation every time it happens, hurting performance.

To prevent unnecessary stored procedure recompilations, you should include all of your DDL statements at the first of the stored procedure so they are not intermingled with DML statements.

Unnecessary Stored Procedure Recompilations Due to Specific Temporary Table Operations

Improper use of temporary tables in a stored procedure can force them to be recompiled every time the stored procedure is run. Here's how to prevent this from happening:

* Any references to temporary tables in your stored procedure should only refer to tables created by that stored procedure, not to temporary tables created outside your stored procedure, or in a string executed using either the sp_executesql or the EXECUTE statement.
* All of the statements in your stored procedure that include the name of a temporary table should appear syntactically after the temporary table.
* The stored procedure should not declare any cursors that refer to a temporary table.
* Any statements in a stored procedure that refer to a temporary table should precede any DROP TABLE statement found in the stored procedure.
* The stored procedure should not create temporary tables inside a control-of-flow statement.

[7.0, 2000] Updated 04-03-2006 More from Microsoft

*****

To find out if your SQL Server is experiencing excessive recompilations of stored procedures, a common cause of poor performance, create a trace using Profiler and track the SP:Recompile event. A large number of recompilations should be an indicator if you potentially have a problem. Identify which stored procedures are causing the problem, and then take correction action (if possible) to reduce or eliminate these excessive recompilations. [7.0, 2000] Updated 04-03-2006

*****

Stored procedures can better boost performance if they are called via Microsoft Transaction Server (MTS) instead of being called directly from your application. A stored procedure can be reused from the procedure cache only if the connection settings calling the stored procedure are the same. If different connections call a stored procedure, SQL Server must load a separate copy of the stored procedure for each connection, which somewhat defeats the purpose of stored procedures.

But if the same connection calls a stored procedure, it can be used over and over from the procedure cache. The advantage of Transaction Server is that it reuses connections, which means that stored procedures can be reused more often. If you write an application where every user opens their own connection, then stored procedures cannot be reused as often, reducing performance. [7.0, 2000] Updated 04-03-2006

*****

Avoid nesting stored procedures, although it is perfectly legal to do so. Nesting not only makes debugging more difficult, it makes it much more difficult to identify and resolve performance-related problems. [6.5, 7.0, 2000] Updated 04-03-2006

*****

If you use input parameters in your stored procedures, you should validate all of them at the beginning of your stored procedure. This way, if there is a validation problem and the client applications needs to be notified of the problem, it happens before any stored procedure processing takes place, preventing wasted effort and boosting performance. [6.5, 7.0, 2000] Updated 6-12-2006

*****

When calling a stored procedure from your application, it is important that you call it using its fully qualified name. Such as:

exec database_name.dbo.myProcedure

instead of:

exec myProcedure

Why? There are a couple of reasons, one of which relates to performance. First, using fully qualified names helps to eliminate any potential confusion about which stored procedure you want to run, helping to prevent bugs and other potential problems.

But more importantly, doing so allows SQL Server to access the stored procedures execution plan more directly, and in turn, speeding up the performance of the stored procedure. Yes, the performance boost is very small, but if your server is running tens of thousands or more stored procedures every hour, these little time savings can add up. [7.0, 2000] Updated 6-12-2006

*****

If a stored procedure needs to return only a single value, and not a recordset, consider returning the single value as an output statement. While output statements are generally used for error-checking, they can actually be used for any reason you like. Returning a single value as at output statement is faster than returning a single value as part of a recordset. [6.5, 7.0, 2000] Updated 6-12-2006

*****

One way to help ensure that stored procedures are reused from execution to execution of the same stored procedure is to ensure that any SET options, database options, or SQL Server configuration options don't change from execution to execution of the same stored procedure. If they do, then SQL Server may consider these same stored procedures to be different, and not be able to reuse the current query plan stored in cache.

Some examples of this include when you change the language used by the stored procedure (using SET) and if you change the Dateformat (using SET). [7.0, 2000] Updated 6-12-2006

*****

If you find that a particular stored procedure recompiles every time it executes, and you have determined that there is nothing you can do about the recompiles, and if that stored procedure is very large, consider the following option. Try to determine what part or parts of the stored procedure is causing the recompiles. Once you have done this, break out this troublesome code into its own stored procedure, and then call this stored procedure from the main stored procedure. The advantage of this is that is it takes much less time to recompile a smaller stored procedure than a larger stored procedure. [7.0, 2000] Updated 6-12-2006

Query optimizer

I've found this interesting article for you guys who want optiomize your query.

Enjoy reading.


This tip may sound obvious to most of you, but I have seen professional developers, in two major SQL Server-based applications used worldwide, not follow it. And that is to always include a WHERE clause in your SELECT statement to narrow the number of rows returned. If you don't use a WHERE clause, then SQL Server will perform a table scan of your table and return all of the rows. In some case you may want to return all rows, and not using a WHERE clause is appropriate in this case. But if you don't need all the rows returned, use a WHERE clause to limit the number of rows returned.

By returning data you don't need, you are causing SQL Server to perform I/O it doesn't need to perform, wasting SQL Server resources. In addition, it increases network traffic, which can also lead to reduced performance. And if the table is very large, a table scan will lock the table during the time-consuming scan, preventing other users from accessing it, hurting concurrency.

Another negative aspect of a table scan is that it will tend to flush out data pages from the cache with useless data, which reduces SQL Server's ability to reuse useful data in the cache, which increases disk I/O and hurts performance. [6.5, 7.0, 2000] Updated 1-24-2006

*****

In a WHERE clause, the various operators used directly affect how fast a query is run. This is because some operators lend themselves to speed over other operators. Of course, you may not have any choice of which operator you use in your WHERE clauses, but sometimes you do.

Here are the key operators used in the WHERE clause, ordered by their performance. Those operators at the top will produce results faster than those listed at the bottom.

  • =
  • >, >=, <, <=
  • LIKE
  • <>

This lesson here is to use = as much as possible, and <> as least as possible. [6.5, 7.0, 2000] Updated 1-24-2006

*****

In a WHERE clause, the various operands used directly affect how fast a query is run. This is because some operands lend themselves to speed over other operands. Of course, you may not have any choice of which operand you use in your WHERE clauses, but sometimes you do.

Here are the key operands used in the WHERE clause, ordered by their performance. Those operands at the top will produce results faster than those listed at the bottom.

  • A single literal used by itself on one side of an operator.
  • A single column name used by itself on one side of an operator, a single parameter used by itself on one side of an operator.
  • A multi-operand expression on one side of an operator.
  • A single exact number on one side of an operator.
  • Other numeric number (other than exact), date, and time.
  • Character data, NULLs.

The simpler the operand, and using exact numbers, provides the best overall performance. [6.5, 7.0, 2000] Updated 1-24-2006

*****

If a WHERE clause includes multiple expressions, there is generally no performance benefit gained by ordering the various expressions in any particular order. This is because the SQL Server Query Optimizer does this for you, saving you the effort. There are a few exceptions to this, which are discussed on this web site. [7.0, 2000] Added 1-24-2006

*****

By default, some developers, especially those who have not worked with SQL Server before, routinely include code similar to this in their WHERE clauses when they make string comparisons:

SELECT column_name FROM table_name
WHERE LOWER(column_name) = 'name'

In other words, these developers are making the assuming that the data in SQL Server is case-sensitive, which it generally is not. If your SQL Server database is not configured to be case sensitive, you don't need to use LOWER or UPPER to force the case of text to be equal for a comparison to be performed. Just leave these functions out of your code. This will speed up the performance of your query, as any use of text functions in a WHERE clause hurts performance.

But what if your database has been configured to be case-sensitive? Should you then use the LOWER and UPPER functions to ensure that comparisons are properly compared? No. The above example is still poor coding. If you have to deal with ensuring case is consistent for proper comparisons, use the technique described below, along with appropriate indexes on the column in question:

SELECT column_name FROM table_name
WHERE column_name = 'NAME' or column_name = 'name'

This code will run much faster than the first example. [6.5, 7.0, 2000] Updated 1-24-2006

*****

Try to avoid WHERE clauses that are non-sargable. The term "sargable" (which is in effect a made-up word) comes from the pseudo-acronym "SARG", which stands for "Search ARGument," which refers to a WHERE clause that compares a column to a constant value. If a WHERE clause is sargable, this means that it can take advantage of an index (assuming one is available) to speed completion of the query. If a WHERE clause is non-sargable, this means that the WHERE clause (or at least part of it) cannot take advantage of an index, instead performing a table/index scan, which may cause the query's performance to suffer.

Non-sargable search arguments in the WHERE clause, such as "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'" generally prevents (but not always) the query optimizer from using an index to perform a search. In addition, expressions that include a function on a column, expressions that have the same column on both sides of the operator, or comparisons against a column (not a constant), are not sargable.

But not every WHERE clause that has a non-sargable expression in it is doomed to a table/index scan. If the WHERE clause includes both sargable and non-sargable clauses, then at least the sargable clauses can use an index (if one exists) to help access the data quickly.

In many cases, if there is a covering index on the table, which includes all of the columns in the SELECT, JOIN, and WHERE clauses in a query, then the covering index can be used instead of a table/index scan to return a query's data, even if it has a non-sargable WHERE clause. But keep in mind that covering indexes have their own drawbacks, such as producing very wide indexes that increase disk I/O when they are read.

In some cases, it may be possible to rewrite a non-sargable WHERE clause into one that is sargable. For example, the clause:

WHERE SUBSTRING(firstname,1,1) = 'm'

Can be rewritten like this:

WHERE firstname like 'm%'

Both of these WHERE clauses produce the same result, but the first one is non-sargable (it uses a function) and will run slow, while the second one is sargable, and will run much faster.

WHERE clauses that perform some function on a column are non-sargable. On the other hand, if you can rewrite the WHERE clause so that the column and function are separate, then the query can use an available index, greatly boosting performance. For example:

Function Acts Directly on Column, and Index Cannot Be Used:

SELECT member_number, first_name, last_name
FROM members
WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21

Function Has Been Separated From Column, and an Index Can Be Used:

SELECT member_number, first_name, last_name
FROM members
WHERE dateofbirth <>

Each of the above queries produces the same results, but the second query will use an index because the function is not performed directly on the column, as it is in the first example. The moral of this story is to try to rewrite WHERE clauses that have functions so that the function does not act directly on the column.

WHERE clauses that use NOT are not sargable, but can often be rewritten to remove the NOT from the WHERE clause, for example:

WHERE NOT column_name > 5

To:

WERE column_name <= 5

Each of the above clauses produces the same results, but the second one is sargable.

If you don't know if a particular WHERE clause is sargable or non-sargable, check out the query's execution plan in Query Analyzer. Doing this, you can very quickly see if the query will be using index lookups or table/index scans to return your results.

With some careful analysis, and some clever thought, many non-sargable queries can be written so that they are sargable. Your goal for best performance (assuming it is possible) is to get the left side of a search condition to be a single column name, and the right side an easy to look up value. [6.5, 7.0, 2000] Updated 1-24-2006

*****

If you currently have a query that uses NOT IN, which offers poor performance because the SQL Server optimizer has to use a nested table scan to perform this activity, instead try to use one of the following options instead, all of which offer better performance:

  • Use EXISTS or NOT EXISTS.
  • Use IN.
  • Perform a LEFT OUTER JOIN and check for a NULL condition.

[6.5, 7.0, 2000] Updated 1-24-2006

*****

When you have a choice of using the IN or the EXISTS clause in your Transact-SQL, you will generally want to use the EXISTS clause, as it is usually more efficient and performs faster. [6.5, 7.0, 2000] Updated 1-24-2006

*****

If you run into a situation where a WHERE clause is not SARGable because of the use of a function on the right side of an equality sign (and there is no other way to rewrite the WHERE clause), consider creating an index on a computed column instead. This way, you avoid the non-SARGable WHERE clause altogether, using the results of the function in your WHERE clause instead.

Because of the additional overhead required for indexes on computed columns, you will only want to do this if you need to run this same query over and over in your application, thereby justifying the overhead of the indexed computed column. [2000] Updated 2-5-2007

*****

If you find that SQL Server uses a TABLE SCAN instead of an INDEX SEEK when you use an IN or OR clause as part of your WHERE clause, even when those columns are covered by an index, consider using an index hint to force the Query Optimizer to use the index.

For example:

SELECT * FROM tblTaskProcesses WHERE nextprocess = 1 AND processid IN (8,32,45)

takes about 3 seconds, while:

SELECT * FROM tblTaskProcesses (INDEX = IX_ProcessID) WHERE nextprocess = 1 AND processid IN (8,32,45)

returns in under a second. [7.0, 2000] Updated 6-21-2004 Contributed by David Ames

*****

If you use LIKE in your WHERE clause, try to use one or more leading characters in the clause, if possible. For example, use:

LIKE 'm%'

Not:

LIKE '%m'

If you use a leading character in your LIKE clause, then the Query Optimizer has the ability to potentially use an index to perform the query, speeding performance and reducing the load on SQL Server.

But if the leading character in a LIKE clause is a wildcard, the Query Optimizer will not be able to use an index, and a table scan must be run, reducing performance and taking more time.

The more leading characters you can use in the LIKE clause, the more likely the Query Optimizer will find and use a suitable index. [6.5, 7.0, 2000] Updated 3-20-2006

*****

If your application needs to retrieve summary data often, but you don't want to have the overhead of calculating it on the fly every time it is needed, consider using a trigger that updates summary values after each transaction into a summary table.

While the trigger has some overhead, overall, it may be less that having to calculate the data every time the summary data is needed. You may have to experiment to see which method is fastest for your environment. [6.5, 7.0, 2000] Updated 3-20-2006

*****

When you have a choice of using the IN or the BETWEEN clauses in your Transact-SQL, you will generally want to use the BETWEEN clause, as it is much more efficient. For example:

SELECT customer_number, customer_name
FROM customer
WHERE customer_number in (1000, 1001, 1002, 1003, 1004)

Is much less efficient than this:

SELECT customer_number, customer_name
FROM customer
WHERE customer_number BETWEEN 1000 and 1004

Assuming there is a useful index on customer_number, the Query Optimizer can locate a range of numbers much faster (using BETWEEN) than it can find a series of numbers using the IN clause (which is really just another form of the OR clause). [6.5, 7.0, 2000] Updated 3-20-2006

*****

If possible, try to avoid using the SUBSTRING function in your WHERE clauses. Depending on how it is constructed, using the SUBSTRING function can force a table scan instead of allowing the optimizer to use an index (assuming there is one). If the substring you are searching for does not include the first character of the column you are searching for, then a table scan is performed.

If possible, you should avoid using the SUBSTRING function and use the LIKE condition instead, for better performance.

Instead of doing this:

WHERE SUBSTRING(column_name,1,1) = 'b'

Try using this instead:

WHERE column_name LIKE 'b%'

If you decide to make this choice, keep in mind that you will want your LIKE condition to be sargable, which means that you cannot place a wildcard in the first position. [6.5, 7.0, 2000] Updated 3-20-2006

*****

Where possible, avoid string concatenation in your Transact-SQL code, as it is not a fast process, contributing to overall slower performance of your application. [6.5, 7.0, 2000] Updated 8-7-2006

*****

Generally, avoid using optimizer hints in your WHERE clauses. This is because it is generally very hard to outguess the Query Optimizer. Optimizer hints are special keywords that you include with your query to force how the Query Optimizer runs. If you decide to include a hint in a query, this forces the Query Optimizer to become static, preventing the Query Optimizer from dynamically adapting to the current environment for the given query. More often than not, this hurts, not helps performance.

If you think that a hint might be necessary to optimize your query, be sure you do all of the following first:

  • Update the statistics on the relevant tables.
  • If the problem query is inside a stored procedure, recompile it.
  • Review the search arguments to see if they are sargable, and if not, try to rewrite them so that they are sargable.
  • Review the current indexes, and make changes if necessary.

If you have done all of the above, and the query is not running as you expect, then you may want to consider using an appropriate optimizer hint.

If you haven't heeded my advice and have decided to use some hints, keep in mind that as your data changes, and as the Query Optimizer changes (through service packs and new releases of SQL Server), your hard-coded hints may no longer offer the benefits they once did. So if you use hints, you need to periodically review them to see if they are still performing as expected. [6.5, 7.0, 2000] Updated 8-7-2006

*****

If you have a WHERE clause that includes expressions connected by two or more AND operators, SQL Server will evaluate them from left to right in the order they are written. This assumes that no parenthesis have been used to change the order of execution. Because of this, you may want to consider one of the following when using AND:

  • Locate the least likely true AND expression first. This way, if the AND expression is false, the clause will end immediately, saving time.
  • If both parts of an AND expression are equally likely being false, put the least complex AND expression first. This way, if it is false, less work will have to be done to evaluate the expression.

You may want to consider using Query Analyzer to look at the execution plans of your queries to see which is best for your situation. [6.5, 7.0, 2000] Updated 8-7-2006

*****

If you want to boost the performance of a query that includes an AND operator in the WHERE clause, consider the following:

  • Of the search criterions in the WHERE clause, at least one of them should be based on a highly selective column that has an index.
  • If at least one of the search criterions in the WHERE clause is not highly selective, consider adding indexes to all of the columns referenced in the WHERE clause.
  • If none of the column in the WHERE clause are selective enough to use an index on their own, consider creating a covering index for this query.

[7.0, 2000] Updated 8-7-2006

*****

The Query Optimizer will perform a table scan or a clustered index scan on a table if the WHERE clause in the query contains an OR operator and if any of the referenced columns in the OR clause are not indexed (or does not have a useful index). Because of this, if you use many queries with OR clauses, you will want to ensure that each referenced column in the WHERE clause has a useful index. [7.0, 2000] Updated 8-7-2006

*****

A query with one or more OR clauses can sometimes be rewritten as a series of queries combined with a UNION ALL statement in order to boost the performance of the query. For example, let's take a look at the following query:

SELECT employeeID, firstname, lastname
FROM names
WHERE dept = 'prod' or city = 'Orlando' or division = 'food'

This query has three separate conditions in the WHERE clause. In order for this query to use an index, then there must be an index on all three columns found in the WHERE clause.

This same query can be written using UNION ALL instead of OR, like this example:

SELECT employeeID, firstname, lastname FROM names WHERE dept = 'prod'
UNION ALL
SELECT employeeID, firstname, lastname FROM names WHERE city = 'Orlando'
UNION ALL
SELECT employeeID, firstname, lastname FROM names WHERE division = 'food'

Each of these queries will produce the same results. If there is only an index on dept, but not the other columns in the WHERE clause, then the first version will not use any index and a table scan must be performed. But the second version of the query will use the index for part of the query, not for all of the query.

Admittedly, this is a very simple example, but even so, it does demonstrate how rewriting a query can affect whether or not an index is used or not. If this query was much more complex, then the approach of using UNION ALL might be must more efficient, as it allows you to tune each part of the index individually, something that cannot be done if you use only ORs in your query.

Note that I am using UNION ALL instead of UNION. The reason for this is to prevent the UNION statement from trying to sort the data and remove duplicates, which hurts performance. Of course, if there is the possibility of duplicates, and you want to remove them, then of course you can use just UNION.

If you have a query that uses ORs and it not making the best use of indexes, consider rewriting it as a UNION ALL, and then testing performance. Only through testing can you ensure one version of your query will be faster than another. [7.0, 2000] Updated 8-7-2006

*****

Don't use ORDER BY in your SELECT statements unless you really need to, as it adds a lot of extra overhead. For example, perhaps it may be more efficient to sort the data at the client than at the server. In other cases, perhaps the client doesn't even need sorted data to achieve its goal. The key here is to remember that you shouldn't automatically sort data, unless you know it is necessary. [6.5, 7.0, 2000] Updated 8-7-2006

*****

Whenever SQL Server has to perform a sorting operation, additional resources have to be used to perform this task. Sorting often occurs when any of the following Transact-SQL statements are executed:

  • ORDER BY.
  • GROUP BY.
  • SELECT DISTINCT.
  • UNION.
  • CREATE INDEX (generally not as critical as happens much less often).

In many cases, these commands cannot be avoided. On the other hand, there are few ways that sorting overhead can be reduced. These include:

  • Keep the number of rows to be sorted to a minimum. Do this by only returning those rows that absolutely need to be sorted.
  • Keep the number of columns to be sorted to the minimum. In other words, don't sort more columns that required.
  • Keep the width (physical size) of the columns to be sorted to a minimum.
  • Sort column with number datatypes instead of character datatypes.

When using any of the above Transact-SQL commands, try to keep the above performance-boosting suggestions in mind. [6.5, 7.0, 2000] Updated 3-20-2006

*****

If you have to sort by a particular column often, consider making that column a clustered index. This is because the data is already presorted for you and SQL Server is smart enough not to resort the data. [6.5, 7.0, 2000] Updated 2-19-2007

*****

If your WHERE clause includes an IN operator along with a list of values to be tested in the query, order the list of values so that the most frequently found values are placed at the first of the list, and the less frequently found values are placed at the end of the list. This can speed performance because the IN option returns true as soon as any of the values in the list produce a match. The sooner the match is made, the faster the query completes. [6.5, 7.0, 2000] Updated 10-16-2006

*****

If your SELECT statement contains a HAVING clause, write your query so that the WHERE clause does most of the work (removing undesired rows) instead of the HAVING clause do the work of removing undesired rows. Using the WHERE clause appropriately can eliminate unnecessary rows before they get to the GROUP BY and HAVING clause, saving some unnecessary work, and boosting performance.

For example, in a SELECT statement with WHERE, GROUP BY, and HAVING clauses, here's what happens. First, the WHERE clause is used to select the appropriate rows that need to be grouped. Next, the GROUP BY clause divides the rows into sets of grouped rows, and then aggregates their values. And last, the HAVING clause then eliminates undesired aggregated groups. If the WHERE clause is used to eliminate as many of the undesired rows as possible, this means the GROUP BY and the HAVING clauses will have less work to do, boosting the overall performance of the query. [6.5, 7.0, 2000] Updated 10-16-2006

*****

If your application performs many wildcard (LIKE %) text searches on CHAR or VARCHAR columns, consider using SQL Server's full-text search option. The Search Service can significantly speed up wildcard searches of text stored in a database. [7.0, 2000] Updated 10-16-2006

*****

The GROUP BY clause can be used with or without an aggregate function. But if you want optimum performance, don't use the GROUP BY clause without an aggregate function. This is because you can accomplish the same end result by using the DISTINCT option instead, and it is faster.

For example, you could write your query two different ways:

USE Northwind
SELECT OrderID
FROM [Order Details]
WHERE UnitPrice > 10
GROUP BY OrderID

Or,

USE Northwind
SELECT DISTINCT OrderID
FROM [Order Details]
WHERE UnitPrice > 10

Both of the above queries produce the same results, but the second one will use fewer resources and perform faster. [6.5, 7.0, 2000] Updated 2-5-2007

*****

The GROUP BY clause can be sped up if you follow these suggestions:

  • Keep the number of rows returned by the query as small as possible.
  • Keep the number of groupings as few as possible.
  • Don't group redundant columns.
  • If there is a JOIN in the same SELECT statement that has a GROUP BY, try to rewrite the query to use a subquery instead of using a JOIN. If this is possible, performance will be faster. If you have to use a JOIN, try to make the GROUP BY column from the same table as the column or columns on which the set function is used.
  • Consider adding an ORDER BY clause to the SELECT statement that orders by the same column as the GROUP BY. This may cause the GROUP BY to perform faster. Test this to see if is true in your particular situation.

[7.0, 2000] Updated 2-5-2007

*****

Sometimes perception is more important that reality. For example, which of the following two queries is the fastest?

  • A query that takes 30 seconds to run, and then displays all of the required results.
  • A query that takes 60 seconds to run, but displays the first screen full of records in less than 1 second.

Most DBAs would choose the first option as it takes less server resources and performs faster. But from many users' point-of-view, the second one may be more palatable. By getting immediate feedback, the user gets the impression that the application is fast, even though in the background, it is not.

If you run into situations where perception is more important than raw performance, consider using the FAST query hint. The FAST query hint is used with the SELECT statement using this form:

OPTION(FAST number_of_rows)

Where number_of_rows is the number of rows that are to be displayed as fast as possible.

When this hint is added to a SELECT statement, it tells the Query Optimizer to return the specified number of rows as fast as possible, without regard to how long it will take to perform the overall query. Before rolling out an application using this hint, I would suggest you test it thoroughly to see that it performs as you expect. You may find out that the query may take about the same amount of time whether the hint is used or not. If this is the case, then don't use the hint. [7.0, 2000] Updated 2-5-2007

*****

It is fairly common to write a Transact-SQL query to compare a parent table and a child table and find out if there are any parent records that don't have a match in the child table. Generally, this can be done three ways:

Using a NOT EXISTS:

SELECT a.hdr_key
FROM hdr_tbl a
WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)

Using a LEFT JOIN:

SELECT a.hdr_key
FROM hdr_tbl a
LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key
WHERE b.hdr_key IS NULL

Using a NOT IN:

SELECT hdr_key
FROM hdr_tbl
WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)

In each case, the above query will return identical results. But which of these three variations of the same query produces the best performance? Assuming everything else is equal, the best performing version through the worst performing version will be from top to bottom, as displayed above. In other words, the NOT EXISTS variation of this query is generally the most efficient.

I say generally because the indexes found on the tables, along with the number of rows in each table, can influence the results. If you are not sure which variation to try yourself, you can try them all and see which produces the best results in your particular circumstances. [7.0, 2000] Updated 2-19-2007

*****

Be careful when using OR in your WHERE clause, as it is fairly simple to accidentally retrieve much more data than you need, which hurts performance. For example, take a look at the query below:

SELECT companyid, plantid, formulaid
FROM batchrecords
WHERE companyid = '0001' and plantid = '0202' and formulaid = '39988773'
OR
companyid = '0001' and plantid = '0202'

As you can see from this query, the WHERE clause is redundant, as

companyid = '0001' and plantid = '0202' and formulaid = '39988773'

is a subset of

companyid = '0001' and plantid = '0202'

In other words, this query is redundant. Unfortunately, the SQL Server query optimizer isn't smart enough to know this, and will do exactly what you tell it to. What will happen is that SQL Server will have to retrieve all the data you have requested, then in effect do a SELECT DISTINCT to remove redundant rows it unnecessarily finds.

In this case, if you drop this code from the query

OR
companyid = '0001' and plantid = '0202'

then run the query, you will receive the same results, but with much faster performance. [6.5, 7.0, 2000] Updated 2-19-2007

*****

Avoid using variables in the WHERE clause of a query located in a batch file. Let's find out why this may not be a good idea.

First, let's look at the following code:

SELECT employee_id
FROM employees
WHERE age = 30 and service_years = 10

Assuming that both the age and the service_years columns have indexes, and the table has many thousands of records, then SQL Server's query optimizer will select the indexes to perform the query and return results very quickly.

Now, let's look at the same query, but written to be more generic, one that you might find in a generic batch file:

DECLARE @age int
SET @age = "30"
DECLARE @service_years int
SET @service_years = "10"
SELECT employee_id
FROM employees
WHERE age = @age and service_years = @service_years

When the above code is run, even though both the age and the service_years columns have indexes, they may not be used, and a table scan may be used instead, potentially greatly increasing the amount of time for the query to run.

The reason the indexes may not be used is because the Query Analyzer does not know the value of the variables when it selects an access method to perform the query. Because this is a batch file, only one pass is made of the Transact-SQL code, preventing the Query Optimizer from knowing what it needs to know in order to select an access method that uses the indexes.

If you cannot avoid using variables in the WHERE clauses of batch scripts, consider using an INDEX query hint to tell the query optimizer to use the available indexes instead of ignoring them and performing a table scan. If, of course, the indexes are highly selective. If the indexes are not highly selective, then a table scan most likely will be more efficient than using the available indexes.

Another option is not to use a script, but a stored procedure instead. Variables in stored procedures don't cause the problem described above. [6.5, 7.0, 2000] Updated 2-19-2007