Reputation: 2427
i have an existing table
id, name, user
15, bob, 1
25, alice, 2
30, ann, 1
55, bob, 2
66, candy, 1
we want the name records for user 1 to now be set to the values in this string:
"ann, candy, dave"
if I do it the easy way
delete from table where user = 1
insert into table (name,user) values (ann,1), (candy,1), (dave,1)`
then the table now looks like this
id, name, user
25, alice, 2
55, bob, 2
67, ann, 1
68, candy, 1
69, dave, 1
i.e. new rows are created. I don't want the new identities, and over time in huge tables, this causes fragmentation and identity holes and so on. what is the most efficient way in SQL to reduce this to just the actual 2 required operations:
delete from table where user = 1 and name is not in the string "ann, candy, dave", so that the table is then:
25, alice, 2
30, ann, 1
55, bob, 2 66, candy, 1 `
insert into table user = 1, name = any value from "ann, candy, dave" that does not match name/user=1 , so that the table is then:
25, alice, 2
30, ann, 1
55, bob, 2
66, candy, 1
67, dave, 1
Upvotes: 0
Views: 35
Reputation: 1270733
It sounds like you have a list and want to process it twice, once for deletes and once for inserts. Store the list in a temporary table and use that for processing.
Along the way, start with a unique index on user, name
to prevent updates into the table:
create unique index idx_table_user_name on table(user, name);
This seems to be a requirement for your data, so let the database enforce it. Then the code for processing is like:
create temporary table toprocess as (
select 1 as user, 'ann' as name union all
select 1, 'candy' union all
select 1, 'dave'
);
create index idx_toprocess_user_name on toprocess(user, name);
delete t
from table t
where t.user in (select p.user from toprocess p) and
not exists (select 1 from toprocess p where p.user = t.user and p.name = t.name);
insert into table(user, name)
select user, name
from toprocess
on duplicate key update user = values(user);
Although this might look a bit complicated, it lets you handle multiple users at the same time. And, the list for processing is only entered once, which reduces the scope for error.
Upvotes: 1
Reputation: 35790
It is not so clear but may be this is what you want:
delete from table where user = 1 and name not in('ann', 'candy', 'dave')
insert into table
select * from(select 'ann' as name
union all
select 'candy'
union all
select 'dave') t
where t.name not in(select name from table where user = 1)
Upvotes: 0