2009-06-03 13 views
5

Deseo implementar SO como etiquetas en el poste de información. Tengo una tabla llamada tag_data con columnas tagId, title, count. Tengo una tabla separada que vincula la relación entre una publicación y las muchas etiquetas que puede usar.Actualice un recuento (campo) de forma segura en SQL

Aquí está el problema, cómo obtengo el recuento actual, lo aumento o disminuyo en uno y lo almacena con SEGURIDAD. ¿Entonces ninguna otra conexión/hilo lo actualizará entre el momento en que selecciono y actualizo?

+0

¿Necesita proteger también la inserción de una nueva etiqueta? Eso generalmente complica las cosas * bastante * un poco. –

+0

update tag_data set count = count + @incrVal donde tagId = @tagId – spencer7593

Respuesta

13

Supongo que también quiere el nuevo recuento, de lo contrario, esto es obvio, simplemente actualice el recuento de conjuntos = recuento + 1.

Si su cláusula de salida de apoyo db en UPDATE (por ejemplo 2K5 SQL Server o 2K8.):

UPDATE table 
    SET count = count + 1 
    OUTPUT inserted.count 
    WHERE [email protected]; 

lo contrario:

begin transaction 
update table 
    set counter=counter+1 
    where [email protected]; 
select counter 
    from table 
    where [email protected]; 
commit; 
+2

Buena respuesta. Estoy confundido acerca de dos cosas. Parece que update/select es una declaración, entonces ¿por qué necesita una transacción? El segundo es a juzgar por la respuesta de los otros 2, parece que la transacción bloqueará la base de datos hasta que esté terminada, entonces ¿por qué MikeyB responde mal? Leí tu respuesta, pero ¿qué ocurre ?, ¿otros hilos pueden leer pero no escribir durante la transacción? –

+3

El bloqueo X adquirido por la actualización es help * durante la transacción * y evita que cualquier otra persona lea o actualice el contador hasta que se comprometa. Esto le proporciona un punto de guardado para hacer su selección sabiendo que recuperará el valor real que actualizó. En el caso de Mikey, la cerradura S adquirida por el seleccionador es * not * help después de que la declaración se complete (en condiciones normales), permitiendo así que un segundo hilo entre y actualice el valor para cuando se ejecute su actualización, y usted sobrescribir esa actualización. De ahí la 'actualización perdida'. –

+0

buena respuesta. @Remus: ¿qué hay de establecer el nivel de aislamiento de Transacción en 'serializable'? –

-2

Pseudocódigo:

begin transaction 
A = select count from tag_data where tagId = TagId 
update tag_data set count = A+1 where tagId = TagId 
commit 
end transaction 

recomiendo encarecidamente haciendo un procedimiento almacenado llamado, digamos, increment_tag(TagId) que hace lo anterior :)

+1

Nada impide que un hilo simultáneo lea el mismo recuento, lo incremente y lo actualice. Este es el ejemplo canónico del caso de actualización perdida en el procesamiento de transacciones. Debería proteger la selección inicial con al menos un nivel de aislamiento de lectura repetible (o un bloqueo). –

+0

esta solución (muy mala) también se puede solucionar cambiando la instrucción SELECT en SELECCIONAR ... PARA ACTUALIZAR. Esto dará como resultado la obtención de bloqueo en esta fila. Este bloqueo contendrá otras transacciones que deseen actualizar/eliminar u otro seleccionar para la actualización en esa fila, hasta que esta transacción se comprometa/revierte. Recuerda que debes asegurarte de que cada parte del código que modifica este contador también usa seleccionar para actualizar o usa la regla "actualizar primero y luego seleccionar"; de lo contrario, todo esto es inútil. –

0

Conde SET = count + 1 es mi humilde opinión, la solución más fácil ..

De manera más general el concepto de ser capaz de obtener los datos, procesarlos y mientras que su demanda de ser procesado no haya cambios subyacentes antes de escribir los resultados de la el procesamiento generalmente no es razonable si también se requiere un sistema escalable.

Por supuesto, puede hacer esto y en muchos entornos salirse con la suya .. Sin embargo, estos enfoques pondrán límites severos a la escalabilidad y complejidad de una aplicación antes de que problemas de simultaneidad inutilicen el sistema.

En mi humilde opinión, el mejor enfoque es tomar una ruta optimista y detectar/reintentar si en el caso de falta de uso, algo que le preocupa cambió.

SELECT Count tan antigua ... DE ...

.. procesamiento ...

ACTUALIZACIÓN ... SET Count = oldplus1 el Conde de edad = Y ...

A menos ACTUALIZAR le da el recuento de filas que espera que suponga que los datos se modificaron y vuelva a intentarlo hasta que tenga éxito.

Cuestiones relacionadas