Home > Net >  SQL Server Locking Rows/Table
SQL Server Locking Rows/Table

Time:12-06

I have a stored procedure that must do the following:

  • Select unflagged rows from table
  • Flag selected rows
  • Dispatch 500 rows to client

I have four clients, each running every minute, and call this stored procedure.

Each client must receive only their own 500 rows. Two clients should never get the same row.

Sometimes it happens that two clients take the same lines, I want to resolve this scenario.

(My idea is that rewrite code to only one update statement with output)

Which is the best approach for this scenario? Do I need configure isolation level?

This is current code example:

SET XACT_ABORT ON

BEGIN TRANSACTION

    DECLARE @IDs TABLE (ID INT) -- declaring table variable
    
    INSERT INTO @IDs    -- insert unflagged IDs (data) into table variable with update lock
        SELECT TOP 500 p.[ID]
        FROM [Core].[Poruka] p WITH (UPDLOCK)
        JOIN Core.vwKorisnickiRacunOmogucenoSlanje vk
             ON p.KorisnickiRacunSifra = vk.KorisnickiRacunSifra
        WHERE 
            (p.Zauzeto = 0 OR p.Zauzeto IS NULL)
            AND vk.OmogucenoSlanjePoruka = 1
        ORDER BY 
            vk.Prioritet

    UPDATE p -- update table to flagged
    SET Zauzeto = 1
    FROM Core.Poruka p
    JOIN @IDs tmp ON tmp.ID = p.ID

    SELECT *      -- select data to client
    FROM [Core].[Poruka] p
    WHERE p.ID IN (SELECT ID 
                   FROM @IDs)

    COMMIT

CodePudding user response:

You don't need a transaction here.

Simply use OUTPUT to output the changed rows to the client

UPDATE p
SET Zauzeto = 1   -- update table to flagged
OUTPUT inserted.* -- output to client
FROM (
    SELECT TOP (500)
        p.*
    FROM [Core].[Poruka] p
        JOIN Core.vwKorisnickiRacunOmogucenoSlanje vk
        ON p.KorisnickiRacunSifra = vk.KorisnickiRacunSifra
    WHERE 
        (p.Zauzeto = 0 OR p.Zauzeto IS NULL)
        AND vk.OmogucenoSlanjePoruka = 1
    ORDER BY 
        vk.Prioritet
) p;

You should really have this join as an EXISTS, to avoid duplicate rows coming back from the join.

UPDATE p
SET Zauzeto = 1   -- update table to flagged
OUTPUT inserted.* -- output to client
FROM (
    SELECT TOP (500)
        p.*
    FROM [Core].[Poruka] p
    WHERE 
        (p.Zauzeto = 0 OR p.Zauzeto IS NULL)
        AND EXISTS (SELECT 1
            FROM Core.vwKorisnickiRacunOmogucenoSlanje vk
            WHERE
                p.KorisnickiRacunSifra = vk.KorisnickiRacunSifra  --join condition
                AND vk.OmogucenoSlanjePoruka = 1
        )
    ORDER BY 
        vk.Prioritet
) p;

The SERIALIZABLE (aka HOLDLOCK) isolation level is wise here.

  • Related