Re: UPSERT with non-unique index

The Natural Philosopher <[email protected]> Sun, 2 Aug 2020 22:28:37 +0100
Newsgroups comp.databases.mysql
Organization A little, after lunch
Message-ID <[email protected]>
On 02/08/2020 19:15, J.O. Aho wrote:
> On 02/08/2020 18.55, Stanimir Stamenkov wrote:
>> [I've posted this on Stack Overflow: 
>> https://stackoverflow.com/questions/63194116/upsert-with-non-unique-index] 
>>
>>
>> I need to implement concurrent-safe UPSERT using a non-unique key and 
>> avoid unnecessary auto-increment of ID.
>>
>> Traditional INSERT ... ON DUPLICATE KEY [1] doesn't work for me, so 
>> I'm performing:
>>
>>      INSERT INTO table (col1, col2, col3, col4, col5)
>>      SELECT 1, 2, 'value3', 'value4', 'value5'
>>       WHERE NOT EXISTS (SELECT 1
>>                           FROM table
>>                          WHERE col3 = 'value3'
>>                            AND col4 = 'value4'
>>                            AND col5 = 'value5')
>>
>> then if it results in no row inserted, I'm performing:
>>
>>      UPDATE table
>>         SET col1 = col1 + 1,
>>             col2 = MAX(col2, 2)
>>       WHERE col3 = 'value3'
>>         AND col4 = 'value4'
>>         AND col5 = 'value5'
>>
>> There's an index:
>>
>>      CREATE INDEX ON table (col3, col4, col5)
>>
>> It is non-unique as there are legacy data that does not allow me to 
>> declare it unique.  Newer records, however, should not have duplicated 
>> (col3, col4, col5) rows.
>>
>> Unsurprisingly, using the given INSERT statement I'm getting mixed 
>> results trying to execute it concurrently from two sessions.  I can 
>> see the second session blocking until the first one commits its 
>> transaction, but then the second transaction is also able to insert a 
>> new row sometimes (or sometimes it achieves the expected of avoiding 
>> to insert a duplicate (col3, col4, col5) row).
> 
> Maybe the blocking is more then way you are testing things than what in 
> reality happens.
> 
> I would suggest you use
> 
> LOCK TABLES table WRITE;
> 
> <do what you did before>
> 
> UNLOCK TABLES;
> 
> 
> LOCK TABLES table WRITE;
> 
> UNLOCK TABLES;
> 
> https://www.mysqltutorial.org/mysql-table-locking/
> https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
> 
I don't know how SQL manages it, but if you have two asynchronous 
instances of read-modify-write, where what is written depends on what is 
read, then you MUST make the read-modify-write cycle ATOMIC - that is 
uninterruptible, by another process doing the same.

You need locking, and the attendant danger that if aprocess crashes with 
locks set...

-- 
The biggest threat to humanity comes from socialism, which has utterly 
diverted our attention away from what really matters to our existential 
survival, to indulging in navel gazing and faux moral investigations 
into what the world ought to be, whilst we fail utterly to deal with 
what it actually is.