SQL Server function to check dynamic SQL syntax

By:   |   Comments (9)   |   Related: > Functions User Defined UDF


Problem

Although most of what you read tells you to stay away from dynamic SQL, the reality is there are places where writing T-SQL code dynamically just makes sense.  The one downside to dynamic SQL is that if it is not written correctly the code will fail. So in this tip I am sharing a function I created to help determine if the dynamic code is OK prior to execution.

Solution

The solution involves creating a scalar user defined function that gets an input SQL string to check and then outputs an error message if the SQL string check returns an error or returns OK if the SQL string's check passed successfully.

The function queries the sys.dm_exec_describe_first_result_set dynamic management view. This dynamic view (or function) takes a Transact-SQL statement as a parameter and describes the metadata of the first result set for the statement.

If an error exists then the error message, number, severity, state, type and type description will return NOT NULL values. In this case the function will return the error message text as its returned values.  If there are no errors, then the function will return OK as its returned value.

Function to Check Dynamic SQL Code

Create the following function and then we will show example use below.

-- =============================================
-- Author:  Eli Leiba
-- Create date: 06-2017
-- Description: Check Dynamic SQL Statement Syntax
-- =============================================
CREATE FUNCTION dbo.CheckDynaSQL (@p1 VARCHAR (2000))
RETURNS VARCHAR (1000)
AS
BEGIN
   DECLARE @Result VARCHAR (1000)

   IF EXISTS (
         SELECT 1
         FROM sys.dm_exec_describe_first_result_set (@p1, NULL, 0)
         WHERE [error_message] IS NOT NULL
            AND [error_number] IS NOT NULL
            AND [error_severity] IS NOT NULL
            AND [error_state] IS NOT NULL
            AND [error_type] IS NOT NULL
            AND [error_type_desc] IS NOT NULL
         )
   BEGIN
      SELECT @Result = [error_message]
      FROM sys.dm_exec_describe_first_result_set(@p1, NULL, 0)
      WHERE column_ordinal = 0
   END
   ELSE
   BEGIN
      SET @Result = 'OK'
   END

   RETURN (@Result)
END
GO

Examples Using Function

All the examples use the Northwind database.  The examples cover various queries like SELECT, UPDATE, INSERT, DELETE, stored procedures and functions.

Note: For these tests it is assumed that in the Northwind database, the Orders table exists and the NewOrders table does not exist.

Checking SELECT statements

Select dbo.CheckDynaSQL ('SELECT *, FROM Orders')
  • Result: Incorrect syntax near the keyword 'FROM'.
Select dbo.CheckDynaSQL ('SELECT * FROM NewOrders')
  • Result: Invalid object name 'NewOrders'.
Select dbo.CheckDynaSQL ('SELECT * FROM Orders')
  • Result: OK

Checking UPDATE statements

Select dbo.CheckDynaSQL ('UPDATE NewOrders set Freight = 35 Where OrderID = 10248')
  • Result:  Invalid object name 'NewOrders'.
Select dbo.CheckDynaSQL ('UPDATE Orders set Freight = 35 Where OrderID = 10248')
  • Result:  OK

Checking DELETE statements

Select dbo.CheckDynaSQL ('DELETE From NewOrders Where OrderID = 10248')

Result:  Invalid object name 'NewOrders'.

Select dbo.CheckDynaSQL ('DELETE From Orders Where OrderID = 10248')

Result:  OK

Checking INSERT statements

Select dbo.CheckDynaSQL ('INSERT TO orders DEFAULT VALUES')
  • Result:  Incorrect syntax near the keyword 'TO'.
Select dbo.CheckDynaSQL ('INSERT INTO orders DEFAULT VALUES')
  • Result:  OK

Checking Stored Procedure execution statements

Select dbo.CheckDynaSQL ('exec dbo.SalesByCategory "Beverages", "1999" ')

Result:  OK

Select dbo.CheckDynaSQL ('exec dbo.SalesByCategory2 "Beverages", "1999" ')
  • Result:  Could not find stored procedure 'dbo.SalesByCategory2'.

Checking Function execution statements

Select dbo.CheckDynaSQL ('select DateAdd (xx, -3, OrderDate) from orders where orderid = 10248') 
  • Result:  'xx' is not a recognized datediff option.
Select dbo.CheckDynaSQL ('select DateAdd (mm, -3, OrderDate) from orders where orderid = 10248') 
  • Result:  OK
Next Steps
  • You can create this simple function in your application database and use it for preliminary checking of dynamic SQL queries.
  • The procedures were tested using Microsoft SQL Server 2014 Standard Edition


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Eli Leiba Eli Leiba is a senior application DBA, a teacher and a senior database consultant with 19 years of RDBMS experience.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips



Comments For This Article




Tuesday, October 29, 2019 - 5:05:04 AM - El Leiba Back To Top (82922)

The sys.dm_exec_describe_first_result_set is a dynamic management function that takes a Transact-SQL statement as a parameter and describes the metadata of the first result set for the statement. This requires the permission to execute the @tsql Transact-SQL statement argument. The function can return many error codes that this simple procedure does not cover. You can expand this procedure to fetch additional information according to the different error types.

The list of error codes is as follows:

1 MISC All errors that are not otherwise described.

2 SYNTAX A syntax error occurred in the batch.

3 CONFLICTING_RESULTS The result could not be determined because of a conflict between two possible first statements.

4 DYNAMIC_SQL The result could not be determined because of dynamic SQL that could potentially return the first result.

5 CLR_PROCEDURE The result could not be determined because a CLR stored procedure could potentially return the first result.

6 CLR_TRIGGER The result could not be determined because a CLR trigger could potentially return the first result.

7 EXTENDED_PROCEDURE The result could not be determined because an extended stored procedure could potentially return the first result.

8 UNDECLARED_PARAMETER The result could not be determined because the data type of one or more of the result set's columns potentially depends on an undeclared parameter.

9 RECURSION The result could not be determined because the batch contains a recursive statement.

10 TEMPORARY_TABLE The result could not be determined because the batch contains a temporary table and is not supported by sp_describe_first_result_set .

11 UNSUPPORTED_STATEMENT The result could not be determined because the batch contains a statement that is not supported by sp_describe_first_result_set (e.g., FETCH, REVERT etc.).

12 OBJECT_TYPE_NOT_SUPPORTED The @object_id passed to the function is not supported (i.e. not a stored procedure)

13 OBJECT_DOES_NOT_EXIST The @object_id passed to the function was not found in the system catalog.


Thursday, October 24, 2019 - 2:06:26 PM - Mark Burns Back To Top (82889)

This will also not work if certain system stored procedures, like sp_send_DBMail are used in the script.  For both this case and Temp table usage cases, the sys.dm_exec_describe_first_result_set* functions will simply fold up their tent and say "not supported", basically.

This limitation clearly affects the usability of these options for the general case of validating SP script syntaxes and/or "compilability" (for lack of a better term).


Wednesday, November 29, 2017 - 7:09:04 AM - Chris Louwaerts Back To Top (73406)

What a great idea!  Thank you for sharing. 

 


Thursday, August 17, 2017 - 1:40:29 PM - Gerald Britton Back To Top (64972)

 

Note that this will not work if temporary tables are used


Monday, August 7, 2017 - 11:33:53 AM - Robert L Davis Back To Top (64228)

 Very clever! Curious why you limited input to varchar(2000) when the dynamic management function accepts nvarchar(max) for that parameter.

 


Friday, August 4, 2017 - 4:29:01 PM - Adrian Hernandez Back To Top (63979)

 Excellent. Thanks for sharing your experience and knowledge. This is a lifesaver.

 


Friday, August 4, 2017 - 9:31:52 AM - Bob Feldsien Back To Top (63964)

 Upon further checking..  If you use parameterized dynamic sql, it will not detect problems with the parameters.  Notice that in the following example, the data type of the @CaseId parameter does not match the data type in the actual table.  The call to the DMV returns 0 rows.  Still a very good function to know and an excellent tip.

CREATE TABLE ORG_CASE(id int, Description varchar(255))

INSERT INTO ORG_CASE(id, Description)
VALUES(1, 'Case Number 1')
 , (1003, 'Case 1003')

DECLARE @SQL nvarchar(4000)
 , @ParamDef nvarchar(500)
 , @CaseId varchar(255) = 'Case 1003'
 , @TableName varchar(128) = 'ORG_CASE'

SET @SQL = 'SELECT * FROM ' + @TableName + ' WHERE id = @CaseId'
SET @ParamDef = '@CaseId int'

SELECT error_number
 , error_message
FROM sys.dm_exec_describe_first_result_set(@SQL, null, 0)
WHERE error_message IS NOT NULL
 AND column_ordinal = 0

DROP TABLE ORG_CASE

 

  In the following example, ORG_CASE is an existing table where "id" is an int field.  Notice that


Friday, August 4, 2017 - 8:55:04 AM - Bob Feldsien Back To Top (63962)

I work in an environment where there is always a LOT of dynamic sql.  For debugging and dev I usually get the resulting sql with a print statement and then execute it in another SSMS window.  Definitely going to try this.


Friday, August 4, 2017 - 4:13:02 AM - RS Back To Top (63943)

Good tip!

Thanks  

 















get free sql tips
agree to terms