Как обновить в SQL, чтобы получить различные кортежи / чтобы не нарушать уникальное ограничение

0

У меня есть таблица сопоставлений с единственным отличием от кортежа (c_id, t_id).

Вот несколько примеров данных, чтобы проиллюстрировать ситуацию:

id  c_id    t_id  
----------------
1   10      2
2   10      3
3   10      7
4   12      2
5   13      3

Я написал функцию слияния для t_ids (x, y → z OR x, y → x). Если у моего контента (c_id) есть и t_ids, то я, конечно, нарушаю ограничение, используя этот оператор:

UPDATE mapping_table
SET t_id = '$target_tid'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';

Результат:

id  c_id    t_id
----------------
1   10      4
2   10      4       /* violates unique constraint */
3   10      7

Теперь я придумал следующее:

/* delete one of the duplicate entries */
DELETE FROM mapping_table
WHERE   ( SELECT count(c_id)
          FROM mapping_table
          WHERE t_id = '$t1_id' OR t_id = '$t2_id'
        ) > 1;

/* update the remaining row */
UPDATE mapping_table
SET t_id = '$target_tid'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';

Теперь я получаю следующую ошибку:
You can't specify target table 'mapping_table' for update in FROM clause

Мои вопросы:

  • Что именно здесь неправильно? Является ли оператор DELETE замеченным как обновление и не может использоваться в предложении WHERE?
  • Это более эффективный способ сделать это?
  • 1
    В операторе delete нет ничего плохого, но mysql не любит, чтобы та же таблица появлялась в подзапросе.
Теги:
sql-update
merge
duplicates
unique-constraint

3 ответа

1
Лучший ответ

Ошибка, с которой вы сталкиваетесь, является особенностью MySQL. Вы можете обойти это с помощью двойного набора подзапросов:

DELETE FROM mapping_table
WHERE  (select *
        from ( SELECT count(c_id)
               FROM mapping_table
               WHERE t_id = '$t1_id' OR t_id = '$t2_id'
             ) > 1
        ) t

Чтобы устранить проблему, просто удалите все идентификаторы, за исключением минимума. Я думаю, что это также может работать:

delete from mapping_table
where id > (select minid from (select min(id) from mapping_table mt2
                               where mt2.c_id = mapping_table.c_id and
                                     mt2.t_id = mapping_table.t_id
                              )
           )

Вы также можете сохранить список идентификаторов во временной таблице и использовать это в запросе:

create temporary table minids as
     select c_id, t_id, min(id) as minid
     from mapping_table
     group by c_id, t_id;

delete from mapping_table
where exists (select 1 from minids
              where mt2.c_id = mapping_table.c_id and
                    mt2.t_id = mapping_table.t_id and
                    mt2.minid > mapping_table.id
             )
  • 0
    У меня были некоторые проблемы с вашими утверждениями, которые были неполными и имели синтаксические ошибки, но так как ваши предложения подтолкнули меня в правильном направлении, я принимаю ваш ответ. - Проверьте мой ответ, чтобы увидеть, что мне действительно нужно.
0

Я искал это решение. Производительность, вероятно, удивительно низкая, но, по крайней мере, я нашел рабочее решение (и узнал что-то).

/* actually delete rows that will become duplicate after the update */
DELETE FROM mt1 USING mapping_table AS mt1 WHERE id IN (

    /* sub-query to allow `mapping_table` in the DELETE statement */
    SELECT * FROM (

        /* select ids/rows with one t_id available */
        SELECT id
        FROM mapping_table AS mt2
        WHERE mt2.tag_id = $t1_id AND c_id IN (

            /* select ids/rows with both t_id available */
            SELECT c_id
            FROM mapping_table AS mt3
            WHERE mt3.c_id = mt2.c_id AND mt3.tag_id = $t2_id)

    /* alias needed for every derived table */
    ) as mres
)

/* Update to merge t_ids */
UPDATE mapping_table
SET t_id = '$target_id'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';
0

Попробуйте это

DELETE FROM mapping_table
    WHERE   ( SELECT count(c_id)
      FROM mapping_table
      WHERE t_id = '$t1_id' OR t_id = '$t2_id'
      Having count(c_id) > 1
    );

EDIT:

попробуйте это в своем статусе обновления

 UPDATE mapping_table
 SET t_id = '$target_tid'
WHERE t_id in (select t_id from mapping_table where t_id= '$t1_id' OR t_id = '$t2_id') 
  • 0
    Спасибо, но это все равно вызывает ту же ошибку. You can't specify target table 'mapping_table' for update in FROM clause
  • 0
    попробуй мой обновленный ответ

Ещё вопросы

Сообщество Overcoder
Наверх
Меню