static void

SqlServer Queuing

Using a database as a queue is a common requirement. An example is sending emails within a website- it can be slow, error-prone, and you don't want to delay returning a page to the user. So the server processing just queues a request in the database, and a worker process picks it up and tries to execute it.

The real problem is there may be more than one worker process, perhaps running on different servers. By using the table as a queue, they can avoid deadlocks or processing records multiple times.

Comprehensive breakdown of queuing including heap queues, FIFO and LIFO.

Table

Let's have a Created column, and an IsProcessed column. Alternatively we could just delete the rows when they are processed.

CREATE TABLE [dbo].[EmailRequests](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[EmailAddress] [nvarchar](250) NOT NULL,
	[Subject] [nvarchar](50) NOT NULL,
	[Body] [nvarchar](500) NOT NULL,
	[IsProcessed] [bit] NOT NULL,
	[Created] [datetime] NOT NULL,
 CONSTRAINT [PK_EmailRequests] PRIMARY KEY CLUSTERED (	[Id] ASC )
)

The INSERT is just a normal INSERT.

INSERT INTO [EmailRequests]
           ([EmailAddress],[Subject],[Body],[IsProcessed],[Created])
     VALUES
           ('[email protected]','Hello','Spam spam spam',0,CURRENT_TIMESTAMP)

Dequeue

This is a FIFO queue, but the order isn't strict (see this explanation)

with cte as (
 select top(1) [Id], [IsProcessed], [EmailAddress], [Subject], [Body]
 from [EmailRequests] with (ROWLOCK, READPAST)
 where [IsProcessed]= 0
 order by [Created]
)
update cte
	set [IsProcessed] = 1
	output inserted.[Id], inserted.[EmailAddress], inserted.[Subject], inserted.[Body]

To make this a bit more realistic, you could add a [IsEmailSent] column, updated when the emailing succeeds. Then you need a process for dealing with records that are IsProcessed but not IsEmailSent (dequeued, but the email failed). Remember resetting IsProcessed may poison the queue! (A [RetryCount] up to a maximum may help).