Esta respuesta puede resultar útil para la pregunta original, pero es principalmente para abordar información inexacta en otras publicaciones. También destaca una sección de tonterías en BOL.
Y como se indica para la documentación de INSERT , adquirirá un bloqueo exclusivo sobre la mesa. La única forma en que se puede hacer un SELECT contra la tabla es usar NOLOCK o establecer el nivel de aislamiento de la transacción.
La sección vinculada de BOL establece:
Una instrucción INSERT siempre adquiere un bloqueo exclusivo (X) en la tabla que modifica y mantiene ese bloqueo hasta que se complete la transacción. Con un bloqueo exclusivo (X), ninguna otra transacción puede modificar datos; las operaciones de lectura solo pueden realizarse con el uso de la sugerencia NOLOCK o el nivel de aislamiento de lectura no comprometida. Para obtener más información, consulte Bloqueo en el motor de base de datos .
NB: A partir de 2014-8-27, BOL se ha actualizado para eliminar las declaraciones incorrectas citadas anteriormente.
Afortunadamente este no es el caso. Si fuera así, las inserciones en una tabla ocurrirían en serie y todos los lectores quedarían bloqueados de toda la tabla hasta que se complete la transacción de inserción. Eso haría que SQL Server sea un servidor de base de datos tan eficiente como NTFS. No muy.
El sentido común sugiere que no puede ser así, pero como señala Paul Randall, " Hazte un favor, no confíes en nadie ". Si no puede confiar en nadie, incluido BOL , supongo que tendremos que demostrarlo.
Cree una base de datos y complete una tabla ficticia con un montón de filas, observando que se devuelve el DatabaseId.
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
USE [master]
GO
IF EXISTS (SELECT name FROM sys.databases WHERE name = N'LockDemo')
DROP DATABASE [LockDemo]
GO
DECLARE @DataFilePath NVARCHAR(4000)
SELECT
@DataFilePath = SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
FROM
master.sys.master_files
WHERE
database_id = 1 AND file_id = 1
EXEC ('
CREATE DATABASE [LockDemo] ON PRIMARY
( NAME = N''LockDemo'', FILENAME = N''' + @DataFilePath + N'LockDemo.mdf' + ''', SIZE = 2MB , MAXSIZE = UNLIMITED, FILEGROWTH = 2MB )
LOG ON
( NAME = N''LockDemo_log'', FILENAME = N''' + @DataFilePath + N'LockDemo_log.ldf' + ''', SIZE = 1MB , MAXSIZE = UNLIMITED , FILEGROWTH = 1MB )
')
GO
USE [LockDemo]
GO
SELECT DB_ID() AS DatabaseId
CREATE TABLE [dbo].[MyTable]
(
[id] [int] IDENTITY(1,1) PRIMARY KEY CLUSTERED
, [filler] CHAR(4030) NOT NULL DEFAULT REPLICATE('A', 4030)
)
GO
INSERT MyTable DEFAULT VALUES;
GO 100
Configure un seguimiento del generador de perfiles que rastreará los eventos de bloqueo: adquiridos y bloqueados: liberados, filtrando en el DatabaseId del script anterior, configurando una ruta para el archivo y observando el TraceId devuelto.
declare @rc int
declare @TraceID int
declare @maxfilesize BIGINT
declare @databaseid INT
DECLARE @tracefile NVARCHAR(4000)
set @maxfilesize = 5
SET @tracefile = N'D:\Temp\LockTrace'
SET @databaseid = 9
exec @rc = sp_trace_create @TraceID output, 0, @tracefile, @maxfilesize, NULL
if (@rc != 0) goto error
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 24, 32, @on
exec sp_trace_setevent @TraceID, 24, 1, @on
exec sp_trace_setevent @TraceID, 24, 57, @on
exec sp_trace_setevent @TraceID, 24, 3, @on
exec sp_trace_setevent @TraceID, 24, 51, @on
exec sp_trace_setevent @TraceID, 24, 12, @on
exec sp_trace_setevent @TraceID, 60, 32, @on
exec sp_trace_setevent @TraceID, 60, 57, @on
exec sp_trace_setevent @TraceID, 60, 3, @on
exec sp_trace_setevent @TraceID, 60, 51, @on
exec sp_trace_setevent @TraceID, 60, 12, @on
exec sp_trace_setevent @TraceID, 23, 32, @on
exec sp_trace_setevent @TraceID, 23, 1, @on
exec sp_trace_setevent @TraceID, 23, 57, @on
exec sp_trace_setevent @TraceID, 23, 3, @on
exec sp_trace_setevent @TraceID, 23, 51, @on
exec sp_trace_setevent @TraceID, 23, 12, @on
-- DatabaseId filter
exec sp_trace_setfilter @TraceID, 3, 0, 0, @databaseid
-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1
-- display trace id for future references
select TraceID=@TraceID
goto finish
error:
select ErrorCode=@rc
finish:
go
Inserte una fila y detenga la traza:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Abra el archivo de rastreo y debería encontrar lo siguiente:
La secuencia de bloqueos tomados es:
- Bloqueo de intención exclusiva en MyTable
- Bloqueo de intención exclusiva en la página 1: 211
- RangeInsert-NullResource en la entrada de índice agrupado para el valor que se inserta
- Cerradura exclusiva en la llave
Los bloqueos se liberan en orden inverso. En ningún momento se ha adquirido un candado exclusivo sobre la mesa.
¡Pero esto es solo un lote de inserción! Eso no es lo mismo que dos, tres o docenas corriendo en paralelo.
Sí lo es. SQL Server (y posiblemente cualquier motor de base de datos relacional) no tiene previsión sobre qué otros lotes pueden estar ejecutándose cuando procesa una declaración y / o lote, por lo que la secuencia de adquisición de bloqueo no varía.
¿Qué pasa con los niveles de aislamiento más altos, por ejemplo, serializable?
Para este ejemplo particular, se toman exactamente los mismos bloqueos. ¡No confíes en mí, pruébalo!