2012-05-15 3 views
3

Моя проблема такая же, как Why does MySQL autoincrement increase on failed inserts?, но вместо увеличения поля id, я бы предпочел просто переписать запрос INSERT, который вызывает у меня проблемы. Pretend У меня есть база данных с двумя полями, id и username, где id является первичным ключом, а username - уникальный ключ. Я в основном ищу синтаксис, который бы сделал INSERT...IF NOT EXISTS. Прямо сейчас, яИзбегайте неудачных вставок, чтобы избежать ложного автоинкремента

INSERT INTO `table`(`username`) 
    VALUES ('foo') 
    ON DUPLICATE KEY UPDATE `id`=`id` 

У меня есть только одна нить запись в базе данных, поэтому мне не нужно никакой защиты параллелизмом. Предложения?

+0

почему возникает такая проблема с обновлением поля автоинкремента? – ManseUK

+0

Потому что я хочу, чтобы не использовать «ХУЖЕЦ» для ключа. –

ответ

4

Вы должны использовать это:

INSERT INTO tableX (username) 
    SELECT 'foo' AS username 
    FROM dual 
    WHERE NOT EXISTS 
     (SELECT * 
      FROM tableX 
      WHERE username = 'foo' 
     ) ; 

Если вы хотите включить va сифилисе для нескольких столбцов:

INSERT INTO tableX (username, dateColumn) 
    SELECT 'foo'      --- the aliases are not needed    
     , NOW()      --- actually 
    FROM dual 
    WHERE NOT EXISTS 
     (SELECT * 
      FROM tableX 
      WHERE username = 'foo' 
     ) ;      
+0

Что такое 'FROM dual'? ** ИЗМЕНИТЬ ** Игнорируйте меня. Нашел это в руководстве. Большой ответ +1 – eggyal

+0

'dual' - это фиктивная таблица с одной строкой (первоначально в СУБД Oracle, где у нее было 2 строки, таким образом, имя). Его можно использовать для построения таблицы из 1 строки. –

+0

Почему это работает, когда в руководстве указано «* вы не можете вставить в таблицу и выбрать из той же таблицы в подзапросе *»? Это потому, что один только вставляет, если ничего не было выбрано? Или «СУЩЕСТВУЕТ» избегать фактического выбора чего-либо? – eggyal

0

использование INSERT IGNORE INTO table. Проверьте ниже пост

http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html

Вы можете сделать это нравится:

INSERT INTO `table`(`username`) 
VALUES ('foo')  
ON DUPLICATE KEY UPDATE `username`= 'foo' 

ИЛИ

INSERT IGNORE INTO `table` 
SET `username` = 'foo' 
+1

Нет. Это все еще автоинкрементирует 'id', даже если ни одна строка не вставлена. –

+0

Извините, я так не думаю. Если существует повторяющийся ключ, он пропустит его; если нет, тогда будет вставлена ​​новая строка, и при этом будет увеличен id. вы ищете другое поведение? – Rahul

+1

Он делает. Я протестировал его. Я запускал 'INSERT IGNORE INTO \' test \ '(\' username \ ') VALUES ('testuser')' 5 раз, а затем проверял, что такое следующее значение автоинкремента. –

3

Я не думаю, что вы можете предотвратить счетчик от приращения, по причинам, изложенным в ответах на вопрос, на который вы связаны между собой.

У вас есть три варианта:

  1. Живет с пропущенными идентификаторами; вы действительно ожидаете использовать до 64 бит?

  2. Проверка на наличие существующей записи перед пытать INSERT:

    DELIMITER ;; 
    
    IF NOT EXISTS (SELECT * FROM `table` WHERE username = 'foo') THEN 
        INSERT INTO `table` (username) VALUES ('foo'); 
    END IF;; 
    
    DELIMITER ; 
    

    Или, еще лучше, использовать FROM dual WHERE NOT EXISTS ... форму, предложенную @ypercube.

  3. Сбросить счетчик после каждой вставки. Если выполнение INSERT операции в хранимой процедуре, вы можете сделать это с помощью обработчика для повторяющихся ключевых ошибок:

    DELIMITER ;; 
    
    CREATE PROCEDURE TEST(IN uname CHAR(16) CHARSET latin1) BEGIN 
        DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN 
        -- WARNING: THIS IS NOT SAFE FOR CONCURRENT CONNECTIONS 
        SET @qry = CONCAT(
         'ALTER TABLE `table` AUTO_INCREMENT = ', 
         (SELECT MAX(id) FROM `table`) 
        ); 
        PREPARE stmt FROM @qry; 
        EXECUTE stmt; 
        DEALLOCATE PREPARE stmt; 
        SET @qry = NULL; 
        END; 
        INSERT INTO `table` (username) VALUES (uname); 
    END;; 
    DELIMITER ; 
    

    принимая во внимание (действительные) проблемы @ypercube поднятый в своих комментариях ниже относительно этой стратегии, вы могли бы вместо этого использовать:

    SELECT AUTO_INCREMENT - 1 
    FROM INFORMATION_SCHEMA.TABLES 
    WHERE table_schema = 'db_name' AND table_name = 'table'; 
    
+0

№ 4, никогда не используйте вариант 4. Это не безопасно. –

+0

Я попытался добавить ваш «ALTER TABLE» в хранимой процедуре, но MySQL закричал на меня. Что здесь не так? 'CREATE DEFINER = \' я \ '@ \' локальный \ ' ПРОЦЕДУРА \' TEST \ '(IN \' uname \ 'CHAR (16) CHARSET latin1) НЕ DETERMINISTIC ИЗМЕНЯВШИХ SQL DATA SQL DEFINER SECURITY INSERT INTO \ 'test \' (\ 'username \') VALUES (uname) ON DUPLICATE KEY (ALTER TABLE \ 'table \' AUTO_INCREMENT = (SELECT MAX (id) FROM \ 'table \')); ' –

+0

@ypercube : Почему это не безопасно, учитывая, что ОП заявляет, что у него есть «* только один поток, записывающий в базу данных, поэтому мне не нужна какая-либо защита от параллелизма»? – eggyal

0

вы также можете проверить перед вставкой, что пользователь, если он был введен раньше, это будет стоить дополнительного вызова в вашу БД, но, если пользователь уже существует, вы можете вернуть информацию, вы нашли и поддельную вставку избегают.

2

вы можете просто сбросить поле автоматического приращения после несостоявшегося вставки

ALTER TABLE table_name AUTO_INCREMENT = 1;

это не приведет к сбросу автоматического приращения к 1, но сбросит его к текущему максимуму плюс один

также отметить, что пользователь вы связаны с к БД должна была изменять привилегии для целевой таблицы

+1

лучший ответ lol – Leo

Смежные вопросы