DOWNLOAD THE CODE:
Download the Code 41044.zip

Xp_execresultset
T-SQL programmers commonly want to execute dynamic code longer than 4000 characters and to execute code constructed by a query. But sp_executesql limits code to 4000 bytes—the nvarchar data type's limitation. And although running queries against system tables and INFORMATION_SCHEMA views is a convenient way to construct administrative code, you have to run the query and copy the code from the Query Analyzer Results pane to the Execution pane before you execute it.

The undocumented xp_execresultset extended stored procedure provides a way for programmers to satisfy both desires. As an example of executing dynamic code longer than 4000 characters, let's store some long, dynamically constructed code in separate rows of a table. Running the code in Listing 5, page 21, creates the TSQLCommands table and populates it with three rows that together construct a valid statement. Note a couple of important things here. First, you have to design the table where you store the command elements so that you'll be able to sort them correctly. I used the cmdrow column to store a sequence of numbers that determine the position of the command's element within the complete command. Second, you can't break a word in the middle and continue it in the next row. Before executing xp_execresultset, run the following query:

SELECT cmdtext FROM TSQLCommands 
  WHERE cmdid = 1 ORDER BY cmdrow

The result is the valid T-SQL command that Figure 2 shows.

The xp_execresultset procedure accepts two arguments: an nvarchar string containing the query whose results you're executing and an nvarchar string containing the name of the database you're running the code against. To execute the results of the preceding query in Pubs, you would run the following code:

EXEC master..xp_execresultset
  N'SELECT cmdtext FROM 
  tempdb..TSQLCommands WHERE 
  cmdid = 1 ORDER BY cmdrow',
  N'pubs'

You get the au_lname and au_fname columns from the authors table, as Figure 3 shows.

As an example of executing query-constructed code, suppose you want to write code that counts the number of rows in all the base tables in your database. First, run the following code to create the #NumRows temporary table:

CREATE TABLE #NumRows(table_name 
  SYSNAME, owner SYSNAME, 
  numrows int)

Then, run the code at callout A in Listing 6 to execute the query that generates the INSERT statements that populate the table. You get the results that Figure 4 shows.

Usually, you'd copy the generated INSERT statements to Query Analyzer's Execution pane and execute them. However, you can use xp_execresultset to run the query, then execute its results as callout B in Listing 6 shows.

Note that because xp_execresultset accepts the query string as a character-based argument, you need to prefix and suffix the string with single quotes and duplicate each quote within the string. To examine the results, query the #NumRows temporary table:

SELECT * FROM #NumRows

In this article, I started exploring T-SQL back doors—undocumented features that T-SQL programmers use—mainly those related to stored procedures. In my next column, I'll discuss back doors related to user-defined functions (UDFs). As you've probably noticed, the back doors that I explored have a lot of useful functionality, but you need to be aware of the risks I mentioned. I hope to see those back doors become supported front doors in a future release of SQL Server.

End of Article

Prev. page     1 2 3 4 [5]     next page -->



You must log on before posting a comment.

If you don't have a username & password, please register now.

Reader Comments

This article disappointed in the beginning but I found the last two pages pretty interesting and useful - thanks.

Cedric (since SQL Server 1.0A so not always in touch with the latest back door potential)

Cedric

Hi,

I found the article interesting, however, I cannot get the code samples to work.

In listing 4 the code

CREATE PROC sp_distinctcount @table_name AS SYSNAME, @col_name AS SYSNAME, @cnt AS OUTPUT

doesn't appear to define as datatype for @cnt and the syntax appears incomplete. I might be incoorect be I was expecting @cnt int OUTPUT.

Upon trying to execute

DECLARE @n AS int EXEC sp_distinctcount @table_name='authors', @col_name='au_lname', @cnt=@n OUTPUT PRINT 'Distinct count of last names of authors is: ' + CAST(@n AS varchar(10))

The error

Server: Msg 214, Level 16, State 2, Procedure sp_executesql, Line 9 Procedure expects parameter '@statement' of type 'ntext/nchar/nvarchar'

was returned.

Could you provide some assistance.

Regards

John Langley

john Langley

Itzik, Excellent article, not just saying it because I respect your knowledge and understanding of SQL Server, I found the information U had to share on special views and tables very useful. . . .I have to admit I stumbled across the executing a special procedure against any database some years ago, and I have found it very useful ever since

Olu Adedeji

Very intresting article. Thanks,Rimon.

Rimon

Script in listing 2 misses nvavchar in declare clause:

CREATE PROC sp_getcolumn @table_name AS SYSNAME, @col_name AS SYSNAME AS DECLARE @sql AS nvarchar (1000) -- here ....

MIkhail

Fix your script, guys Listing 4: Creation Script for the Sp_distinctcount Stored Procedure USE master GO

CREATE PROC sp_distinctcount @table_name AS SYSNAME, @col_name AS SYSNAME, @cnt AS INT OUTPUT -- here AS DECLARE @sql AS nvarchar (1000) SET @sql = N'SELECT @distinctcount = COUNT(DISTINCT [' + @col_name + N']) FROM [' + @table_name + N']'

EXEC sp_executesql @stmt = @sql, @params = N'@distinctcount int OUTPUT', -- here @distinctcount = @cnt OUTPUT GO

Mikhail

You wrote "T-SQL programmers can also use sp_executesql's undocumented output parameters.", but output parameters in sp_executesql are not undocumented. They are documented in KB262499: http://support.microsoft.com/?id=262499

Razvan Socol

Excelent article.

However, the arguments described for sp_execdirect are incorrect. The correct arguments for sp_execresultset are an nvarchar string containing the query and a bit field indicating if debug info needs to be show, instead of executing the resultset. The debug argument has a default of 0 (no debug info).

Karl Gram

When I run EXEC master..xp_execresultset with the accompaning query I get "The command(s) completed successfully." but no records. I have tried both with your example and my own. I am running everything from query analyzer.

(SQL2000)

Mark Murray

I'm trying to use the output parameters for sp_executesql you mentioned in your article, with no success. Here is the code:

ALTER PROCEDURE ap_GetRemoteServerVersion @ServerName varchar(50), @Results nvarchar(65) OUTPUT AS BEGIN DECLARE @SQL nvarchar(500)

SET @SQL = N'exec ' + @ServerName + '.master.dbo.xp_msver ProductVersion'

EXECUTE sp_executesql @Stmt = @SQL, @Params = N'@Results nvarchar(65) OUTPUT', @Results = @Results OUTPUT

END

This is the error message I receive when I run this code:

Procedure 'ap_GetRemoteServerVersion' expects parameter '@Results', which was not supplied.

My purpose in running this code is to capture "Character_Value" from the result set of xp_msver. This value is the SQL Server version information for a particular SQL Server. If you can tell me what I'm doing wrong, or know of another way to capture values from extended stored procedures, it would be greatly appreciated.

Result set for exec xp_msver 'ProductVersion':

Index Name Internal_Value Character_Value ----- -------------- -------------- --------------- 2 ProductVersion 458752 7.00.1063

Ken Powers

Hello Itzik,

Of course you are aware that there is a XP and SP version of ExecResultset. The SP version does not have the TargetDatabase as a parameter. But, your other backdoor trick of calling it from another database even though it lives in Master applies again. These different calls to generate record counts for all tables in pubs give the same result:

master..xp_ExecResultset N'select ''select count(*) as '' + quotename(name) + '' from '' + name from sysobjects where type = ''u'' order by name', N'pubs' go pubs..sp_ExecResultset N'select ''select count(*) as '' + quotename(name) + '' from '' + name from sysobjects where type = ''u'' order by name', 1 go

The code of the SP version can simply be retrieved with sp_helptext SP_ExecResultset (it's extremely ugly).

Thanks, Jacques Roumimper

Jacques Roumimper

the code sp_distinctcount contains the following errors: 1) @CNT AS INT OUTPUT 2) DECLARE @SQL AS NVARCHAR(1000)

michele

Hello Itzik, I used xp_execresultset to execute code. My intention was to execute code longer than 4000 characters. But the procedure truncates the code at 4000 characters. This means that it is not possible to execute code longer than 4000 characters with your method.

However, I did find a solution to execute code longer than 4000 characters. You have to write the code to a text file with BCP and run this text file with te osql command.

Clemens van der Veen.

cvdv

Article Rating 3 out of 5

 
 

ADS BY GOOGLE