it-swarm.com.ru

MySQL INSERT IF (пользовательские операторы if)

Во-первых, вот краткое изложение вопроса:

Можно ли выполнить оператор INSERT условно? Что-то похожее на это:

IF(expression) INSERT...

Теперь я знаю, что могу сделать это с помощью хранимой процедуры . Мой вопрос: могу ли я сделать это в своем запросе?


Теперь, почему я хотел бы сделать это?

Предположим, у нас есть следующие 2 таблицы:

products: id, qty_on_hand
orders: id, product_id, qty

Теперь, допустим, поступил заказ на 20 кукол вуду (идентификатор продукта 2).
Сначала мы проверяем, достаточно ли количества в наличии:

SELECT IF(
    ( SELECT SUM(qty) FROM orders WHERE product_id = 2  ) + 20
    <=
    ( SELECT qty_on_hand FROM products WHERE id = 2)
, 'true', 'false');

Затем, если он оценивается как true, мы запускаем запрос INSERT.
Все идет нормально.


Однако есть проблема с параллелизмом.
Если поступят 2 заказа в в одно и то же время , они оба могут прочитать имеющееся количество до того, как какой-либо из них поступит в заказ Затем они оба разместят заказ, таким образом превышая qty_on_hand.


Итак, вернемся к корню вопроса:
Можно ли условно выполнить оператор INSERT, чтобы мы могли объединить оба этих запроса в один?

Я много искал, и единственным типом условного оператора INSERT, который я смог найти, был ON DUPLICATE KEY, который, очевидно, здесь не применим.

28
Joseph Silber
INSERT INTO TABLE
SELECT value_for_column1, value_for_column2, ...
FROM wherever
WHERE your_special_condition

Если никакие строки не возвращаются из выбора (потому что ваше специальное условие ложно), вставка не происходит.

Использование схемы из вопроса (при условии, что ваш столбец id равен auto_increment):

insert into orders (product_id, qty)
select 2, 20
where (SELECT qty_on_hand FROM products WHERE id = 2) > 20;

Это не будет вставлять строки, если на складе недостаточно, в противном случае будет создана строка заказа.

Хорошая идея, кстати!

45
Bohemian

Пытаться:

INSERT INTO orders(product_id, qty)
SELECT 2, 20 FROM products WHERE id = 2 AND qty_on_hand >= 20

Если существует продукт с id, равным 2, и qty_on_hand для этого продукта больше или равен 20, то произойдет вставка со значениями product_id = 2 и qty = 20. В противном случае вставка не произойдет. 

Примечание : Если ваши идентификаторы продуктов уникальны, вы можете добавить предложение LIMIT в конце оператора SELECT.

17
Shef

Не уверен насчет параллелизма, вам нужно прочитать о блокировке в mysql, но это позволит вам быть уверенным, что вы получите только 20 предметов, если доступно 20 предметов:

update products 
set qty_on_hand = qty_on_hand - 20 
where qty_on_hand >= 20
and id=2

Затем вы можете проверить, сколько строк было затронуто. Если никто не пострадал, у вас не было достаточно запаса. Если на 1 ряд повлияло, вы эффективно использовали запас.

2
My Other Me

Вы, вероятно, решаете проблему неправильно. 

Если вы боитесь, что две операции чтения будут происходить одновременно, и, таким образом, одна из них будет работать с устаревшими данными, решение состоит в том, чтобы использовать блокировки или транзакции .

Пусть запрос сделает это:

  • блокировка таблицы для чтения
  • читать таблицу
  • Обновление таблицу 
  • снять блокировку
1
Konerak