简体   繁体   中英

Violation of PRIMARY KEY constraint error occurred in the insert sql right after the update sql returns 0 rows

I have the code like this

        if (***Adapter.UpdateRow(memberID, Type) == 0)
        {
            ***Adapter.InsertRow(memberID, Type);
        }

but i get the Violation of PRIMARY KEY constraint error in the InsertRow. the primary key in table is memberID and Type and here are the sqls:

InsertRow

        string sql = @"
            INSERT INTO TBL***** ( 
                   MemberID
                 , Type
                 , DateTime
                   ) 
            VALUES ( 
                   @MemberID 
                 , @Type 
                 , GETDATE() 
                   )
        ";

        var cm = new SqlCommand(sql.ToString());
        cm.Parameters.Add("@MemberID", SqlDbType.Int).Value = memberID;
        cm.Parameters.Add("@Type", SqlDbType.Int).Value = (int)Type;

        return this.ExecuteNonQuery(cm);

UpdateRow

            string sql = @"
            UPDATE TBL****
               SET DateTime = GETDATE()
             WHERE MemberID = @MemberID
               AND Type = @Type
        ";

        var cm = new SqlCommand(sql.ToString());
        cm.Parameters.Add("@MemberID", SqlDbType.Int).Value = memberID;
        cm.Parameters.Add("@Type", SqlDbType.Int).Value = (int)Type;

        return this.ExecuteNonQuery(cm);

i'm really want to now the reason why it happened (right after the update one).


thank you all for your comments . I think that maybe there are two threads that conflicted with each other ,then the first one inserted successfully but the second one failed.

I have this logic in my web site's user registration and it happends when the user click the activation link in the register mail. I think the user may double click(with a high speed) the link and the error occured.

A Violation of PRIMARY KEY constraint means that you're attempting to insert a record that will create a duplicate on the primary key fields.

Check the table definition and note the fields in the primary key.

If you can post the table definition in your question for other readers even better :)

Edit after comment added

Given your primary key contains both MemberId and Type, there is likely a logic error in the update, or there is a scenario where no results are returned in the update.

You should use an upsert (update or insert) as Allan S. Hansen has suggested.

The logic is as follows:

  1. Check if the record exists
  2. Either update if exists or insert a new record

You wouldn't normally perform an update to check if the record exists, as the update may perform no changes in some scenarios.

Your code is relying on the ROWCOUNT being returned after executing DML. This is not guaranteed to happen, because is subject to SET NOCOUNT setting. When SET NOCOUNT is ON the returned rowcount is always 0, irrelevant how many rows where actually updated. The returned rowcount is what ExecuteNonQuery() returns.

You could check and ensure SET NOCOUNT is OFF but it would still be a lost cause. The code you propose is fundamentally flawed in presence of concurrency: multiple client apps can run the UPDATE simultaneously and all conclude they should INSERT and then only one would succeed. The correct way to do this is to use MERGE statement:

MERGE INTO TBL**** as t
USING (VALUES (GETDATE(), @MemberId, @Type)) AS s (date, MemberID, Type)
  ON t.MemberID =s.MemberID and t.Type = s.Type
WHEN MATCHED  THEN
  UPDATE SET t.DateTime = s.date
WHEN NOT MATCHED BY TARGET
  INSERT (MemberID, Type, DateTime) VALUES (s.MemberID, s.Type, s.DateTime);

This is a single statement that does wither the INSERT or the UPDATE without the concurrency risks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM