Diese Antwort kann sich als hilfreich für die ursprüngliche Frage erweisen, dient jedoch in erster Linie dazu, ungenaue Informationen in anderen Posts zu behandeln. Es wird auch ein Abschnitt des Unsinns in BOL hervorgehoben.
Und wie in der INSERT- Dokumentation angegeben, erhält es eine exklusive Sperre für den Tisch. Die einzige Möglichkeit, ein SELECT für die Tabelle durchzuführen, besteht darin, NOLOCK zu verwenden oder die Isolationsstufe der Transaktion festzulegen.
Der verknüpfte Abschnitt von BOL lautet:
Eine INSERT-Anweisung erhält immer eine exklusive (X) Sperre für die Tabelle, die sie ändert, und hält diese Sperre, bis die Transaktion abgeschlossen ist. Mit einer exklusiven (X) Sperre können keine anderen Transaktionen Daten ändern. Lesevorgänge können nur unter Verwendung des NOLOCK-Hinweises oder der nicht festgeschriebenen Isolationsstufe erfolgen. Weitere Informationen finden Sie unter Sperren im Datenbankmodul .
NB: Ab dem 27.08.2014 wurde BOL aktualisiert, um die oben angegebenen falschen Aussagen zu entfernen.
Zum Glück ist dies nicht der Fall. Wenn dies der Fall wäre, würden Einfügungen in eine Tabelle seriell erfolgen und alle Leser würden für die gesamte Tabelle blockiert, bis die Einfügetransaktion abgeschlossen ist. Das würde SQL Server als Datenbankserver genauso effizient machen wie NTFS. Nicht sehr.
Der gesunde Menschenverstand legt nahe, dass dies nicht möglich ist, aber wie Paul Randall betont: " Tun Sie sich selbst einen Gefallen, vertrauen Sie niemandem ". Wenn Sie niemandem vertrauen können, einschließlich BOL , müssen wir es wohl nur beweisen.
Erstellen Sie eine Datenbank und füllen Sie eine Dummy-Tabelle mit einer Reihe von Zeilen, wobei Sie die zurückgegebene DatabaseId notieren.
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
Richten Sie einen Profiler-Trace ein, der die Ereignisse sperren: erfasst und gesperrt: freigegeben verfolgt, nach der Datenbank-ID aus dem vorherigen Skript filtert, einen Pfad für die Datei festlegt und die zurückgegebene Trace-ID notiert.
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
Fügen Sie eine Zeile ein und stoppen Sie die Ablaufverfolgung:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Öffnen Sie die Trace-Datei und Sie sollten Folgendes finden:
Die Reihenfolge der gesperrten Sperren ist:
- Absichts-exklusive Sperre für MyTable
- Absichts-Exklusiv-Sperre auf Seite 1: 211
- RangeInsert-NullResource für den Clustered-Indexeintrag für den einzufügenden Wert
- Exklusives Schlüsselschloss
Die Schlösser werden dann in umgekehrter Reihenfolge freigegeben. Zu keinem Zeitpunkt wurde eine exklusive Sperre für den Tisch erworben.
Dies ist jedoch nur eine Chargeneinfügung! Das ist nicht dasselbe wie zwei, drei oder Dutzende, die parallel laufen.
Ja ist es. SQL Server (und möglicherweise jedes relationale Datenbankmodul) hat keine Voraussicht darüber, welche anderen Stapel möglicherweise ausgeführt werden, wenn eine Anweisung und / oder ein Stapel verarbeitet werden, sodass die Reihenfolge der Sperrenerfassung nicht variiert.
Was ist mit höheren Isolationsstufen, z. B. serialisierbar?
Für dieses spezielle Beispiel werden genau die gleichen Sperren verwendet. Vertrau mir nicht, versuch es!