2009-02-18 13 views
17

que tienen una tabla de MySQL de tareas a realizar, teniendo cada fila parámetros para una sola tarea.
Hay muchas aplicaciones de trabajo (posiblemente en diferentes máquinas), que realizan tareas en un bucle.
Las aplicaciones acceden a la base de datos utilizando las API nativas en C de MySQL.MySQL UPDATE y SELECT de una sola pasada

Con el fin de poseer una tarea, una aplicación hace algo así:

  • Generar un identificador único global (para simplificar, digamos que es un número)

  • UPDATE tasks
    SET guid = %d
    WHERE guid = 0 LIMIT 1

  • SELECT params
    FROM tasks
    WHERE guid = %d

  • Si la última consulta devuelve una fila, somos dueños de ella y tener los parámetros para ejecutar

¿Hay una manera de conseguir el mismo efecto (es decir, 'poseer' una fila y obtener sus parámetros) en una sola llamada al servidor?

Respuesta

0

no sé acerca de la parte llamada única, pero lo que usted está describiendo es una cerradura. Los bloqueos son un elemento esencial de las bases de datos relacionales.

No conozco los detalles de bloquear una fila, leerla y luego actualizarla en MySQL, pero con un poco de lectura de mysql lock documentation puedes hacer todo tipo de manipulaciones basadas en bloqueos.

El postgres documenation de locks tiene un gran ejemplo que describe exactamente lo que quiere hacer: bloquear la mesa, leer la tabla, modificar la tabla.

+0

Las cerraduras son una exageración en este caso. La solución anterior usa el bloqueo interno de MySQL, por lo tanto, es mucho más rápido. –

+0

Veo su punto ... no necesita bloqueos aquí ... No tengo conocimiento de una sola llamada. – Daniel

6

Es posible crear un procedimiento que lo hace:

CREATE PROCEDURE prc_get_task (in_guid BINARY(16), OUT out_params VARCHAR(200)) 
BEGIN 

    DECLARE task_id INT; 

    SELECT id, out_params 
    INTO task_id, out_params 
    FROM tasks 
    WHERE guid = 0 
    LIMIT 1 
    FOR UPDATE; 

    UPDATE task 
    SET guid = in_guid 
    WHERE id = task_id; 

END; 

BEGIN TRANSACTION; 

CALL prc_get_task(@guid, @params); 

COMMIT; 
+1

Probé algo similar. En pocas palabras: las consultas SQL nativas son mucho más rápidas que los procedimientos almacenados. –

+3

En MySQL no hay tanta diferencia como, por ejemplo, en Oracle. No puede hacerlo en una sola consulta de todos modos, pero el procedimiento es una llamada al servidor, tal como lo pidió. Con un poco de esfuerzo, puede colocar BEGIN y COMMIT en el procedimiento, pero es mejor hacerlo desde la aplicación, por las dudas. – Quassnoi

+0

Entonces, básicamente, estás diciendo que no es factible en una sola llamada. Esperaba que alguna forma de INSERT INTO _tmp SELECT ... fuera lo suficientemente importante para permitir una llamada a mysql_store_results() ya que permite a mysql_affected_rows() –

1

Si usted está buscando una sola consulta, entonces no puede suceder. La función ACTUALIZAR devuelve específicamente solo la cantidad de elementos que se actualizaron. Del mismo modo, la función SELECCIONAR no altera una tabla, solo devuelve valores.

Usando un procedimiento de hecho se convierten en una sola función y puede ser útil si el cierre es una preocupación para usted. Si su mayor preocupación es el tráfico de red (es decir, pasar demasiadas consultas), utilice el procedimiento. Si la preocupación es la sobrecarga del servidor (es decir: el PP está trabajando demasiado duro), entonces la sobrecarga adicional de un procedimiento podría empeorar las cosas.

+0

La preocupación es, de hecho, la sobrecarga del servidor. Mi esperanza era que, dado que la actualización ya 'seek() ed' a la fila, un SELECT sería más fácil para el servidor dentro de la misma declaración. –

+2

También estoy buscando un SELECT/UPDATE único y encontré esta pregunta. No obstante, no quiero que se bloquee, pero para modificar la columna LastAccessed en la fila NOW() cuando se recupera la fila – CashCow

+1

Hola Paulo, ¿puedes explicar (o dar un enlace) por qué los procedimientos harían las cosas más lentas para DB? Pensé que SP es más rápido: S – confiq

8

tratan como esto

UPDATE `lastid` SET `idnum` = (SELECT `id` FROM `history` ORDER BY `id` DESC LIMIT 1); 

por encima de código que funcionó para mí

+0

Funcionó para mí también. Yo prefiero este método – Darius

+0

¡¡¡Gracias !!!!!!!!! –

+0

¿Es esta condición de carrera segura? –

0
UPDATE tasks 
SET guid = %d, params = @params := params 
WHERE guid = 0 LIMIT 1; 

Se devolverá 1 o 0, dependiendo de si los valores se han cambiado de manera efectiva.

SELECT @params AS params; 

Este solo selecciona la variable de la conexión.

Desde: here

1

que tienen el mismo problema exacto. Terminamos con PostreSQL lugar, y UPDATE ... RETURNING:

La cláusula opcional DEVOLUCIÓN causa UPDATE para computar y el valor (s) de retorno sobre la base de cada fila en realidad actualiza. Se puede calcular cualquier expresión que use las columnas de la tabla y/o columnas de otras tablas mencionadas en FROM. Se utilizan los valores nuevos (posteriores a la actualización) de las columnas de la tabla. La sintaxis de la lista de RETORNO es idéntica a la de la lista de salida de SELECCIONAR.

Ejemplo: UPDATE 'my_table' SET 'status' = 1 WHERE 'status' = 0 LIMIT 1 RETURNING *;

O, en su caso: UPDATE 'tasks' SET 'guid' = %d WHERE 'guid' = 0 LIMIT 1 RETURNING 'params';

siento, sé que esto no responde a la pregunta con MySQL, y puede que no sea fácil simplemente cambiar a PostgreSQL, pero es la mejor manera que hemos encontrado para hacerlo. Incluso 6 años después, MySQL aún no es compatible con UPDATE ... RETURNING. Se podría agregar en algún momento en el futuro, pero por ahora MariaDB only has it for DELETE statements.

Edición: No es una tarea (prioridad baja) a add UPDATE ... RETURNING support to MariaDB.

Cuestiones relacionadas