Search code examples
sqlsql-servert-sqlsql-server-2014

SQL Server: is this a bug or do I have a misunderstanding?


Today I'm found a very sticky problem on SQL Server 2014.

Scenario: I want to pay awards to my customer (some pin code for cell phone operator)

In last cycle of loop T.Used = 0 condition is bypassed and is not working. I know in other conditions in that query (T.Cash < (@myAwards - @paid)) is there a mistake and I must to use T.Cash <= (@myAwards - @paid) instead of this but please focus on main question.

Why it's happened when I update Used flag to 1 (True) then in next loop it's selected while it doesn't have a valid condition (T.Used = 0)?

DECLARE @myAwards INT = 90000,
        @paid INT = 0;

DECLARE @Temp TABLE
(
    Id INT NOT NULL,
    Pin VARCHAR(100) NOT NULL,
    Cash INT NOT NULL,
    [Weight] INT NULL,
    Used BIT NOT NULL 
)

INSERT INTO @Temp
    SELECT 
       UPFI.Id, UPFI.PinCode,
       PT.Cash, NULL, 0
   FROM 
       dbo.UploadedPinFactorItem UPFI WITH (NOLOCK)
   INNER JOIN 
       dbo.PinType PT WITH (NOLOCK) ON PT.ID = UPFI.PinTypeID
   WHERE 
       PT.Cash <= @myAwards

UPDATE T
SET [Weight] = ISNULL((SELECT COUNT(TT.Id) 
                       FROM @Temp TT 
                       WHERE TT.Cash = T.Cash), 0) * T.Cash
FROM @Temp T

--For debug (first picture)
SELECT * FROM @Temp

DECLARE @i int = 1
DECLARE @count int = 0
SELECT @count = COUNT([Id]) FROM @Temp C WHERE C.Used = 0

WHILE (@i <= @count AND @paid < @myAwards)
BEGIN       
    DECLARE @nextId INT,
            @nextCash INT,
            @nextFlag BIT;

    -- 'T.Used = 0' condition is by passed
    SELECT TOP (1) 
        @nextId = T.Id, @nextCash = T.Cash, @nextFlag = T.Used
    FROM 
        @Temp T
    WHERE 
        T.Used = 0
        AND T.Cash < (@myAwards - @paid)
    ORDER BY  
        T.[Weight] DESC, T.Cash DESC, T.Id DESC

    UPDATE @Temp
    SET Used = 1
    WHERE Id = @nextId

    SET @i = @i + 1
    SET @paid = @paid + @nextCash

    --Show result in second picture
    SELECT 
        @i AS 'i', @paid AS 'paid', @nextFlag AS 'flag', @nextId AS 'marked Id',* 
    FROM
        @temp T
    ORDER BY  
        T.[Weight] DESC, T.Cash DESC, T.Id DESC
END

SELECT 'final', @paid, *  
FROM @temp T
ORDER BY T.[Weight] DESC, T.Cash DESC, T.Id DESC

Please let me to understand this is a bug or I have misunderstanding

First screenshot:

First picture

Second screenshot (result of loop):

Second picture

Third screenshot (final result):

Third picture


Solution

  • As per my comments:

    This isn't a problem with the condition, the problem is with the implemented logic. After i = 4, there are no more rows where T.Used = 0 AND T.Cash < (@myAwards - @paid), that makes it so your reassigning variables gets zero rows, so they mantain the previous values.

    You can test this behavior by doing:

    DECLARE @A INT = 10; 
    
    SELECT @A = object_id 
    FROM sys.all_objects 
    WHERE name = 'an object that doesn''t exist' 
    
    SELECT @A;