Mysql
 sql >> база данни >  >> RDS >> Mysql

MySQL транзакция:SELECT + INSERT

Това, от което се нуждаете, е заключване . Транзакциите наистина „не са строго необходими“.

Можете да избирате между "песимистично заключване" и "оптимистично заключване". Решението коя от тези две възможности зависи от вас и трябва да бъде оценено основно като се има предвид:

  • нивото на едновременност, което имате
  • продължителността на атомарните операции, които трябва да бъдат в базата данни
  • сложността на цялата операция

Ще препоръчам да прочетете тези две, за да изградите представа за свързаните неща:

Пример за по-добро обяснение

Това може би не е толкова елегантно, но е само пример, който показва как е възможно да се направи всичко без транзакция (и дори без УНИКАЛНИ ограничения). Това, което е необходимо да направите, е да използвате следния комбиниран оператор INSERT + SELECT и след неговото изпълнение за да проверите броя на засегнатите редове. Ако броят на засегнатите редове е 1, значи е успешен, иначе (ако е 0) е имало сблъсък и другата страна е спечелила.

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= @endTime AND `end` >= @startTime
    AND `devices_id` = @deviceId)
GROUP BY (1);

Това е пример за оптимистично заключване, получено без транзакции и с една SQL операция.

Както е написано, има проблем, че трябва да има поне един ред вече в slot таблица, за да работи (в противен случай клаузата SELECT винаги ще връща празен набор от записи и в този случай нищо не се вмъква evei, ако няма сблъсъци. Има две възможности да го накарате действително да работи:

  • вмъкнете един фиктивен ред в таблицата, може би с датата в миналото
  • пренапишете, така че основната клауза FROM да се отнася до всяка таблица, която има поне един ред или по-добре създайте една малка таблица (може би с име dummy ) само с една колона и само един запис в нея и пренапишете както следва (обърнете внимание, че вече няма нужда от клаузата GROUP BY)

    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
    FROM `dummy`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= @endTime AND `end` >= @startTime
        AND `devices_id` = @deviceId);
    

Ето поредица от инструкции, които, ако просто копирате/поставите, показват идеята в действие. Предполагам, че кодирате дата/час в полета int като число с обединени цифри на дата и час.

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
VALUES (1008141200, 1008141210, 11, 2, 'Dummy Record', 14)

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141206, 1408141210, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141210 AND `end` >= 1408141206
    AND `devices_id` = 14)
GROUP BY (1);

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141208, 1408141214, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141214 AND `end` >= 1408141208
    AND `devices_id` = 14)
GROUP BY (1);

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141216, 1408141220, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141220 AND `end` >= 1408141216
    AND `devices_id` = 14)
GROUP BY (1);

SELECT * FROM `slot`;

Това очевидно е краен пример за оптимистично заключване, но в крайна сметка е много ефективен, защото всичко се прави само с една SQL инструкция и с ниско взаимодействие (обмен на данни) между сървъра на базата данни и php кода. Освен това практически няма "реално" заключване.

...или с песимистично заключване

Същият код може да се превърне в добра реализация на Pessimistc Locking, просто обграждаща изрични инструкции за заключване/отключване на таблица:

LOCK TABLE slot WRITE, dummy READ;

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
FROM `dummy`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= @endTime AND `end` >= @startTime
    AND `devices_id` = @deviceId);

UNLOCK TABLES;

Разбира се, в този случай (песимистично заключване) SELECT и INSERT могат да бъдат разделени и някакъв php код да бъде изпълнен между тях. Този код обаче остава много бърз за изпълнение (без обмен на данни с php, без междинен php код) и така продължителността на Pessimistic Lock е възможно най-кратката. Поддържането на Pessimistic Lock възможно най-кратко е ключов момент, за да се избегне забавянето на приложението.

Във всеки случай трябва да проверите броя на засегнатите записи, връщана стойност, за да разберете дали е успяла, тъй като кодът е практически същият и така получавате информацията за успех/неуспех по същия начин.

Тук http://dev.mysql.com/doc/ refman/5.0/en/insert-select.html те казват, че "MySQL не позволява едновременни вмъквания за изрази INSERT ... SELECT" така че не би трябвало да е необходимо Pessimistic Lock, но така или иначе това може да бъде добър вариант, ако смятате, че това ще се промени в бъдещите версии на MySQL.

Аз съм „Оптимист“ че това няма да се промени;-)




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да изтриете MySQL запис след определено време

  2. Свойството на PHP обект има скоби в него

  3. Сравнете редове в същата таблица в mysql

  4. Как да премахна нови знаци от редове от данни в mysql?

  5. Две Laravel приложения на един и същ сървър, които са в конфликт едно с друго