MySQL INSERT IF (declaraciones personalizadas si)

Primero, aquí está el resumen conciso de la pregunta:

¿Es posible ejecutar una INSERT condicionalmente? Algo parecido a esto:

 IF(expression) INSERT... 

Ahora, sé que puedo hacer esto con un procedimiento almacenado. Mi pregunta es: ¿puedo hacer esto en mi consulta?


Ahora, ¿por qué querría hacer eso?

Supongamos que tenemos las siguientes 2 tablas:

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

Ahora, digamos que viene un pedido de 20 muñecos Voodoo (identificador de producto 2).
Primero verificamos si hay suficiente cantidad disponible:

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

Entonces, si se evalúa como verdadero, ejecutamos una consulta INSERT .
Hasta aquí todo bien.


Sin embargo, hay un problema con la concurrencia.
Si dos pedidos llegan al mismo tiempo , es posible que ambos lean la cantidad disponible antes de que cualquiera de ellos ingrese el pedido. Entonces ambos harán el pedido, excediendo así el qty_on_hand .


Entonces, volviendo a la raíz de la pregunta:
¿Es posible ejecutar una INSERT condicionalmente, para que podamos combinar ambas consultas en una sola?

Busqué mucho, y el único tipo de statement INSERT condicional que pude encontrar fue ON DUPLICATE KEY , que obviamente no se aplica aquí.

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

Si no se devuelven las filas de la selección (porque su condición especial es falsa), no se insertará.

Usando su esquema de la pregunta (suponiendo que su columna de id sea auto_increment ):

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

Esto no insertará filas si no hay suficientes existencias disponibles, de lo contrario creará la fila de orden.

Buena idea por cierto!

Tratar:

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

Si existe un producto con id igual a 2 y el qty_on_hand es mayor o igual a 20 para este producto, se producirá un inserto con los valores product_id = 2 y qty = 20 . De lo contrario, no se insertará.

Nota : Si los ID de sus productos son únicos, es posible que desee agregar una cláusula LIMIT al final de la instrucción SELECT .

Probablemente estés resolviendo el problema de la manera incorrecta.

Si tiene miedo de que se produzcan dos operaciones de lectura al mismo tiempo y, por lo tanto, una funcionará con datos obsoletos, la solución es usar lockings o transacciones .

Haz que la consulta haga esto:

  • mesa de locking para leer
  • leer tabla
  • tabla de actualización
  • deslocking

No estoy seguro acerca de la concurrencia, necesitará leer sobre el locking en mysql, pero esto le permitirá estar seguro de que solo toma 20 elementos si hay 20 elementos disponibles:

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

Luego puede verificar cuántas filas se vieron afectadas. Si ninguno se vio afectado, no tenía suficiente stock. Si se afectó 1 fila, efectivamente ha consumido el stock.