APX1086 – Check syntax errors
Description:
This rule evaluates the T-SQL script for any syntax error, by parsing the T-SQL statement and show any errors.
It is recommended to check the documentation for each T-SQL statement in order to use it in the query properly.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 |
SELECT * from FirstTable; SELECT * frm SecondTable; SELECT * form ThirdTable; DELETE from FourthTable wher id > 0; SELECT * from FifthTable; SELECT q from SixthTable; |
Message:
Incorrect syntax near ‘*’
APX1095 – GOTO Usage
Description:
This rule evaluates the T-SQL script for using GOTO statements.
The excessive GOTO use considered as poor programming practice that may lead to unmaintainable and unreadable code when the code grows long.
User better alternative statements to control the execution path.
For more information visit
https://www.quora.com/Why-is-the-goto-statement-in-C-advised-to-avoid
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 3 GOTO FirstRoute; IF @cnt = 7 GOTO SecondRoute; END; FirstRoute: SELECT * FROM FirstTable; GOTO ThirdRoute; SecondRoute: SELECT * FROM SecondTable; ThirdRoute: SELECT * FROM ThirdTable; GO DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 4 BEGIN SELECT * FROM FirstTable; END; IF @cnt = 5 BEGIN SELECT * FROM ThirdTable; END; END; GO CREATE PROCEDURE proc_GoToFirstFailedEx AS BEGIN DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 4 GOTO FirstRoute; IF @cnt = 5 GOTO SecondRoute; END; FirstRoute: SELECT * FROM FirstTable; GOTO ThirdRoute; label2_1: SELECT * FROM SecondTable; label3_1: SELECT * FROM ThirdTable; END; GO CREATE PROCEDURE proc_GoToSecondFailedEx AS BEGIN DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 4 BEGIN SELECT * FROM FirstTable; SELECT * FROM ThirdTable; END; IF @cnt = 5 BEGIN SELECT * FROM SecondTable; SELECT * FROM ThirdTable; END; END; END; |
Message:
GOTO statement detected
APX1112 – Semi-colons statement terminator
Description:
This rule evaluates the T-SQL script for missing semi-colons at the end of each statement.
Although technically statement not terminated with semi-colons, terminating statements with semi-colons improves code format, layout, and readability and as such is considered a best practice.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
SELECT * FROM FirstTable SELECT * FROM SecondTable; SELECT * FROM ThirdTable; SELECT * FROM FourthTable SELECT * FROM FifthTable SELECT * FROM SixthTable; SELECT * FROM SeventhTable; DELETE FROM EightthTable; UPDATE NinethTable SET val = 0; |
Message:
The statement ‘SELECT * FROM FirstTable’ is not terminated with semi-colon
APX1113 – Uninitialized variable
Description:
This rule evaluates the T-SQL script for using the variables before they are initialized.
Using uninitialized variables will result with incorrect output.
You should initialize all variables before the first use, as T-SQL guarantees only the newly declared variables will be initialized to NULL.
For more information visit
Example script:
1 2 3 4 5 6 7 8 |
DECLARE @Var1 AS INT, @Var2 AS INT, @Var3 AS INT; SET @Var1 = 1000; IF(@Var2 > 10 AND @Var2 < 100 AND @Var3 > 50) BEGIN SET @Var1 = 2000; END; |
Message:
The variable @Var2 is used but not initialized
APX1114 – Unused parameter
Description:
This rule evaluates the T-SQL script for having unused parameters.
Unused parameters are considered “dead code” that contribute to maintenance and readability problems.
It is considered a best practice to keep code clean of any unused elements.
For more information visit
https://solutioncenter.apexsql.com/how-to-clean-up-sql-database-code-by-removing-unused-parameters/
Example script:
1 2 3 4 5 6 7 8 9 10 11 |
CREATE PROCEDURE proc_UnusedParam @City NVARCHAR(30) = NULL, @FirstAddressLine NVARCHAR(60) = NULL, @SecondAddressLine NVARCHAR(60) = NULL, @ThirdAddressLine NVARCHAR(60) = NULL, @FourthAddressLine NVARCHAR(60) = NULL AS SELECT * FROM Persons WHERE City = @City AND AddressLine1 LIKE '%' + @FirstAddressLine + '%' AND StrField = @ThirdAddressLine; |
Message:
Parameter ‘@FourthAddressLine NVARCHAR(60) = NULL’ is not used
APX1123 – SET NOCOUNT OFF Usage
Description:
This rule evaluates the T-SQL script for using SET NOCOUNT OFF in functions, stored procedures and triggers unless row affected output is needed.
Setting the NOCOUNT option to ON helps significantly in enhancing the performance of stored procedures that contain loops and many statements that don’t return too much actual data, by reducing the network traffic caused by writing such type of messages.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
CREATE PROCEDURE proc_NOCOUNTFirstFailedEx AS BEGIN DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 4 GOTO FirstRoute; IF @cnt = 5 GOTO SecondRoute; END; FirstRoute: SELECT * FROM FirstTable; GOTO ThirdRoute; SecondRoute: SELECT * FROM SecondTable; ThirdRoute: SELECT * FROM ThirdTable; END; GO CREATE PROCEDURE proc_NOCOUNTSecondFailedEx AS BEGIN DECLARE @cnt INT; SET @cnt = 1; WHILE @cnt < 10 BEGIN SELECT @cnt; SET @cnt = @cnt + 1; IF @cnt = 4 GOTO FirstRoute; IF @cnt = 5 GOTO SecondRoute; END; FirstRoute: SELECT * FROM FirstTable; GOTO ThirdRoute; SecondRoute: SELECT * FROM FirstTable; ThirdRoute: SELECT * FROM FirstTable; END; GO CREATE PROC proc_NOCOUNTFirstGoodEx AS SET NOCOUNT ON; SELECT * FROM dbo.FirstTable; GO |
Message:
SET NOCOUNT is off
APX1131 – Un-closed cursor
Description:
This rule evaluates the T-SQL script for leaving the local cursors opened.
Any opened local cursors should be explicitly closed.
When you close an opened cursor, the current result set will be released, and any cursor locks held on the rows on which the cursor is positioned will be freed. On the other hand, CLOSE will leave the data structures available for reopening again, without being able to fetch it until the cursor is reopened.
For more information visit
https://docs.microsoft.com/en-us/sql/t-sql/functions/cursor-status-transact-sql?view=sql-server-2017
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
CREATE PROCEDURE proc_UnclosedCursorFirstFailedEx AS BEGIN DECLARE MyFirstFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFirstFailedCursor; FETCH NEXT FROM MyFirstFailedCursor; END; GO CREATE PROCEDURE proc_UnclosedCursorSecondFailedEx AS BEGIN DECLARE MySecondFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; GO CREATE PROCEDURE proc_UnclosedCursorFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO CREATE PROCEDURE proc_UnclosedCursorSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
Local cursor ‘MyFirstFailedCursor’ not closed
APX1132 – Cursor not deallocated
Description
This rule evaluates the T-SQL script for NOT deallocating the local cursor explicitly.
When the cursor reference is deallocated, all cursor references are removed, and the data structures comprising the cursor are released. When a cursor name is associated with a cursor, the name cannot be used for another cursor of the same scope (global or local) until this cursor has been deallocated.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
CREATE PROCEDURE proc_NotDeallocatedCursorFirstFailedEx AS BEGIN DECLARE MyFirstFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFirstFailedCursor; FETCH NEXT FROM MyFirstFailedCursor; END; GO CREATE PROCEDURE proc_NotDeallocatedCursorSecondFailedEx AS BEGIN DECLARE MySecondFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; GO CREATE PROCEDURE proc_NotDeallocatedCursorThirdFailedEx AS BEGIN DECLARE MyThirdFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyThirdFailedCursor; FETCH NEXT FROM MyThirdFailedCursor; CLOSE MyThirdFailedCursor; END; GO CREATE PROCEDURE proc_NotDeallocatedCursorFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO CREATE PROCEDURE proc_NotDeallocatedCursorSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
The cursor, ‘MyThirdFailedCursor’ is not explicitly deallocated
APX1133 – RAISERROR with severity above 18 missing WITH LOG clause
Description:
This rule evaluates the T-SQL script for writing a RAISERROR statements with a severity above 18 that lack a WITH LOG clause.
For severity levels from 19 through 25, the WITH LOG option is required.
The WITH LOG option is used to Log the error in the error log and the application log for the instance of the Microsoft SQL Server Database Engine.
For more information visit
https://docs.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
CREATE PROCEDURE proc_RAISERRORMissWithLogFirstFailedEx AS BEGIN RAISERROR(N'Error.', 20, 1); END; GO CREATE PROCEDURE proc_RAISERRORMissWithLogSecondFailedEx AS BEGIN RAISERROR(N'Error.', 21, 1); END; GO CREATE PROCEDURE proc_RAISERRORMissWithLogThirdFailedEx AS BEGIN RAISERROR(N'Error message %s %d.', 25, 1, N'number', 5); END; GO CREATE PROCEDURE proc_RAISERRORMissWithLogFirstGoodEx AS BEGIN RAISERROR(N'Error.', 10, 1); END; GO CREATE PROCEDURE proc_RAISERRORMissWithLogSecondGoodEx AS BEGIN RAISERROR(N'Error.', 30, 1) WITH LOG; END; GO |
Message:
RAISEERROR statement with severity above > 18 and doesn’t contain a WITH LOG clause
APX1136 – Direct use of Date-Time functions
Description:
This rule evaluates the T-SQL script for having micro-differences during the running of code.
This is due to the fact that the Date-time functions are considered as non-deterministic functions.
This can be avoided by obtaining date-time information as a variable at the beginning of a procedure.
For more information visit
https://www.sqlshack.com/sql-convert-date-functions-and-formats/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
CREATE PROCEDURE proc_DirectDateTimeFunFirstFailedEx AS BEGIN SELECT SYSDATETIME(), CURRENT_TIMESTAMP; END; GO CREATE PROCEDURE proc_DirectDateTimeFunSecondFailedEx AS BEGIN SELECT SYSDATETIME(), SYSDATETIMEOFFSET(), SYSUTCDATETIME(), CURRENT_TIMESTAMP, GETDATE(), GETUTCDATE(); SELECT CONVERT(TIME, SYSDATETIME()), CONVERT(TIME, CURRENT_TIMESTAMP), CONVERT(TIME, GETDATE()); END; GO CREATE PROCEDURE proc_DirectDateTimeFunThirdFailedEx AS BEGIN SELECT SYSDATETIME(), CURRENT_TIMESTAMP, GETDATE(); END; GO CREATE PROCEDURE procDirectDateTimeFunFirstGoodEx AS BEGIN SELECT DATEPART(MONTH, GETUTCDATE()); END; GO |
Message:
Direct use of Date-Time functions detected vs storing in a variable
APX1139 – WAITFOR delay statement
Description:
This rule evaluates the T-SQL script for using WAITFOR statement with DELAY or TIME inside a programmability object.
Generally, such statements should be avoided except for very specific cases.
The WAITFOR command does consume a small number of CPU cycles so it is recommended not to use it excessively.
For more information visit
https://www.sqlshack.com/sql-server-wait-type-waitfor/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
CREATE PROCEDURE proc_WAITFORFirstFailedEx AS BEGIN WAITFOR TIME '10:00'; DELETE FROM FirstTable; END; GO CREATE PROCEDURE proc_WAITFORSecondFailedEx AS BEGIN WAITFOR DELAY '01:00'; DELETE FROM FirstTable; WAITFOR DELAY '02:00'; DELETE FROM SecondTable; END; GO CREATE PROCEDURE proc_WAITFORThirdFailedEx AS BEGIN DECLARE @dellay CHAR(8)= '00:10:00'; WAITFOR DELAY @dellay; DELETE FROM FirstTable; END; GO CREATE PROC proc_WAITFORFirstGoodEx AS SET NOCOUNT ON; SELECT * FROM dbo.FirstTable; SET NOCOUNT OFF; GO |
Message:
WAITFOR statement detected
APX1154 – Unpaired sp_xml_preparedocument
Description:
This rule evaluates the T-SQL script for calling the sp_xml_preparedocument procedure without being paired with and followed by an sp_xml_removedocument call.
The MSXML parser uses one-eighth the total memory available for SQL Server.
To avoid running out of memory, run sp_xml_removedocument to free up the memory.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
CREATE PROCEDURE proc_XMLPairedFirstFailedEx AS BEGIN DECLARE @handle1 INT; EXEC sp_xml_preparedocument @handle1 OUTPUT; END; GO CREATE PROCEDURE proc_XMLPairedSecondFailedEx AS BEGIN DECLARE @handle2 INT; DECLARE @xmlval VARCHAR(256); SET @xmlval = '<ROOT><resultset><title>ApexSQL Enforce Report</title></resultset></ROOT>'; EXEC sp_xml_preparedocument @handle2 OUTPUT, @xmlval; END; GO CREATE PROCEDURE proc_XMLPairedThirdFailedEx AS BEGIN DECLARE @handle3 INT; DECLARE @xmlval VARCHAR(256); SET @xmlval = '<ROOT><resultset><title>ApexSQL Enforce Report</title></resultset></ROOT>'; EXEC sp_xml_preparedocument @handle3 OUTPUT, @xmlval, '<ROOT xmlns:abc="urn:AxEnforce">'; END; GO CREATE PROCEDURE proc_XMLPairedFirstGoodEx AS BEGIN DECLARE @handle4 INT; EXEC sp_xml_preparedocument @handle4 OUTPUT; EXEC sp_xml_removedocument @handle4; END; GO CREATE PROCEDURE proc_XMLPairedSecondGoodEx AS BEGIN DECLARE @handle5 INT; DECLARE @xmlval VARCHAR(256); SET @xmlval = '<ROOT><resultset><title>ApexSQL Enforce Report</title></resultset></ROOT>'; EXEC sp_xml_preparedocument @handle5 OUTPUT, @xmlval; EXEC sp_xml_removedocument @handle5; END; GO |
Message:
The XML document, @handle’*’, was opened but not closed
APX1162 – Cyclomatic complexity
Description:
This rule evaluates the T-SQL script for having complex or too large codes, by measuring cyclomatic complexity.
The code complexity is measured by the number of control flow statements e.g. IF
Complex code is unreadable and hard to be maintained.
For more information visit
https://www.geeksforgeeks.org/cyclomatic-complexity/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
CREATE PROCEDURE proc_CyclomaticComplexFirstFailedEx AS BEGIN UPDATE FirstTable set col1=12 where col2 = 10; IF @@ROWCOUNT = 0 BEGIN UPDATE FirstTable set col1=25 where col2 = 100; IF @@ROWCOUNT = 0 BEGIN UPDATE FirstTable set col1=50 where col2 = 1000; IF @@ROWCOUNT = 0 BEGIN UPDATE FirstTable set col1=100 where col2 = 10000; END END END select * from FirstTable; END; GO CREATE PROCEDURE proc_CyclomaticComplexSecondFailedEx AS BEGIN DECLARE @handle INT; DECLARE @xmlval VARCHAR(256); SET @xmlval = '<ROOT><resultset><title>ApexSQL Enforce Report</title></resultset></ROOT>'; EXEC sp_xml_preparedocument @handle OUTPUT, @xmlval; EXEC sp_xml_removedocument @handle; END; GO CREATE PROCEDURE proc_CyclomaticComplexFirstGoodEx AS BEGIN SELECT * FROM SecondTable; END; GO CREATE PROCEDURE proc_CyclomaticComplexSecondGoodEx AS BEGIN SELECT * FROM ThirdTable; END; GO |
Message:
Statement complexity of 25, exceeds the allowed threshold
APX1191 – Reserved word used as identifier
Description:
This rule evaluates the T-SQL script for using SQL Server reserved words as identifiers.
Although it is syntactically possible to use SQL Server reserved keywords as identifiers and object names in Transact-SQL scripts, you can do this only by using delimited identifiers.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CREATE TYPE dbo.[Alter] FROM varchar(11) NOT NULL; CREATE TYPE [Order] FROM varchar(11) NOT NULL; EXEC sp_addtype N'Alter', N'char(10)',N'not null'; EXEC sp_addtype N'Table', N'char(10)',N'not null'; EXEC sys.sp_addtype [Alter], N'char(10)',N'not null'; EXEC sp_addtype N'AlterType', N'char(10)',N'not null'; EXEC sp_addtype N'TableType', N'char(10)',N'not null'; |
Message:
The reserved word, ‘<word>’, is used as an identifier
APX1206 – Invalid cursor operation
Description:
This rule checks the violation for any cursor operations like OPEN, FETCH, CLOSE, DEALLOCATE without a declared cursor.
Declaring a cursor is a pre-requisite for any of these operations.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
CREATE PROCEDURE proc_InvalidCursorOpFirstFailedEx AS BEGIN OPEN MyFirstFailedCursor; END; GO CREATE PROCEDURE proc_InvalidCursorOpSecondFailedEx AS BEGIN OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; GO CREATE PROCEDURE proc_InvalidCursorOpThirdFailedEx AS BEGIN OPEN MyThirdFailedCursor; CLOSE MyThirdFailedCursor; END; GO CREATE PROCEDURE proc_InvalidCursorOpFourthFailedEx AS BEGIN DEALLOCATE MyFourthFailedCursor; END; GO CREATE PROCEDURE proc_InvalidCursorOpFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR LOCAL FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO CREATE PROCEDURE proc_InvalidCursorOpSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR GLOBAL READ_ONLY FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
OPEN, CLOSE, FETCH or DELLOCATION clause without a declared cursor
APX1207 – Missing SET NOCOUNT ON before Unatteended DML execution
Description:
This rule evaluates the T-SQL script for missing the SET NOCOUNT ON option before executing the DML statement.
Setting NOCOUNT ON prevents viewing messages about the number of rows affected, which is unnecessary during unattended execution.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
CREATE PROC proc_MissNOCOUNTDMLFirstFailedEx AS SELECT * FROM FirstTable; GO CREATE PROC proc_MissNOCOUNTDMLSecondFailedEx AS BEGIN SELECT * FROM FirstTable; SET NOCOUNT ON; SELECT * FROM SecondTable; END; GO CREATE PROC proc_MissNOCOUNTDMLThirdFailedEx AS BEGIN SELECT * FROM FirstTable; SET NOCOUNT ON; SELECT * FROM SecondTable; SET NOCOUNT ON; SELECT * FROM ThirdTables; END; GO CREATE PROC proc_MissNOCOUNTDMLFirstGoodEx AS BEGIN SET NOCOUNT ON; SELECT * FROM FirstTable; SET NOCOUNT OFF; END; GO |
Message:
No SET NOCOUNT ON
APX1209 – INSERT EXEC statement
Description:
This rule evaluates the T-SQL script for using INSERT EXEC that may result in the error “An INSERT EXEC statement cannot be nested”.
Avoid using nested INSERT EXEC statement where possible.
For more information visit
http://www.sommarskog.se/share_data.html#INSERTEXEC
Example script:
1 2 3 4 5 6 7 |
INSERT INTO FirstTable (col1, col2) VALUES (1, 2); INSERT INTO SecondTable (col3, col4) VALUES (5, 7); INSERT INTO ThirdTable EXECUTE proc_InsertData; INSERT INTO FourthTable EXECUTE proc_InsertData; |
Message:
INSERT EXEC statement detected
APX1212 – Printing from a trigger
Description:
This rule evaluates the T-SQL script for printing results inside triggers.
Generally, printing inside triggers has no purpose and should be avoided as the triggers aren’t called explicitly, along with the unnecessary network traffic.
For more information visit
https://www.sqlshack.com/triggers-in-sql-server/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
CREATE TABLE Logins (LoginName NVARCHAR(100) NOT NULL, Pass NVARCHAR(50) NOT NULL ); GO CREATE TRIGGER triggerWithPrintFirstFailedEx ON Logins FOR INSERT AS SELECT * FROM Logins; PRINT 'test1'; GO CREATE TRIGGER triggerWithPrintSecondFailedEx ON Logins FOR UPDATE AS SELECT * FROM Logins; PRINT 'test2'; GO CREATE TRIGGER triggerWithPrintThirdFailEx ON Logins FOR DELETE AS SELECT * FROM Logins; PRINT 'test3'; PRINT 'test4'; GO CREATE TRIGGER triggerWithPrintFirstGoodEx ON Logins FOR INSERT, UPDATE, DELETE AS EXEC PassLog; GO |
Message:
PRINT statement detected inside this trigger
APX1220 – No TRY…CATCH
Description:
This rule evaluates the T-SQL script for NOT encapsulating all statements between the BEGIN and COMMIT/ROLLBACK statements in a TRY…CATCH block.
It is recommended to encapsulate your transaction in a TRY…CATCH block to end up with a safe-fail in the case of unexpected errors.
For more information visit
https://www.sqlshack.com/how-to-implement-error-handling-in-sql-server/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
CREATE PROCEDURE proc_TransTryCatchFirstFailedEx AS BEGIN BEGIN TRANSACTION; DELETE FROM FirstTable WHERE col1 > 0; COMMIT; END; GO CREATE PROCEDURE proc_TransTryCatchSecondFailedEx AS BEGIN BEGIN TRANSACTION; INSERT INTO SecondTable VALUES(1,2); INSERT INTO SecondTable VALUES(3,4); COMMIT; END; GO CREATE PROCEDURE proc_TransTryCatchThirdFailedEx AS BEGIN BEGIN TRANSACTION DemoTrans; INSERT INTO SecondTable VALUES(1,2); INSERT INTO SecondTable VALUES(3,4); COMMIT TRANSACTION DemonTrans; END; GO CREATE PROCEDURE proc_TransTryCatchFirstGoodEx AS BEGIN BEGIN TRY BEGIN TRANSACTION GoodTrans; INSERT INTO SecondTable VALUES(1,2); INSERT INTO SecondTable VALUES(3,4); COMMIT TRANSACTION GoodTrans; END TRY BEGIN CATCH PRINT 'Error'; END CATCH; END; GO CREATE PROCEDURE proc_TransTryCatchSecondGoodEx AS BEGIN BEGIN TRY BEGIN TRANSACTION SecondGoodTrans; DELETE FROM FirstTable WHERE col1 > 0; COMMIT; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrNum, ERROR_MESSAGE() AS ErrMsg; END CATCH; END; GO |
Message:
BEGIN or COMMIT/ROLLBACK statement is not encapsulated in a TRY CATCH statement
APX1233 – Undocumented stored procedure usage
Description:
This rule evaluates the T-SQL script for using undocumented procedures.
It is recommended to replace the undocumented stored procedures with the documented ones.
Undocumented SQL Server Stored procedures are more likely to change and/or be deprecated.
For more information visit
https://social.technet.microsoft.com/wiki/contents/articles/16975.aspx
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
CREATE PROCEDURE proc_UndocumentSPFirstFailedEx AS BEGIN EXEC sp_blockcnt; END; GO CREATE PROCEDURE proc_UndocumentSPSecondFailedEx AS BEGIN EXEC sp_checknames; END; GO CREATE PROCEDURE proc_UndocumentSPThirdFailedEx AS BEGIN EXEC sp_column_rowset; END; GO CREATE PROCEDURE proc_UndocumentSPFirstGoodEx AS BEGIN DECLARE @handle INT; EXEC sp_xml_preparedocument @handle OUTPUT; END; GO |
Message:
Use of an undocumented procedure, EXEC sp_blockcnt;, detected
APX1235 – Non-recommended settings
Description:
This rule evaluates the T-SQL script for changing the value of certain options e.g. ARITHABORT.
These changes are not recommended as it will cause the statement to be recompiled on every execution.
Recommended options are:
- ARITHABORT ON
- ANSI_NULL_DFLT_ON ON
- ANSI_PADDING ON
- ANSI_WARNINGS ON
- CONCAT_NULL_YIELDS_NULL ON
- NUMERIC_ROUNDABOUT OFF
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
CREATE PROCEDURE proc_NonRecommendFirstFailedEx WITH RECOMPILE AS BEGIN SET ANSI_NULLS OFF; EXEC sp_blockcnt; END; GO CREATE PROCEDURE proc_NonRecommendSecondFailedEx WITH RECOMPILE AS BEGIN SET ARITHABORT OFF; SET ANSI_WARNINGS OFF; SET CONCAT_NULL_YIELDS_NULL OFF; EXEC sp_blockcnt; END; GO CREATE PROCEDURE proc_NonRecommendThirdFailedEx WITH RECOMPILE AS BEGIN SET CONCAT_NULL_YIELDS_NULL OFF; EXEC sp_blockcnt; END; GO CREATE PROCEDURE proc_NonRecommendFirstGoodEx AS BEGIN DECLARE @handle INT; EXEC sp_xml_preparedocument @handle OUTPUT; END; GO |
Message:
A recommended setting. SET ANSI_NULLS ON;, is changed in the statement
APX1241 – Cannot roll back THROW
Description:
This rule evaluates the T-SQL script for writing a THROW statement without a preceding statement terminator.
This may result in the error “Cannot roll back THROW. No transaction or savepoint of that name was found.”
A statement terminator should be used with the ROLLBACK statement to avoid that error.
For more information visit
https://www.sqlshack.com/how-to-implement-error-handling-in-sql-server/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
CREATE PROCEDURE proc_THROWRollbackFirstFailedEx AS BEGIN BEGIN TRANSACTION; BEGIN TRY INSERT INTO testUniq(ID) VALUES(5); INSERT INTO testUniq(ID) VALUES(5); END TRY BEGIN CATCH ROLLBACK THROW; END CATCH; COMMIT; END; GO CREATE PROCEDURE procTHROWRollbackFirstGoodEx AS BEGIN BEGIN TRANSACTION; BEGIN TRY INSERT INTO testUniq(ID) VALUES(5); INSERT INTO testUniq(ID) VALUES(5); END TRY BEGIN CATCH ROLLBACK; THROW; END CATCH; COMMIT; END; GO |
Message:
THROW statement will result in the error “Cannot roll back THROW”
APX1281 – Permission is granted in procedure body
Description:
This rule evaluates the T-SQL script for missing GO from the GRANT statement.
If GO is missing, the GRANT statement will be included as part of the procedure itself, essentially granting itself permission.
Use GO to close the procedure body and before executing the GRANT statement.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
CREATE PROCEDURE proc_GrantNoGOFirstFailedEx AS BEGIN DECLARE MyFirstFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFirstFailedCursor; FETCH NEXT FROM MyFirstFailedCursor; END; GRANT EXECUTE ON proc_GrantNoGOFirstFailedEx TO TestRole WITH GRANT OPTION; GO CREATE PROCEDURE proc_GrantNoGOSecondFailedEx AS BEGIN DECLARE MySecondFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; GRANT EXECUTE ON proc_GrantNoGOSecondFailedEx TO SecondTestRole WITH GRANT OPTION; GO CREATE PROCEDURE proc_GrantNoGOThirdFailedEx AS BEGIN DECLARE MyThirdFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyThirdFailedCursor; FETCH NEXT FROM MyThirdFailedCursor; CLOSE MyThirdFailedCursor; END; GRANT EXECUTE ON proc_GrantNoGOThirdFailedEx TO ThirdTestRole WITH GRANT OPTION; GO CREATE PROCEDURE proc_GrantNoGOFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR LOCAL FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO GRANT EXECUTE ON proc_GrantNoGOFirstGoodExTO TO FourthTestRole WITH GRANT OPTION; GO |
Message:
Permission is granted in procedure body
APX1284 – Duplicate Cursor name
Description:
This rule evaluates the T-SQL script for declaring more than one cursor with the same name.
You cannot create more than one cursor with the same name before deallocating the first cursor.
Consider deallocating the cursor before using the same name again, or simply use a unique name for each cursor.
For more information visit
https://www.sqlshack.com/sql-server-cursor-tutorial/
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
CREATE PROCEDURE proc_DuplicateCursorsFirstFailedEx AS BEGIN DECLARE MyFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFailedCursor; FETCH NEXT FROM MyFailedCursor; END; GO CREATE PROCEDURE proc_DuplicateCursorsSecondFailedEx AS BEGIN DECLARE MyFailedCursor CURSOR FOR SELECT * FROM SecondTable; OPEN MyFailedCursor; FETCH NEXT FROM MyFailedCursor; END; GO CREATE PROCEDURE proc_DuplicateCursorsThirdFailedEx AS BEGIN DECLARE MyFailedCursor CURSOR FOR SELECT * FROM ThirdTable; OPEN MyFailedCursor; FETCH NEXT FROM MyFailedCursor; CLOSE MyFailedCursor; END; GO CREATE PROCEDURE proc_DuplicateCursorsFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR LOCAL FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO CREATE PROCEDURE proc_DuplicateCursorsSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR GLOBAL READ_ONLY FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
Cursor name, MyFailedCursor, is already in use
APX1285 – Cursor with no specified scope
Description:
This rule evaluates the T-SQL script for NOT declaring explicitly the cursor scope as LOCAL or GLOBAL.
It is better to define the scope of the cursor as LOCAL or GLOBAL explicitly.
If the cursor scope is not specified, the default value will be controlled by the setting of the default to the local cursor database option.
For more information visit
https://docs.microsoft.com/en-us/sql/t-sql/language-elements/declare-cursor-transact-sql
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
CREATE PROCEDURE proc_CursorNoScopeFirstFailedEx AS BEGIN DECLARE MyFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFailedCursor; FETCH NEXT FROM MyFailedCursor; END; GO CREATE PROCEDURE proc_CursorNoScopeSecondailedEx AS BEGIN DECLARE MySecondFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; GO CREATE PROCEDURE proc_CursorNoScopeThirdFailedEx AS BEGIN DECLARE MyThirdFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyThirdFailedCursor; FETCH NEXT FROM MyThirdFailedCursor; CLOSE MyThirdFailedCursor; END; GO CREATE PROCEDURE proc_CursorNoScopeFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR LOCAL FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO CREATE PROCEDURE proc_CursorNoScopeSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR GLOBAL READ_ONLY FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
The cursor scope, MySecondFailedCursor, is not specified
APX1287 – Hardcoded database name
Description:
This rule evaluates the T-SQL script for using hardcoded database names.
Such code is considered “brittle” code, that would break if the database name is changed.
Don’t use the database name unless you absolutely have to.
You can use SQL Server Synonyms to limit the naming dependency.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
USE FirstDemoDB; CREATE PROCEDURE proc_HardcodedDBNameFirstFailedEx AS BEGIN EXEC FirstDemoDB.Proc_FirstSP @val1 = N'ABC'; END; GO USE SecondDemoDB; GO CREATE PROCEDURE proc_HardcodedDBNameSecondFailedEx AS BEGIN EXEC SecondDemoDB.proc_SecondSPs @val2 = N'ABC'; END; GO USE ThirdDemoDB; GO CREATE PROCEDURE proc_HardcodedDBNameThirdFailedEx AS BEGIN EXEC ThirdDemoDB.proc_ThirdSP @val3 = N'ABC'; END; GO USE EnforceDemoDB; CREATE PROCEDURE proc_HardcodedDBNameFirstGoodEx AS BEGIN EXEC proc_FourthSP @val4 = N'ABC'; END; GO GO USE FourthDemoDB; CREATE PROCEDURE proc_HardcodedDBNameSecondGoodEx AS BEGIN EXEC proc_FifthSP @val5 = N'ABC'; END; GO |
Message:
A hardcoded, database name, <database_name>, is used
APX1288 – Statement outside of begin..end block
Description:
This rule evaluates the T-SQL script for mistakenly writing a statement outside the begin..end clause, such as including GO after a statement inside the begin..end clause.
The GO statement should be the last statement in any CREATE or ALTER statement.
For more information visit
Example script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
CREATE PROCEDURE proc_BeginEndBlockFirstFailedEx AS BEGIN DECLARE MyFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MyFailedCursor; FETCH NEXT FROM MyFailedCursor; END; PRINT 'test1'; PRINT 'bad line after proc'; GO CREATE PROCEDURE proc_BeginEndBlockSecondFailedEx AS BEGIN DECLARE MySecondFailedCursor CURSOR FOR SELECT * FROM FirstTable; OPEN MySecondFailedCursor; FETCH NEXT FROM MySecondFailedCursor; END; PRINT 'test2'; GO CREATE PROCEDURE proc_BeginEndBlockFirstGoodEx AS BEGIN DECLARE MyFirstGoodCursor CURSOR LOCAL FOR SELECT * FROM FirstTable; OPEN MyFirstGoodCursor; FETCH NEXT FROM MyFirstGoodCursor; CLOSE MyFirstGoodCursor; DEALLOCATE MyFirstGoodCursor; END; GO GO CREATE PROCEDURE proc_BeginEndBlockSecondGoodEx AS BEGIN DECLARE MySecondGoodCursor CURSOR GLOBAL READ_ONLY FOR SELECT * FROM SecondTable; OPEN MySecondGoodCursor; FETCH NEXT FROM MySecondGoodCursor; CLOSE MySecondGoodCursor; DEALLOCATE MySecondGoodCursor; END; GO |
Message:
A statement exists outside the begin..end block in procedure, pro_BeginEndBlockFirstFailedEx
APX1308 – Trigger should be enabled
Description:
This rule evaluates the T-SQL script for having disabled triggers.
It is recommended to keep the trigger enabled if its functionality is required.
If the trigger is disabled, some changes will not be recorded.
For more information visit
https://www.sqlshack.com/triggers-in-sql-server/
Example script:
1 2 3 4 5 |
ALTER TABLE FirstTable DISABLE TRIGGER MySecondTrigger; ALTER TABLE SecondTable DISABLE TRIGGER ALL; ALTER TABLE ThirdTable DISABLE TRIGGER ALL; ALTER TABLE FourthTable ALTER COLUMN Col2 VARCHAR(50); ALTER TABLE FifthTable ALTER COLUMN Col2 VARCHAR(50); |
Message:
Trigger(s) is disabled, consider enabling it
- Best author award in 2021 - January 3, 2022
- Best author award in 2020 - January 5, 2021
- Best author award in 2019 - January 3, 2020