Domanda

I have much information in the table named "A". The table named "B" is empty.

I get 500 rows from table A, which are not in the table "B":

SELECT TOP 500 * FROM A  WHERE 
NOT EXISTS ( 
        SELECT * FROM B  WHERE 
        (
             A.id1=B.id1 AND A.id2=B.id2
        )

)

Everything works well, But I have one problem here.

I have a web application, which calls that SQL query. Imagine that 200 people together call my application, there will be duplicates in the table named "B". Sow how can I lock those rows?

Is this correct?

SELECT TOP 500 * FROM A WITH (ROWLOCK)  WHERE 
NOT EXISTS ( 
    SELECT * FROM B WITH (ROWLOCK)  WHERE 
    (
        A.id1=B.id1 AND A.id2=B.id2
    )
)

FOR MORE CLEARLY:

imagine that when you open my web application, page calls (for instance Servlet or PHP) SQL query.

If many person together open my application there is threading problem. while open query is writing data to the table named B, another query parallely doing same thing.

Threading problem: while one thread read 500 row, another thread may read the same data because first thread is still writing data at this moment and has not finished it yet.

thread 1 read that data from table "A": 1,2,3,4,5,6,7 ... 500

thread 1 wriote that data to the table named "B": 1,2,3,4,5,6,7,...495

thread 1 is not finished and thread 2 read data that from table "a": 496, 497 .. 500 ,.... 995

thread 2 write 496, 497 .. 500 ,.... 995

then again thread 1 write data

495 .. 500

For exampl

È stato utile?

Soluzione

A rowlock is only going to lock the row.
Moving to tablelock would impact performance.

I would add a status column to A of InProcess.
Then in a transaction mark that to true.

So your query would also have a where A.InProcess = false.

Altri suggerimenti

I need to point out that the queries that you have posted won't actually write anything to Table B.

That being said, if you're just looking to lock down rows that are being worked on in Table A, I would suggest adding a status column to the table and using a stored procedure similar to the following.

DECLARE @Results TABLE
(
    id1 int NOT NULL,
    id2 int NOT NULL  --Assuming these are ints
)

UPDATE TOP (500) A with (ROWLOCK, READPAST)
SET [Status] = 'InProcess'
OUTPUT inserted.id1,
       inserted.id2
    INTO @Results
WHERE [Status] <> 'InProcess'

SELECT * FROM @Results temp
INNER JOIN A
    ON A.id1 = temp.id1 AND A.id2=temp.id2

I have used this method when I had multiple workers coming to a single table looking for tasks to do.

The keys points here are:

  1. SELECT statement do not lock or block, but UPDATE statments will.
  2. The ROWLOCK hint is only used to prevent table locks. It does not magically create row locks. We provide the hint only so that we don't lock down the whole table.
  3. The READPAST hint tells each query that if it encounters a row lock, it should proceed on the the next row instead of waiting on the lock to release. We provide this hint so that multiple queries can execute at the same time.
  4. We set the [Status] field to indicate that this row is being worked on and should be ignored until such time as we decide to reset the status (if ever). This prevents multiple queries from reaching the status after the locks have been released.
  5. While we've used an UPDATE to give us the locks we want, we still need to actually retrieve some data. The OUTPUT statement into a table variable lets us capture the keys so that we can merge back into the table later.

There's a lot going on here, and maybe you don't need all this, but this is the method I found to give me high concurrency while preventing race conditions.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top