Cette réponse peut s'avérer utile pour la question d'origine, mais vise principalement à corriger des informations inexactes dans d'autres publications. Il met également en évidence une section de non-sens dans BOL.
Et comme indiqué pour la documentation INSERT , il obtiendra un verrou exclusif sur la table. La seule façon d'effectuer un SELECT sur la table est d'utiliser NOLOCK ou de définir le niveau d'isolement de la transaction.
La section liée de BOL indique:
Une instruction INSERT acquiert toujours un verrou exclusif (X) sur la table qu'elle modifie et conserve ce verrou jusqu'à la fin de la transaction. Avec un verrou exclusif (X), aucune autre transaction ne peut modifier les données; les opérations de lecture ne peuvent avoir lieu qu'avec l'utilisation de l'indicateur NOLOCK ou le niveau d'isolement non validé. Pour plus d'informations, voir Verrouillage dans le moteur de base de données .
NB: Depuis le 2014-8-27, BOL a été mis à jour pour supprimer les déclarations incorrectes citées ci-dessus.
Heureusement, ce n'est pas le cas. S'il en était ainsi, les insertions dans une table se produiraient en série et tous les lecteurs seraient bloqués de la table entière jusqu'à la fin de la transaction d'insertion. Cela ferait de SQL Server un serveur de base de données aussi efficace que NTFS. Pas très.
Le bon sens suggère qu'il ne peut en être ainsi, mais comme le souligne Paul Randall, " Rendez-vous service, ne faites confiance à personne ". Si vous ne pouvez faire confiance à personne, y compris BOL , je suppose que nous devrons simplement le prouver.
Créez une base de données et remplissez une table fictive avec un tas de lignes, en notant le DatabaseId renvoyé.
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
Configurez une trace de profileur qui suivra les événements de verrouillage: acquis et de verrouillage: libéré, filtrage sur le DatabaseId à partir du script précédent, définition d'un chemin d'accès au fichier et notant le TraceId renvoyé.
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
Insérez une ligne et arrêtez la trace:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Ouvrez le fichier de trace et vous devriez trouver ce qui suit:
La séquence de verrous pris est:
- Verrouillage exclusif sur MyTable
- Serrure intentionnelle exclusive sur la page 1: 211
- RangeInsert-NullResource sur l'entrée d'index cluster pour la valeur insérée
- Serrure exclusive sur la clé
Les verrous sont ensuite libérés dans l'ordre inverse. À aucun moment un verrou exclusif n'a été acquis sur la table.
Mais ce n'est qu'une insertion par lots! Ce n'est pas la même chose que deux, trois ou des dizaines fonctionnant en parallèle.
Oui, ça l'est. SQL Server (et sans doute tout moteur de base de données relationnelle) ne sait pas quels autres lots peuvent être exécutés lorsqu'il traite une instruction et / ou un lot, de sorte que la séquence d'acquisition des verrous ne varie pas.
Qu'en est-il des niveaux d'isolement plus élevés, par exemple sérialisables?
Pour cet exemple particulier, exactement les mêmes verrous sont pris. Ne me faites pas confiance, essayez-le!