About TempDb log

by Andrei Hetel 29. March 2010 03:17

TempDb is a system database available to all users and databases belonging to a SQL Server instance. As the name suggest is a temporary store. When TempDb LOG is growing too big or too fast (I've seen a log occupying 200GB - 97% full) obviously there is a problem with your database. Solutions that you may found on the net are:

 

  • Shrink tempDB transaction log - using log backup for example, inside a job
  • Restart SQL Server - that will recreate tempdb and tempdb log with default initial size

 

 

Both can be used just in the case you don't want to admit that your database has a problem. You should start looking first at the objects that generate excessive use of TempDb like:

 

 

  • Triggers - yes, because inserted and deleted tables are created in TempDB
  • Temporary Tables or Global Temporary Tables (e.g. CREATE TABLE #MyTemp(col1 INT)).
  • Table variables (DECLARE @MyTemp TABLE (col1 INT)) - Yes they can be created in TempDB!
  • XML - sp_xml_preparedocument creates a work table in tempdb.
  • Queries can store intermediate results for joins, aggregates (GROUP BY) and sorting (ORDER BY).
  • Cursors, where static type is the worse possible.

Note: Personally, I can't see any reason somebody would ever use a Global Temporary Table!

 

Use the following command to list all temp tables:

 

SELECT name FROM tempdb..sysobjects
WHERE NAME LIKE '#%'

 

Output, like in the following image:

TempDB Objects

 

Easiest method to monitor the logs is to issue the following command:

 

DBCC SQLPERF(LOGSPACE)

 

Result looks like in the following picture, you are interested on line 2 :

 

DBCC SQLPERF output

 

 

As a conclusion, I would recommend 2 things:

  • Review your code and try to reduce the use tempdb (see above who's using temp db).
  • Estimate the amount of space required by tempdb database, and set initial values for it - we don't want extents.

 

 

SQL Server - space used by every table

by Andrei Hetel 26. March 2010 16:41

This particular script I consider very useful especially when you 'inherit' a database that need tuning. I pick the idea long time ago somewhere from the net and I simplify it as much as I could. It contains only a couple of lines. During the years was used against SQL Server 2000, 2005 and 2008. First, create a table:

 

CREATE TABLE _tmpspace
(
  [name] SYSNAME,
  [rows] INT,
  reserved VARCHAR(50),
  data VARCHAR(50),
  index_size VARCHAR(50),
  unused VARCHAR(50)
)

 

A stored procedure is needed:

 

CREATE PROCEDURE [dbo].[_TablesSpaceUsed]
AS
BEGIN
  SET NOCOUNT ON;

  DBCC updateusage(0) WITH NO_INFOMSGS
  TRUNCATE TABLE _tmpspace
  EXEC sp_msforeachtable 'INSERT _tmpspace EXEC sp_spaceused ''?'''
  SELECT * FROM _tmpspace ORDER BY [name]
END

 

Please note the use of undocumented stored procedure msforeachtable. Another interesting procedure is sp_MSforeachdb, but I'll write about it some other time.

 

SQL Server unique index on a column that contain NULL values

by Andrei Hetel 20. March 2010 08:09

Best way to explain this is to use an example, a very simple example.

 

CREATE TABLE Agenda (
    AgendaId [int] IDENTITY(1,1) NOT NULL,
    FirstName VARCHAR(50),
    LastName VARCHAR(50),
    ExternalId INT,
CONSTRAINT [PK_Agenda] PRIMARY KEY CLUSTERED ([AgendaId] ASC))

 

AgendaId column is the primary key of the table and ExternalId is a column that link to an external system. For some of the records, ExternalId field is NULL (synchronization not made with the external system). Our job is to make sure that values from our columns are unique ignoring of course NULL values. Let's insert some data into our table.

 

INSERT INTO Agenda
SELECT 'John', 'Doe', 100
UNION
SELECT 'Cameron', 'Smith', NULL
UNION
SELECT 'Ken', 'McGregor', NULL
UNION
SELECT 'Judy', 'Knight', 200
UNION
SELECT 'Jan', 'McQueen', 300

 

Let's try to create the UNIQUE index on ExternalId column:

 

CREATE UNIQUE NONCLUSTERED INDEX [MyExternalId] ON [dbo].[Agenda]
([ExternalId] ASC)
-- Error message:
Msg 1505, Level 16, State 1, Line 1
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.Agenda' and the index name 'MyExternalId'. The duplicate key value is (NULL).

 

Next step is to create a calculated column (please note the use of our primary key), as follows:

 

ALTER TABLE Agenda ADD ExternalIdUnique AS
(CASE WHEN ExternalId IS NULL THEN AgendaId ELSE ExternalId END)

 

There is no problem to create the unique index on the calculated column:

 

CREATE UNIQUE NONCLUSTERED INDEX [MyExternalId] ON [dbo].[Agenda]
([ExternalIdUnique] ASC)

-- Test the index...
INSERT INTO Agenda VALUES ('test', 'failed', 100)
GO
INSERT INTO Agenda VALUES ('OK', 'PASSED', NULL)
GO

 

Basically, that's all, pretty simple.

 

SQL Server - couple of words about locking

by Andrei Hetel 18. March 2010 18:24

This article is related to a post written a while ago about NOLOCK hint. Basically what I want to say is that waiting for a lock is not actually a deadlock. The process of acquiring locks is actually pretty normal and is indeed required by most systems - a reason for using a RDBMS instead of an excel file to store our data.

 

Simple explanation for a deadlock

 

A classic deadlock example is when user1 has a lock on table1 and wants to acquire a lock on table2; user2 has a lock on table2 and wants to acquire a lock on table1. In such a situation, SQL Server will choose a deadlock victim, one transaction continue and the other one is rollback.

 

To reduce the chance of a deadlock, you should minimize the duration of transactions!

 

Shared lock

 

When a SELECT statement is executed, its transaction obtains a shared lock on the data. Another SELECT that tries to read the same piece of data is permitted to read; but a transaction that tries to update the data will be prevented from doing so until the shared lock is released. How long a shared lock is maintained?

  • when transaction isolation level is READ_COMMITTED lock against a row/page is releases when stepping to the next row/page
  • when transaction isolation level is SERIALIZABLE or REPEATABLE_READ lock is maintained for the duration of the query.
  • READ_UNCOMMITTED is not requesting any locks
SQL Server default isolation level is READ_COMMITTED.

 

SQL Server jobs

by Andrei Hetel 17. March 2010 10:56

Recently I had to do some tasks on a SQL Server database with performance problems. Actually, huge performance problems caused by about 40 server jobs averaging 5-6 job steps. Why so many jobs? Because developers were programmers, not DBAs - and a programmer usually think at a row-by-row level. Most of jobs can be replaced with triggers, but this is another story.

In a scenario like this, a big problem could be to see if a certain stored procedure is used or not. Rather than iterating through all job steps, you can write a query something like:

 

SELECT
    j.Name AS jobName,
    js.Step_id,
    js.STEP_NAME AS StepName,
    js.Last_Run_Date,
    js.Last_Run_Time,
    js.Database_Name,
    js.command,
    j.enabled AS JobEnabled
FROM
    msdb..SYSJOBS j, msdb..SYSJOBSTEPS js
WHERE
    j.JOB_ID=js.JOB_ID
    -- AND js.command like '%My stored procedure%'

 

Above query will show all the job. Modify the last line to find the stored procedure you're looking for. Please note that jobs are saved in msdb system database.