DoEvents with SQL Server 2000 and Extended Procedures
In this article, we will leverage Extended Stored Procedures
to extend the functionality of SQL Server and give a starting point to
experiment with Extended Stored Procedures. Extended Stored Procedures are an
extension to SQL Server that allow you to register DLLs in order to perform
tasks that would otherwise not be possible with T-SQL. This article does not go
into the details of creating your own Extended Stored Procedure, but introduces
you to some of the tools you will need to explore existing DLLs and turn them
into Extended Stored Procedures.
The need to yield
As a developer, I was used to the magic DoEvents function that
allows a thread to give up some of its computing cycles to other threads (for
better or for worse). Similarly, I have been looking to allow a few
low-priority SQL statements to yield execution time to more important SQL
statements. For instance, long running queries called by certain non-critical
reports could be created in a way that would use less CPU time relative to more
important queries. In my quest, I investigated the WAITFOR DELAY statement,
which allows you to pause your T-SQL for a certain time. Using WAITFOR is a
good option, however the pause is unconditional; it does not allow the thread
to use the CPU at full capacity if no other statements are running and it does
not allow you to use the CPU at all during the pause.
Using Dependency Walker
Knowing
that Visual Basic provides a DoEvents function, I loaded MSVBVM60.DLL (under
system32) in Dependency Walker, which you can install as a support tool and
located in the Windows 2000 installation CD. Dependency Walker shows you the
list of methods this DLL provides, and as you can see, rtcDoEvents is listed.
It is interesting to look at this file since many more functions are available
and could potentially be leveraged within SQL Server.
You can also experiment with other DLLs and see what functions
may be available to you to extend SQL Server’s functionality without having to
create a single line of code. In addition, these DLLs have been for the most
part tested, which reduces the probability of causing SQL Server instability.
Registering and using rtcDoEvents
The next step is to register rtcDoevents. This is done within
Query Analyzer with the following script. Since this script should be executed
on the master database, we are using USE MASTER. This creates a stored
procedure in master called rtcDoEvents, which will attempt to call a function
within MSVBVM60.DLL with the same name. Thus, creating a stored procedure and
calling it DoEvents would not work in this case; the names must match.
USE MASTER
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
exec sp_addextendedproc N'rtcDoEvents', N'MSVBVM60.dll'
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
Finally, we are creating a completely useless T-SQL query with
only one purpose in life: demonstrate what rtcDoEvents can do for you. The
query has two variables: @iMAX and @iNBR_DOEVENTS. The first variable is used
to control the number of loops the query will execute. We are choosing to
execute one million loops, which simulates roughly eight seconds of load with a
CPU locked at 100%. The second variable is used to control the number of
DoEvents we will simulate. The more DoEvents are being generated, the longer
the script will take to execute. Here is the script:
DECLARE @iMAX int
DECLARE @iNBR_DOEVENTS int
SET @iMAX = 1000000 --
Number of iterations
SET @iNBR_DOEVENTS = 0 --
Number of DoEvents to generate
DECLARE @i int
SET @i=0
WHILE @i<@iMAX
BEGIN
--
Increase the counter
SET
@i = @i + 1
--
Should we fire DoEvents at all?
IF
@iNBR_DOEVENTS>0
--
If yes, is it time to fire the DoEvents?
if
(ROUND(@i / (@iMAX/@iNBR_DOEVENTS), 10) * (@iMAX/@iNBR_DOEVENTS) = @i)
BEGIN
Print
'DoEvent Number ' + CAST( @i / (@iMAX/@iNBR_DOEVENTS) as VARCHAR(10))
--
I guess so… call it now
EXEC
master..rtcDoEvents
END
END
As you can see, there is not much going on in this query. We
are calling the rtcDoEvents stored procedure in master when we need to call
VB’s DoEvents. Et Voila. Next, let’s take a look at the things we can do with
this.
Impact of DoEvents on SQL Server Threads

In
order to see the impact DoEvents can have on my query, I opened two connections
running the query listed above. I ran two tests, for which the results are
shown in PerfMon. Perfmon shows the two threads of execution for our queries
and displays the %CPU Time taken by each thread. The curves on the left
represent Test 1 and the curves in the middle represent Test 2.
In Test 1, we set the first query to execute with
@iNBR_DOEVENTS = 1000 and the second query with 0. Perfmon shows that the first
query (in blue) yields most of its CPU cycle to query two (in black) as long as
query two is running. Query two executes the loops faster than query 1, which
was our goal.
In Test 2, we changed query one’s parameter to 100 instead of
1000. Now we can see the blue line (query 1) still yielding to query two, but
to a much lesser extent. Query 2 still runs a little bit faster than query 1,
but cannot obtain as much CPU time as it did in test 1.
One more important note is the impact of the DoEvents method
call on the overall performance of the query. When running the query with
@iNBR_DOEVENTS = 0, you execute the query with optimal performance (8 seconds
on my computer). When you set this value to 100, the query will run in 15
seconds when no other queries are running. This is of course very costly from a
performance standpoint. When running with another query (as shown in Test 2),
it takes about 30 seconds. This demonstrates that there is a performance hit by
using DoEvents, but that it allows a query to yield some of its CPU time to
other queries only when necessary (unlike WAITFOR).
Conclusion
As we have seen, it may be possible to use the DoEvents
provided by Visual Basic through the use of Extended Stored Procedures in SQL
Server. Of course this technique is shown for demonstration purposes only and
may not be suitable for certain production environments. Nevertheless,
leveraging existing DLLs within SQL Server is a great way to cut down your cost
of ownership and gives you a certain level of stability since these DLLs have
been already tested for you. If certain DLLs offer functionality that is too
complex to incorporate directly within SQL Server, you can also create a
wrapper DLL that simplifies the calling mechanism. Have fun!