MBauer
MBauer

Reputation: 33

T-SQL / Unexpected NULL handling when ANSI_NULLS is turned OFF

I'm just struggling with the NULL value handling in SQL Server (tested on Version 12.0.5000.0). Basically, my intention is to get all rows that have a column value <> a static value (e.g. 999). I'm NOT searching for an alternative LIKE "use the ISNULL function". The query is generated by a third-party engine and I do not intend write a parser and change the statement.

-- All examples with ANSI_NULLS OFF
SET ANSI_NULLS OFF;
GO

--------------------------------------------------------------------------------------------------------
-- "Virtual" example / working as expected
--------------------------------------------------------------------------------------------------------
    DECLARE 
        @One INT = 1,
        @Null INT = NULL

    SELECT
         IIF(@Null = NULL, 1, 0) '@Null = NULL' -- To test if ANSI_NULL is OFF
        ,IIF(@One <> NULL, 1, 0) '@One <> NULL' -- working with NULL variable
        ,IIF(1 <> NULL, 1, 0) '1 <> NULL'       -- working with NULL value

--------------------------------------------------------------------------------------------------------
-- MSDN Example / NOT working as expected
    -- https://learn.microsoft.com/en-us/sql/t-sql/statements/set-ansi-nulls-transact-sql
--------------------------------------------------------------------------------------------------------

    -- Create table t1 and insert values.  
    CREATE TABLE dbo.t1 (a INT NULL);  
    INSERT INTO dbo.t1 values (NULL),(0),(1);  
    GO  

    -- SET ANSI_NULLS to OFF and test.  
    DECLARE @varname int;  
    SET @varname = 999;

    SELECT a   
    FROM t1   
    WHERE a <> @varname;    -- working with NULL variable

    SELECT a   
    FROM t1   
    WHERE a <> 999;         -- NOT working with NULL value

    -- Drop table t1.  
    DROP TABLE dbo.t1;  

Could anyone explain why the "virtual" example is working in a different way then the MSDN example?

Virtual example:
+--------------+--------------+-----------+
| @Null = NULL | @One <> NULL | 1 <> NULL |
+--------------+--------------+-----------+
|            1 |            1 |         1 |
+--------------+--------------+-----------+

MSDN example:
-- SELECT 1
+------+
|  a   |
+------+
| NULL |
| 0    |
| 1    |
+------+

-- SELECT 2
+------+
|  a   |
+------+
| 0    |
| 1    |
+------+

Upvotes: 3

Views: 323

Answers (1)

Lukasz Szozda
Lukasz Szozda

Reputation: 175596

Looks like query optimizer chooses different comparison operator:

DECLARE @varname int;  
SET @varname = 999;

SELECT a   
FROM t1   
WHERE a <> @varname;

XML execution plan:

<Predicate>
    <ScalarOperator ScalarString="[fiddle_84f7799901e54a779e8bff464a2d01f3].[dbo].[t1].[a] &lt;&gt; [@varname]">
        <Compare CompareOp="IS NOT">
            <ScalarOperator>
                <Identifier>
                    <ColumnReference Database="[fiddle_84f7799901e54a779e8bff464a2d01f3]" Schema="[dbo]" Table="[t1]" Column="a"></ColumnReference>
                </Identifier>
            </ScalarOperator>
            <ScalarOperator>
                <Identifier>
                    <ColumnReference Column="@varname"></ColumnReference>
                </Identifier>
            </ScalarOperator>
        </Compare>
    </ScalarOperator>
</Predicate> 

Compare CompareOp="IS NOT"


Second query with hardcoded value:

SELECT a   
FROM t1   
WHERE a <> 999; 

-- same as
DECLARE @varname int = 999;

SELECT a   
FROM t1   
WHERE a <> (SELECT @varname);

XML execution plan:

<Predicate>
    <ScalarOperator ScalarString="[fiddle_ac5121a789da473382366733b51ef441].[dbo].[t1].[a]&lt;&gt;(999)">
        <Compare CompareOp="NE">
            <ScalarOperator>
                <Identifier>
                    <ColumnReference Database="[fiddle_ac5121a789da473382366733b51ef441]" Schema="[dbo]" Table="[t1]" Column="a"></ColumnReference>
                </Identifier>
            </ScalarOperator>
            <ScalarOperator>
                <Const ConstValue="(999)"></Const>
            </ScalarOperator>
        </Compare>
    </ScalarOperator>
</Predicate>

Compare CompareOp="NE"

DBFiddle

EDIT:

SET ANSI_NULLS

SET ANSI_NULLS ON affects a comparison only if one of the operands of the comparison is either a variable that is NULL or a literal NULL. If both sides of the comparison are columns or compound expressions, the setting does not affect the comparison.

Upvotes: 2

Related Questions