Как выбрать с множественными условиями IN во ВРЕМЕННОЙ таблице?

#mysql

#mysql

Вопрос:

Проблема

Я хотел бы очистить беспорядочную базу данных и заменить ссылки на повторяющиеся записи. В этом пользовательском (моем гораздо более сложном) примере у меня есть две таблицы:

  • Octopuses
  • Цвет

Мы знаем, что:

  • Осьминог имеет цвет.
  • Таблица colors содержит дубликаты
  • Некоторые осьминоги могут иметь тот же цвет, что и другие осьминоги, но отличаться color_id .

Способ, которым я решил эту проблему, включает TEMPORARY таблицы. Чтобы избежать ошибки:

 Can't Reopen Table 'duplicates'
  

Я просто дублирую свою TEMPORARY таблицу много раз:

 CREATE TEMPORARY TABLE duplicates1 SELECT * FROM duplicates;
CREATE TEMPORARY TABLE duplicates2 SELECT * FROM duplicates;
  

Вопрос

Я хотел бы избежать клонирования TEMPORARY таблиц.

Данные

 CREATE TABLE `test`.`octopuses` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `color_id` INT NOT NULL,
  PRIMARY KEY (`id`));

CREATE TABLE `test`.`colors` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`id`));
  

С некоторыми цветами с дублированием:

 INSERT INTO colors (name) VALUES
    ('cream'), ('sepia'), ('daffodil'), ('lipstick'),                        
    ('lipstick'), ('garnet'), ('flamingo'), ('navy'),                        
    ('chartreuse'), ('garnet'), ('flamingo'), ('juniper'),                             
    ('flint'), ('flint'), ('charcoal'), ('garnet');                     
  

И несколько осьминогов:

 INSERT INTO octopuses (name, color_id) VALUES
    ('Bubbles', 1), ('Inky', 8), ('Octavius', 1),
    ('Sir Inks-A-Lot', 7), ('Octavia', 16), ('Kraken', 6),
    ('Oncho', 15), ('Big Floppy Sea Spider', 14), ('Calamari', 2),
    ('Scuba Doo', 13), ('Squidward Tentacles', 5), ('Wiggleton', 9),
    ('Cthulhu', 2), ('Octopussy', 3), ('Triton', 10), 
    ('Doctor Octopus', 11), ('Billy The Squid', 4), ('Stretch', 12);
  

Пример

Чтобы решить проблему, я сначала создаю список дубликатов:

 CREATE TEMPORARY TABLE duplicates SELECT
    *, COUNT(*) AS count
FROM
    colors
GROUP BY name
HAVING count > 1;
  

Вот оно:

 mysql> select * FROM duplicates;
 ---- ---------- ------- 
| id | name     | count |
 ---- ---------- ------- 
|  4 | lipstick |     2 |
|  6 | garnet   |     3 |
|  7 | flamingo |     2 |
| 13 | flint    |     2 |
 ---- ---------- ------- 
  

Затем я хотел бы создать соответствующую таблицу, в которой у меня есть id дубликат и id который нужно заменить на:

 CREATE TEMPORARY TABLE duplicates1 SELECT * FROM duplicates;
CREATE TEMPORARY TABLE duplicates2 SELECT * FROM duplicates;

CREATE TEMPORARY TABLE corresponding SELECT
    id, name,
    (SELECT
            id
        FROM
            duplicates2
        WHERE
            duplicates2.name = colors.name) AS first_id
FROM
    colors
WHERE
    name IN (SELECT
            name
        FROM
            duplicates)
        AND id NOT IN (SELECT
            id
        FROM
            duplicates1)
ORDER BY name ASC;
  

Здесь содержимое:

 mysql> SELECT * FROM corresponding;
 ---- ---------- ---------- 
| id | name     | first_id |
 ---- ---------- ---------- 
| 11 | flamingo |        7 |
| 14 | flint    |       13 |
| 10 | garnet   |        6 |
| 16 | garnet   |        6 |
|  5 | lipstick |        4 |
 ---- ---------- ---------- 
  

Затем я просто обновляю octopuses таблицу:

 CREATE TEMPORARY TABLE corresponding1 SELECT * FROM corresponding;

UPDATE octopuses
SET
    color_id = (SELECT
            first_id
        FROM
            corresponding1
        WHERE
            corresponding1.id = color_id)
WHERE
    color_id IN (SELECT
            id
        FROM
            corresponding)
  

В конце концов я удаляю дубликаты:

 DELETE FROM colors WHERE id IN (SELECT id FROM corresponding);
  

Краткое содержание

Этот пример, возможно, не лучший для иллюстрации моей проблемы, но здесь я хотел бы избежать клонирования временных таблиц и найти способ выбора с несколькими IN условиями в TEMPORARY таблицах.

Ответ №1:

Попробуйте подумать наоборот.

Вы могли бы сделать:

 UPDATE octopuses
        INNER JOIN
    (SELECT 
        *,
            (SELECT 
                    id
                FROM
                    colors
                WHERE
                    colors.name = (SELECT 
                            name
                        FROM
                            colors
                        WHERE
                            color_id = colors.id)
                LIMIT 1) AS first_color_id
    FROM
        octopuses
    HAVING color_id <> first_color_id) AS DUP ON dup.color_id = octopuses.color_id 
SET 
    octopuses.color_id = first_color_id
WHERE
    octopuses.color_id <> first_color_id;


CREATE TEMPORARY TABLE to_delete SELECT id FROM colors WHERE NOT EXISTS (
    SELECT id FROM octopuses WHERE color_id = colors.id
);

DELETE FROM colors WHERE id IN (SELECT id FROM to_delete);
  

Итак, ответ на ваш вопрос таков:

Всякий раз, когда вам нужно клонировать временную таблицу, подумайте дважды, и вы найдете другой способ, который не требует повторного открытия временной таблицы дважды!