SA0115 : Ensure variable assignment from SELECT with no rows
Introduction
Section titled “Introduction”A SELECT statement does not assign values to variables if no rows are returned, potentially leading to unexpected behavior and incorrect assumptions.
Description
Section titled “Description”This issue arises in T-SQL when developers expect a SELECT statement to always update variables, regardless of whether any rows are returned by the query. In SQL Server, if a query doesn’t find any matching rows, the variables remain unchanged rather than being reset or set to NULL . This can lead to logical errors and unexpected behavior in code execution.
For example:
-- Example of a query that might not update variables as expectedDECLARE @Variable INT;SET @Variable = 1;SELECT @Variable = ColumnName FROM TableName WHERE SomeCondition = 'value';-- If no rows meet the condition, @Variable remains 1In this example, if the SELECT statement returns no rows, @Variable will not be set to NULL or any new value. Instead, it retains its previous value, leading to potential inconsistencies in subsequent logic that relies on @Variable .
-
This behavior can cause data integrity issues if not properly safeguarded, as assumptions about variable values may be incorrect.
-
Unexpected results in business logic or data processing could occur if the condition for the
SELECTis frequently unmet.
How to fix
Section titled “How to fix”Properly handle cases where a SELECT statement might not assign new values to variables.
Follow these steps to address the issue:
1.Initialize variables to a default or null state before the SELECT statement to avoid retaining previous values.
2.Use a conditional check to set the variable only if the SELECT statement returns rows. This can be done using IF EXISTS or another control flow statement.
3.Consider using a SELECT INTO statement to handle cases where no rows are found, isolating the logic and ensuring predictable outcomes.
For example:
-- Example of corrected query DECLARE @Variable INT; SET @Variable = NULL; -- Initialize the variable IF EXISTS(SELECT 1 FROM TableName WHERE SomeCondition = 'value') BEGIN SELECT @Variable = ColumnName FROM TableName WHERE SomeCondition = 'value'; ENDThe rule has a Batch scope and is applied only on the SQL script.
Parameters
Section titled “Parameters”Rule has no parameters.
Remarks
Section titled “Remarks”The rule does not need Analysis Context or SQL Connection.
Effort To Fix
Section titled “Effort To Fix”13 minutes per issue.
Categories
Section titled “Categories”Design Rules, Bugs
Additional Information
Section titled “Additional Information”There is no additional info for this rule.
Example Test SQL
Section titled “Example Test SQL”CREATE PROCEDURE [dbo].[proc_SqlEnlight_Test_SA0115] @input int, @input2 int = 5, @output int outputAS
SELECT @output = UpdatedWho, @input2 = 6 FROM dbo._asBlockHist WHERE UpdatedWho = @input;SELECT @output
-- Case 1: Always referenced using ISNULLDECLARE @Supplier CHAR(5)SELECT @Supplier = Supplier FROM MDID_TRAN.dbo.TrOrderPOWHERE PoNum = '10101234'SELECT ISNULL(@Supplier, 'XXXXX')
-- Case 2: Not ensured assigned but not usedDECLARE @NotEnsuredAssignedSupplier1 CHAR(5)--SET @NotEnsuredAssignedSupplier1 = 'XXXXXX'SELECT @NotEnsuredAssignedSupplier1 = Supplier FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'--SELECT @NotEnsuredAssignedSupplier1
-- Case 3: Default value assignedDECLARE @Supplier2 CHAR(5)SELECT @Supplier2 = 'XXXXXX'SELECT @Supplier2 = Supplier FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'SELECT @Supplier2
-- Case 4: Not handled - should generate SA0115 rule violationDECLARE @NotEnsuredAssigned CHAR(5)-- SET @NotEnsuredAssigned = 'XXXXXX'SELECT @NotEnsuredAssigned = Supplier FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'SELECT @NotEnsuredAssigned
-- Case 5: Not handled but will be ignoredDECLARE @NotEnsuredAssigned1 CHAR(5)SELECT @NotEnsuredAssigned1 = Supplier /*IGNORE:SA0115*/ FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'SELECT @NotEnsuredAssigned1
-- Case 6:Default value assignedDECLARE @EnsuredAssigned2 CHAR(10) = 'DEFAULT'SELECT @EnsuredAssigned2 = Supplier FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'SELECT @EnsuredAssigned2
-- Case 7: ROWCOUNT checked and default value assignedDECLARE @EnsuredAssigned3 CHAR(10)SELECT @EnsuredAssigned3 = Supplier FROM MDID_TRAN.dbo.TrOrderPo WHERE PoNum = '10101234'
IF @@ROWCOUNT = 0BEGIN -- Variable is assigned after @@ROWCOUNT check SELECT @EnsuredAssigned3 = 'DEFAULT'ENDSELECT @EnsuredAssigned3
-- Case 8: Variable is checked for NULL and assignedDECLARE @Supplier4 CHAR(5);SELECT @Supplier4 = SupplierFROM MDID_TRAN.dbo.TrOrderPOWHERE PoNum = '10101234';
IF ((@Supplier4 IS NULL))BEGIN SET @Supplier4 = '';END;Analysis Results
Section titled “Analysis Results”| Message | Line | Column | |
|---|---|---|---|
| 1 | SA0115 : Variable @output assignment from SELECT with no rows not ensured. | 7 | 7 |
| 2 | SA0115 : Variable @NotEnsuredAssigned assignment from SELECT with no rows not ensured. | 31 | 7 |