Добавлять в базу только уникальные строки

Тема в разделе "Базы данных", создана пользователем Sunday, 27 сен 2017.

Модераторы: latteo
  1. Sunday

    Sunday

    Регистр.:
    13 дек 2009
    Сообщения:
    778
    Симпатии:
    327
    Требуется создать таблицу и добавлять туда периодически по многу обычных текстовых строк, состоящих из разного кол-ва слов.
    Подскажите какие особенности нужно учесть при создании таблицы и как сделать так, чтобы при последующем добавлении строк, дубликаты просто отсеивались, а всё остальное добавлялось?

    Спасибо.
     
  2. Black Hat

    Black Hat

    Регистр.:
    15 май 2015
    Сообщения:
    155
    Симпатии:
    101
    Первый способ
    1. Добавить уникальный ключ. Какой тип столбца для хранения строк? Если TEXT - решение не подойдет, для VARCHAR - подойдет, но там также есть ограничение первичного ключа.
    2. Делать INSERT IGNORE при добавлении

    Второй способ - следить за уникальностью хеша (и надеяться, что не словите коллизию). В принципе тут изложено
     
    Sunday и Q_BASIC нравится это.
  3. nejtr0n

    nejtr0n Постоялец

    Регистр.:
    24 янв 2014
    Сообщения:
    127
    Симпатии:
    72
    Если данные "горячие", можно хранить в каком нибудь in memory хранилище (типа redis, memcached)
     
  4. zabolots

    zabolots Постоялец

    Регистр.:
    11 сен 2012
    Сообщения:
    56
    Симпатии:
    21
    Если про MySQL (ну или MariaDB) речь, то могу посоветовать следующее:
    Индекс, как уже подсказали, должен быть по ключевому полю. Тип таблицы сделайте InnoDB, всю процедуру добавления проведите в одну транзакцию. Тогда обновление индекса произойдет сразу для всей пачки добавленных строк, а не по одной. Скорость вставки будет при этом значительно выше (в разы).
    Или можно попробовать сделать тип таблицы MyISAM (работает быстрее, но не поддерживает транзакции, существует незначительный риск потери данных) и перед вставкой записей запросом выключать инлексы, а после вставки включать (в результате индекс будет создан заново).
    Советую попробовать на тестовых данных оба эти варианта и выбрать тот, который окажется быстрее.
    <-------------- добавлено через 145 сек. -------------->
    Не думаю, что "горячие данные" этот случай ТС, но в MySQL есть специально для этого тип таблиц Memory. Работает как обычная таблица, только очень быстро - все данные в памяти, при выключении питания данные из таблицы теряются. Удобно для каких-то промежуточных вычислений.
     
    Black Hat и Sunday нравится это.
  5. Sunday

    Sunday

    Регистр.:
    13 дек 2009
    Сообщения:
    778
    Симпатии:
    327
    Кто подскажет, а как проверять уникальность в случае если в таблице не одно поле?
    К примеру имеем:
    Код:
    id | name | title
    name и title могут повторяться по отдельности, но не должны повторятся одновременно. т.е. если у них общий ID.
     
  6. zabolots

    zabolots Постоялец

    Регистр.:
    11 сен 2012
    Сообщения:
    56
    Симпатии:
    21
    Для этого используется составной индекс:
    Код:
    CREATE INDEX name_title ON articles(name, title);
     
    Sunday нравится это.
  7. Black Hat

    Black Hat

    Регистр.:
    15 май 2015
    Сообщения:
    155
    Симпатии:
    101
    Не будет работать. И какая у вас БД? В MySQL это ограничение нельзя сделать (если не рассматривать триггеры).
     
  8. Jays

    Jays Создатель

    Регистр.:
    4 окт 2015
    Сообщения:
    20
    Симпатии:
    2
    думаю это поможет
    INSERT ... ON DUPLICATE KEY UPDATE
     
    Sunday нравится это.
  9. Sunday

    Sunday

    Регистр.:
    13 дек 2009
    Сообщения:
    778
    Симпатии:
    327
    MySQL :)
    Это для меня вообще тёмный лес
     
  10. Black Hat

    Black Hat

    Регистр.:
    15 май 2015
    Сообщения:
    155
    Симпатии:
    101
    Для примера возьмем такую минималистичную табличку:
    Код:
    CREATE TABLE `articles` (
      `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(255) NOT NULL,
      `title` VARCHAR(255) NOT NULL,
      PRIMARY KEY (`id`)
    ) engine=innodb;
    
    Выполним запрос на вставку двух записей:
    Код:
    INSERT INTO `articles` VALUES
    (DEFAULT, 'sample', 'sample'),
    (DEFAULT, 'sample 1', 'sample 2')
    ;
    
    Я выполняю команды из консоли mysql, мне показывается результат. Все вставилось успешно:
    Код:
    Query OK, 2 rows affected (0.01 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    Теперь создадим триггер на вставку данных. Он срабатывает при каждой вставке.
    Код:
    DELIMITER $$
    CREATE TRIGGER `before_insert_articles`
    BEFORE INSERT ON `articles`
    FOR EACH ROW
    BEGIN
      IF NEW.name = NEW.title
      THEN
        SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT = 'Error: name = title!';
      END IF;
    END$$
    DELIMITER ;
    
    Перед строкой "DELIMITER ;" MySQL должен вывести успех:
    Код:
    Query OK, 0 rows affected (0.00 sec)
    
    Если этого не произошло, то возможные причины:
    • такой триггер уже есть (не наш случай)
    • ошибка синтаксиса (я проверил, не наш случай)
    • текущему пользователю запрещено создавать триггеры. такое может быть на шаред-хостингах. смотрите выхлоп "show grants;"
    Продолжим. Попробуем вставить одну "неправильную" запись:
    Код:
    INSERT INTO `articles` VALUES
    (DEFAULT, 'sample', 'sample');
    
    Должно вывестись:
    Код:
    > ERROR 1643 (02000): Error: name = title!
    
    Вставка не произошла. Попробуем случай посложнее, где вторая строка проходит условие:
    Код:
    INSERT INTO `articles` VALUES
    (DEFAULT, 'sample', 'sample'),
    (DEFAULT, 'sample 3', 'sample 4')
    ;
    
    Получим ту же самую ошибку:
    Код:
    > ERROR 1643 (02000): Error: name = title!
    
    Это произошло потому что триггер работает на уровне запросов.
    У нас в таблице уже есть две записи:
    Код:
    mysql> select * from articles;
    +----+----------+----------+
    | id | name     | title    |
    +----+----------+----------+
    |  1 | sample   | sample   |
    |  2 | sample 1 | sample 2 |
    +----+----------+----------+
    2 rows in set (0.00 sec)
    
    Теперь попробуем сделать "правильную" вторую запись "неправильной":
    Код:
    mysql> UPDATE `articles` SET name = title;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 2  Changed: 1  Warnings: 0
    mysql> select * from articles;
    +----+----------+----------+
    | id | name     | title    |
    +----+----------+----------+
    |  1 | sample   | sample   |
    |  2 | sample 2 | sample 2 |
    +----+----------+----------+
    2 rows in set (0.00 sec)
    
    Запрос выполнился успешно. Если необходимо задать ограничение на обновление, следует создать еще один аналогичный триггер:
    Код:
    BEFORE UPDATE ON `articles`
    
     
    Sunday нравится это.