On DUPLICATE KEY UPDATE with unique columns

user3342816 :

Having a table that holds some UI settings for a data-table, typically like:

| ID  |   name | alias |   pos  | def_pos | disp |
+-----+--------+-------+--------+---------+------+
|  1  |  name1 | bar   |   1    |    1    |   1  | 
|  2  |  name2 | foo   |   3    |    2    |   1  |
|  3  |  name3 | bar   |   2    |    3    |   1  |
|  4  |  name4 | baz   |   4    |    4    |   0  |
  • ID: PRIMARY
  • name: UNIQUE

Here pos can be updated by user by dragging columns in a graphical interface.

As name and id are unique and I update multiple rows at once I use INSERT and ON DUPLICATE KEY UPDATE instead of UPDATE when changing values. So for example if swapping row 2 and 3 from example above:

INSERT INTO
    ui_data_columns (id, name, pos)
VALUES
    (2, '', 2),
    (3, '', 3)
ON DUPLICATE KEY UPDATE
    pos = VALUES(pos)

As per:

So far so good :P


Now my thought was to add UNIQUE constraint to pos and def_pos as well as one column can not have same position. It is not possible set two column values to the same from the UI, but would be nice to have the constraints as they are unique and ... well, to learn.

Challenge then becomes that if one try to use the KEY UPDATE one run into conflicts when swapping two values. Thus I can not say pos = VALUES(pos) when pos exists - even though it get rectified in same statement (if you get what I mean). Assume they are inserted in sequence so typically:

INSERT INTO ui_data_columns (id, name, pos)
VALUES (2, '', 2)
ON DUPLICATE KEY UPDATE pos = VALUES(pos)

# Error: pos = 2 exists

Even though this fixes is:

INSERT INTO ui_data_columns (id, name, pos)
VALUES (3, '', 3)
ON DUPLICATE KEY UPDATE pos = VALUES(pos)

How to go about doing this?


Small thoughts:

  • Some query magic?
  • Use a temporary copy of the table without constraints clear and fill?
  • Delete records in question first then insert?
  • Drop the constraint for the duration of the insert then re-apply?
  • ...
Akina :

A trick - use intermediate. I.e. perform 3 updates, not 2.

create table test (id int primary key, pos int unique);
insert into test values (1,1),(2,3),(3,2),(4,4);
select * from test;
id | pos
-: | --:
 1 |   1
 3 |   2
 2 |   3
 4 |   4
insert into test values
(3,3), (2,2)
on duplicate key update pos = values(pos);
Duplicate entry '3' for key 'test.pos'
select * from test;
id | pos
-: | --:
 1 |   1
 3 |   2
 2 |   3
 4 |   4
insert into test values
(2,0), (3,3), (2,2)
on duplicate key update pos = values(pos);

select * from test;
id | pos
-: | --:
 1 |   1
 2 |   2
 3 |   3
 4 |   4

db<>fiddle here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=372446&siteId=1