Wednesday, March 4, 2009

Linking 64-bit SQL Server 2005 to a 32-bit SQL Server 2000

I had an environment with two SQL Servers 2000, let's name them SQL1 and SQL2. On SQL2 I created a linked server to SQL1 so I was able to run distributed queries between these two servers.

Recently we added a new server, SQL3, which was a 64-bit SQL Server 2005, with the intention to replace SQL2. We moved all the databases from SQL2 to SQL3. On SQL3 we also created linked server to SQL1.

Linked server was created successfully, but when I tried to run my distributed queries from SQL3, an error popped up saying:
Cannot obtain the schema rowset "DBSCHEMA_TABLES_INFO" for OLE DB provider "SQLNCLI" for linked server "". The provider supports the interface, but returns a failure code when it is used.

It took me a while until I found a KB article dealing with this issue. I followed the instructions and solved the problem.

What they say is that you have to manually upgrade a certain set of your SQL Server 2000 system stored procedures. In order to do that, you have to execute Instcat.sql script on your SQL Server 2000. By default, this script is located in C:\Program Files\Microsoft SQL Server\MSSQL\Install.
For a detailed explanation read the Microsoft KB article.

Monday, September 8, 2008

Fighting big transaction log

How to shrink it?

When you face a problem where a transaction log file has eaten all of your disk space and the whole system stopped responding, all you want to do is shrink it as soon as possible. Here is how you do this:

1st:
use master
backup log database name to disk = 'path'
2nd:
use database name
dbcc shrinkfile (log file name, truncateonly)

Or, if the time is crytical or you don't find any space for backup, you can do this (faster, but somewhat risky) procedure:

1st:
use master
backup log database name with truncate_only
2nd:
use database name
dbcc shrinkfile (log file name, truncateonly)
3rd: (This step is optional, but strongly recommended - make a full backup of your database):
use master
backup database database name to disk = 'path'

Why did it grow so big?

Transaction log is an essential part of a SQL Server database. Every single data change that needs to be saved into a database is first being written into a log file and then periodically (when the checkpoint occurs) flush into a data file.

If a system failure occurs, log file is needed to recover the database into a consistent state. During the recovery process, SQL Server reads the log file to determine which transactions are written to the log, but are not yet flush into the data file. Such transactions must be rolled forward. On the other hand, transactions that started, but there is no flag in the log file that they were committed, must be rolled back.

Since every data change needs to be saved into a transaction log file, the log file is thus growing bigger and bigger. If you don't take proper care of it, you could eventually end up with a log file occupying all of your free disk space.

How to keep transaction log size reasonable?

Growing of a transaction log file can be slowed down by performing transaction log backups regularly. When you backup a log file, SQL Server determines which parts of the log file are not needed any more and marks them. This is called truncating the log. By doing so, physical size of the log is not reduced, but the marked parts can be reused later and overwritten by new log data. Therefore, you should set up a backup strategy and it should include performing log backups.

In the first shrinking procedure (shown above) we have thus performed a transaction log backup and saved it to a disk. This also truncated the log, but the file itself didn't change its size.
To shrink the log, we had to use DBCC shrinkfile or DBCC shrinkdatabase command (We did that in step 2). This releases unneeded parts of the log to the operating system. If you have never performed backup of your log file, there wouldn't be any parts of log marked as unneeded and shrinking the log by DBCC commands could not reduce the file size.

In the second proposed solution, we used with truncate_only option, which in fact meant that we didn't want to save the log backup anywhere to disk, but rather just truncate the log. Be very cautious when backing up with truncate_only because doing so breaks the log chain and you cannot make a "regular" log backup after that, which could potentially lead to the loss of data! It is therefore highly recommended to make a full backup immediately after backing up the log with truncate_only option. (That's what we did in step 3).

If you don't need to have point-in-time recovery, then you can keep your transaction log at a reasonable size just by changing the recovery model of your database to simple. (This could be the case with your development database, for example.) When working in simple recovery model, transaction log is being truncated automatically with each checkpoint so you don't have to backup the log (what's more, it is not even possible to backup the log in simple recovery model).

To find out which recovery model your database is using, execute the following command:

select databasepropertyex('database name', 'recovery')

To change the recovery option for your database, run this command:

alter database database name set recovery {full simple bulk_logged}

Thursday, August 7, 2008

What is the DATALENGTH('') ?

I was writing a stored procedure on a SQL Server 2005 database that had to deal with some strings and I couldn't make it work. After hours spent on debugging, I came to this:

select DATALENGTH('')
Result: 0

I was very surprised by this result because I very much beleived that one byte is needed to represent an empty string, so I expected to see 1 as the result.
Then I started searching and found that different versions of SQL Server would give me different result of that same query. SQL Server 6.5 would thus give 1, but every version after that would say 0.

If it is really necessary you can force your database to behave like in one of the older versions of SQL Server. For example:

EXEC sp_dbcmptlevel 'MyDatabaseName', '65';
GO

select DATALENGTH('')
Result: 1

By executing sp_dbcmptlevel you actually change the value of the compatibility level database option.
What behavioral differences exist between 60, 65, 70, 80 and 90 compatibility level you can see on http://msdn.microsoft.com/en-us/library/ms178653.aspx