2010-08-30 11 views
13

Me interesa saber si una consulta select for update bloqueará una fila que no existe."seleccionar para la actualización" impide que otras conexiones se inserten cuando la fila no está presente

p. Ej.

Tabla FooBar con dos columnas, foo y bar, foo tiene un índice único

  • consulta Edición select bar from FooBar where foo = ? for update
  • Si consulta devuelve cero filas
    • consulta Edición insert into FooBar (foo, bar) values (?, ?)

Ahora es posible e que el inserto provocaría una violación del índice o lo impide el select for update?

Interesado en el comportamiento en SQLServer (2005/8), Oracle y MySQL.

+0

¿el MSSQL incluso apoyar esta sintaxis? –

+0

Tiene "con updlock", que creo que es más o menos lo mismo. –

Respuesta

7

En Oracle, SELECCIONAR ... PARA ACTUALIZAR no tiene efecto en una fila inexistente (la instrucción simplemente genera una excepción Sin datos encontrados). La instrucción INSERT evitará duplicados de valores clave únicos/principales. Cualquier otra transacción que intente insertar los mismos valores de clave se bloqueará hasta que la primera transacción se comprometa (momento en el que la transacción bloqueada recibirá un error de clave duplicado) o se retrotrae (en ese momento la transacción bloqueada continúa).

+0

Repetiría la parte sobre "la declaración simplemente plantea una excepción no encontrada de datos"; un SELECCIONAR ... PARA ACTUALIZAR podría devolver filas y aún no tener efecto en una fila inexistente. ;) –

+0

No en el caso de que el OP indique, donde la selección estaba en foo = explicit_value, siendo foo únicamente. – DCookie

+0

Es posible que la instrucción SELECT devuelva 0 filas. Solo aumenta NO_DATA_FOUND si se invoca con INTO. – jva

0

SQL Server solo tiene el FOR UPDATE como parte de un cursor. Y, solo se aplica a las instrucciones UPDATE que están asociadas con la fila actual en el cursor.

Por lo tanto, el FOR UPDATE no tiene ninguna relación con INSERT. Por lo tanto, creo que su respuesta es que no es aplicable en SQL Server.

Ahora, es posible simular el comportamiento FOR UPDATE con las transacciones y las estrategias de bloqueo. Pero eso puede ser más de lo que estás buscando.

+0

Históricamente, puede haber habido una diferencia en el comportamiento (cuando las actualizaciones bloquearon toda la tabla), pero no en la última década (y muy posiblemente, no desde que se introdujo FOR UPDATE). – JulesLt

2

En Oracle:

Sesión 1

create table t (id number); 
alter table t add constraint pk primary key(id); 

SELECT * 
FROM t 
WHERE id = 1 
FOR UPDATE; 
-- 0 rows returned 
-- this creates row level lock on table, preventing others from locking table in exclusive mode 

Sesión 2

SELECT * 
FROM t 
FOR UPDATE; 
-- 0 rows returned 
-- there are no problems with locking here 

rollback; -- releases lock 


INSERT INTO t 
VALUES (1); 
-- 1 row inserted without problems 
+0

No creo que 'SELECT FOR UPDATE' evite un' INSERT' en ninguna sesión, solo un 'UPDATE'. –

6

MySQL

SELECT ... FOR UPDATE con ACTUALIZACIÓN

El uso de transacciones de InnoDB (confirmación automática desactivada), un SELECT ... FOR UPDATE permite una sesión para bloquear temporalmente un registro en particular (o registros) de manera que ninguna otra sesión puede actualizar eso.Luego, dentro de la misma transacción, la sesión puede realizar un UPDATE en el mismo registro y confirmar o revertir la transacción. Esto le permitiría bloquear el registro para que ninguna otra sesión pueda actualizarlo mientras quizás haga otra lógica comercial.

Esto se logra con el bloqueo. InnoDB utiliza índices para bloquear registros, por lo que bloquear un registro existente parece fácil: simplemente bloquea el índice para ese registro.

SELECT ... FOR UPDATE con INSERT

Sin embargo, para utilizar con SELECT ... FOR UPDATEINSERT, ¿cómo bloquear un índice para un disco que aún no existe? Si está utilizando el nivel de aislamiento predeterminado de REPEATABLE READ, InnoDB también utilizará bloqueos. Siempre que conozca el id (o incluso el rango de identificadores) para bloquear, InnoDB puede bloquear el espacio para que no se pueda insertar ningún otro registro en ese espacio hasta que hayamos terminado.

Si su columna id eran una columna de incremento automático, entonces SELECT ... FOR UPDATE con INSERT INTO sería problemático porque no vas a saber lo que el nuevo id fue hasta insertarla. Sin embargo, como conoce el id que desea insertar, SELECT ... FOR UPDATE con INSERT funcionará.

Claves clínicas

En el nivel de aislamiento por omisión, SELECT ... FOR UPDATE en un registro inexistente hace no bloque de otras transacciones. Entonces, si dos transacciones hacen un SELECT ... FOR UPDATE en el mismo registro de índice inexistente, ambos obtendrán el bloqueo y ninguna de las transacciones podrá actualizar el registro. De hecho, si ambos lo intentan, se detectará un interbloqueo y uno se revertirá.

Por lo tanto, si usted no quiere hacer frente a un callejón sin salida, que sólo podría hacer lo siguiente:

INSERT INTO ...

iniciar una transacción, y realizar la INSERT. Haz tu lógica de negocios y confirma o revierte la transacción. Tan pronto como realice el INSERT en el índice de registro inexistente en la primera transacitón, todas las demás transacciones se bloquearán si intentan INSERT un registro con el mismo índice único. Si la segunda transacción intenta insertar un registro con el mismo índice después de que la primera transacción confirme la inserción, recibirá un error de "clave duplicada". Manejar en consecuencia.

SELECT ... LOCK IN SHARE MODE

Si selecciona con LOCK IN SHARE MODE antes de la INSERT, si una transacción anterior ha insertado ese registro, pero no se ha comprometido todavía, la SELECT ... LOCK IN SHARE MODE bloqueará hasta que la transacción previa ha completado.

lo tanto, para reducir la posibilidad de errores de claves duplicadas, especialmente si mantiene los bloqueos por un tiempo mientras se realiza la lógica de negocio antes de comprometerse ellos o rodar de nuevo:

  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Si devuelve ningún registro, a continuación,
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)
+0

Generalmente cierto, pero SELECCIONAR ... PARA ACTUALIZAR con INSERT sin embargo es inútil en mi humilde opinión, al menos en MySQL. Mire [aquí] (http://stackoverflow.com/a/31184293/3239842) para obtener una explicación. – Binarus

Cuestiones relacionadas