it-swarm.com.de

Warum ist die Verwendung einer Tabellenvariablen in diesem speziellen Fall mehr als doppelt so schnell wie eine # temp-Tabelle?

Ich habe mir den Artikel hier angesehen Temporäre Tabellen im Vergleich zu Tabellenvariablen und ihre Auswirkungen auf die SQL Server-Leistung und konnte in SQL Server 2008 ähnliche Ergebnisse wie die dort für 2005 gezeigten reproduzieren.

Wenn die gespeicherten Prozeduren (Definitionen unten) mit nur 10 Zeilen ausgeführt werden, führt die Tabellenvariablenversion out die temporäre Tabellenversion mehr als zweimal aus.

Ich habe den Prozedur-Cache geleert und beide gespeicherten Prozeduren 10.000 Mal ausgeführt und dann den Vorgang für weitere 4 Läufe wiederholt. Ergebnisse unten (Zeit in ms pro Charge)

T2_Time     V2_Time
----------- -----------
8578        2718      
6641        2781    
6469        2813   
6766        2797
6156        2719

Meine Frage ist: Was ist der Grund für die bessere Leistung der Tabellenvariablenversion?

Ich habe Nachforschungen angestellt. z.B. Betrachten Sie die Leistungsindikatoren mit

SELECT cntr_value
from sys.dm_os_performance_counters
where counter_name = 'Temp Tables Creation Rate';

bestätigt, dass in beiden Fällen die temporären Objekte nach dem ersten Durchlauf zwischengespeichert werden wie erwartet anstatt bei jedem Aufruf erneut von Grund auf neu erstellt zu werden.

In ähnlicher Weise die Auto Stats, SP:Recompile, SQL:StmtRecompileevents in Profiler (Screenshot unten) zeigt, dass diese Ereignisse nur einmal auftreten (beim ersten Aufruf von #temp table gespeicherte Prozedur) und die anderen 9.999 Ausführungen lösen keines dieser Ereignisse aus. (Die Tabellenvariablenversion erhält keines dieser Ereignisse.)

Trace

Der etwas höhere Overhead des ersten Durchlaufs der gespeicherten Prozedur kann jedoch in keiner Weise den großen Gesamtunterschied erklären, da es immer noch nur wenige ms dauert, den Prozedurcache zu leeren und beide Prozeduren einmal auszuführen, sodass ich weder Statistiken noch Statistiken glaube Neukompilierungen können die Ursache sein.

Erforderliche Datenbankobjekte erstellen

CREATE DATABASE TESTDB_18Feb2012;

GO

USE TESTDB_18Feb2012;

CREATE TABLE NUM 
  ( 
     n INT PRIMARY KEY, 
     s VARCHAR(128) 
  ); 

WITH NUMS(N) 
     AS (SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY $/0) 
         FROM   master..spt_values v1, 
                master..spt_values v2) 
INSERT INTO NUM 
SELECT N, 
       'Value: ' + CONVERT(VARCHAR, N) 
FROM   NUMS 

GO

CREATE PROCEDURE [dbo].[T2] @total INT 
AS 
  CREATE TABLE #T 
    ( 
       n INT PRIMARY KEY, 
       s VARCHAR(128) 
    ) 

  INSERT INTO #T 
  SELECT n, 
         s 
  FROM   NUM 
  WHERE  n%100 > 0 
         AND n <= @total 

  DECLARE @res VARCHAR(128) 

  SELECT @res = MAX(s) 
  FROM   NUM 
  WHERE  n <= @total 
         AND NOT EXISTS(SELECT * 
                        FROM   #T 
                        WHERE  #T.n = NUM.n) 
GO

CREATE PROCEDURE [dbo].[V2] @total INT 
AS 
  DECLARE @V TABLE ( 
    n INT PRIMARY KEY, 
    s VARCHAR(128)) 

  INSERT INTO @V 
  SELECT n, 
         s 
  FROM   NUM 
  WHERE  n%100 > 0 
         AND n <= @total 

  DECLARE @res VARCHAR(128) 

  SELECT @res = MAX(s) 
  FROM   NUM 
  WHERE  n <= @total 
         AND NOT EXISTS(SELECT * 
                        FROM   @V V 
                        WHERE  V.n = NUM.n) 


GO

Testskript

SET NOCOUNT ON;

DECLARE @T1 DATETIME2,
        @T2 DATETIME2,
        @T3 DATETIME2,  
        @Counter INT = 0

SET @T1 = SYSDATETIME()

WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.T2 10
SET @Counter += 1
END

SET @T2 = SYSDATETIME()
SET @Counter = 0

WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.V2 10
SET @Counter += 1
END

SET @T3 = SYSDATETIME()

SELECT DATEDIFF(MILLISECOND,@T1,@T2) AS T2_Time,
       DATEDIFF(MILLISECOND,@T2,@T3) AS V2_Time
37
Martin Smith

Die Ausgabe von SET STATISTICS IO ON Für beide sieht ähnlich aus

SET STATISTICS IO ON;
PRINT 'V2'
EXEC dbo.V2 10
PRINT 'T2'
EXEC dbo.T2 10

Gibt

V2
Table '#58B62A60'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

Table '#58B62A60'. Scan count 10, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

T2
Table '#T__ ... __00000000E2FE'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

Table '#T__ ... __00000000E2FE'. Scan count 0, logical reads 20
Table 'NUM'. Scan count 1, logical reads 3

Und wie Aaron in den Kommentaren hervorhebt, ist der Plan für die Tabellenvariablenversion tatsächlich weniger effizient, da beide einen verschachtelten Schleifenplan haben, der von einer Indexsuche für dbo.NUM Gesteuert wird. Die Tabellenversion #temp Führt eine aus Suchen Sie in den Index für [#T].n = [dbo].[NUM].[n] mit dem Restprädikat [#T].[n]<=[@total], während die Tabellenvariablenversion eine Indexsuche für @V.n <= [@total] mit dem Restprädikat @V.[n]=[dbo].[NUM].[n] durchführt und so mehr Zeilen verarbeitet (weshalb dieser Plan für eine größere Anzahl von Zeilen so schlecht funktioniert)

Wenn Sie Extended Events verwenden, um die Wartetypen für die spezifische Spid zu betrachten, erhalten Sie diese Ergebnisse für 10.000 Ausführungen von EXEC dbo.T2 10

+---------------------+------------+----------------+----------------+----------------+
|                     |            |     Total      | Total Resource |  Total Signal  |
| Wait Type           | Wait Count | Wait Time (ms) | Wait Time (ms) | Wait Time (ms) |
+---------------------+------------+----------------+----------------+----------------+
| SOS_SCHEDULER_YIELD | 16         | 19             | 19             | 0              |
| PAGELATCH_SH        | 39998      | 14             | 0              | 14             |
| PAGELATCH_EX        | 1          | 0              | 0              | 0              |
+---------------------+------------+----------------+----------------+----------------+

und diese Ergebnisse für 10.000 Ausführungen von EXEC dbo.V2 10

+---------------------+------------+----------------+----------------+----------------+
|                     |            |     Total      | Total Resource |  Total Signal  |
| Wait Type           | Wait Count | Wait Time (ms) | Wait Time (ms) | Wait Time (ms) |
+---------------------+------------+----------------+----------------+----------------+
| PAGELATCH_EX        | 2          | 0              | 0              | 0              |
| PAGELATCH_SH        | 1          | 0              | 0              | 0              |
| SOS_SCHEDULER_YIELD | 676        | 0              | 0              | 0              |
+---------------------+------------+----------------+----------------+----------------+

Es ist also klar, dass die Anzahl der PAGELATCH_SH - Wartezeiten im Fall der #temp - Tabelle viel höher ist. Mir ist keine Möglichkeit bekannt, die Warte-Ressource zum erweiterten Ereignis-Trace hinzuzufügen, um dies weiter zu untersuchen

WHILE 1=1
EXEC dbo.T2 10

Während in einer anderen Verbindung Polling sys.dm_os_waiting_tasks

CREATE TABLE #T(resource_description NVARCHAR(2048))

WHILE 1=1
INSERT INTO #T
SELECT resource_description
FROM sys.dm_os_waiting_tasks
WHERE session_id=<spid_of_other_session> and wait_type='PAGELATCH_SH'

Nachdem das Gerät etwa 15 Sekunden lang laufen gelassen worden war, wurden die folgenden Ergebnisse erzielt

+-------+----------------------+
| Count | resource_description |
+-------+----------------------+
|  1098 | 2:1:150              |
|  1689 | 2:1:146              |
+-------+----------------------+

Diese beiden Seiten, die zwischengespeichert werden, gehören zu (verschiedenen) nicht gruppierten Indizes in der Basistabelle tempdb.sys.sysschobjs Mit den Namen 'nc1' Und 'nc2'.

Das Abfragen von tempdb.sys.fn_dblog Während der Läufe zeigt an, dass die Anzahl der Protokolldatensätze, die bei der ersten Ausführung jeder gespeicherten Prozedur hinzugefügt wurden, etwas variabel war, aber bei nachfolgenden Ausführungen war die Anzahl, die bei jeder Iteration hinzugefügt wurde, sehr konsistent und vorhersehbar. Sobald die Prozedurpläne zwischengespeichert sind, beträgt die Anzahl der Protokolleinträge etwa die Hälfte der für die Version #temp Erforderlichen.

+-----------------+----------------+------------+
|                 | Table Variable | Temp Table |
+-----------------+----------------+------------+
| First Run       |            126 | 72 or 136  |
| Subsequent Runs |             17 | 32         |
+-----------------+----------------+------------+

Wenn Sie die Transaktionsprotokolleinträge für die Tabellenversion #temp Der SP] bei jedem nachfolgenden Aufruf der gespeicherten Prozedur genauer betrachten, werden drei Transaktionen und die Tabellenvariable eins nur zwei erstellt.

+---------------------------------+----+---------------------------------+----+
|           #Temp Table                |         @Table Variable              |
+---------------------------------+----+---------------------------------+----+
| CREATE TABLE                    |  9 |                                 |    |
| INSERT                          | 12 | TVQuery                         | 12 |
| FCheckAndCleanupCachedTempTable | 11 | FCheckAndCleanupCachedTempTable |  5 |
+---------------------------------+----+---------------------------------+----+

Die Transaktionen INSERT/TVQUERY sind bis auf den Namen identisch. Dies enthält die Protokollsätze für jede der 10 Zeilen, die in die temporäre Tabelle oder Tabellenvariable eingefügt wurden, sowie die Einträge LOP_BEGIN_XACT/LOP_COMMIT_XACT.

Die Transaktion CREATE TABLE Wird nur in der Version #Temp Angezeigt und sieht wie folgt aus.

+-----------------+-------------------+---------------------+
|    Operation    |      Context      |    AllocUnitName    |
+-----------------+-------------------+---------------------+
| LOP_BEGIN_XACT  | LCX_NULL          |                     |
| LOP_SHRINK_NOOP | LCX_NULL          |                     |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc1  |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc1  |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc2  |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc2  |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst |
| LOP_COMMIT_XACT | LCX_NULL          |                     |
+-----------------+-------------------+---------------------+

Die Transaktion FCheckAndCleanupCachedTempTable wird in beiden Fällen angezeigt, enthält jedoch 6 zusätzliche Einträge in der Version #temp. Dies sind die 6 Zeilen, die sich auf sys.sysschobjs Beziehen und genau das gleiche Muster wie oben haben.

+-----------------+-------------------+----------------------------------------------+
|    Operation    |      Context      |                AllocUnitName                 |
+-----------------+-------------------+----------------------------------------------+
| LOP_BEGIN_XACT  | LCX_NULL          |                                              |
| LOP_DELETE_ROWS | LCX_NONSYS_SPLIT  | dbo.#7240F239.PK__#T________3BD0199374293AAB |
| LOP_HOBT_DELTA  | LCX_NULL          |                                              |
| LOP_HOBT_DELTA  | LCX_NULL          |                                              |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst                          |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc1                           |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc1                           |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | sys.sysschobjs.nc2                           |
| LOP_INSERT_ROWS | LCX_INDEX_LEAF    | sys.sysschobjs.nc2                           |
| LOP_MODIFY_ROW  | LCX_CLUSTERED     | sys.sysschobjs.clst                          |
| LOP_COMMIT_XACT | LCX_NULL          |                                              |
+-----------------+-------------------+----------------------------------------------+

Wenn Sie diese 6 Zeilen in beiden Transaktionen betrachten, entsprechen sie denselben Operationen. Das erste LOP_MODIFY_ROW, LCX_CLUSTERED Ist eine Aktualisierung der Spalte modify_date In sys.objects. Die verbleibenden fünf Zeilen befassen sich alle mit der Umbenennung von Objekten. Da name eine Schlüsselspalte der beiden betroffenen NCIs ist (nc1 Und nc2), Wird dies als Löschen/Einfügen für diejenigen ausgeführt, und dann geht es zurück zum Clustered-Index und aktualisiert das auch.

Es scheint, dass für die Tabellenversion #temp Wenn die gespeicherte Prozedur endet, besteht ein Teil der von der Transaktion FCheckAndCleanupCachedTempTable durchgeführten Bereinigung darin, die temporäre Tabelle von #T__________________________________________________________________________________________________________________00000000E316 In umzubenennen Ein anderer interner Name wie #2F4A0079 und wenn er eingegeben wird, benennt die Transaktion CREATE TABLE ihn zurück. Dieser Flip-Flop-Name kann in einer Verbindung gesehen werden, die dbo.T2 In einer Schleife ausführt, während in einer anderen

WHILE 1=1
SELECT name, object_id, create_date, modify_date
FROM tempdb.sys.objects 
WHERE name LIKE '#%'

Beispielergebnisse

Screenshot

Eine mögliche Erklärung für den beobachteten Leistungsunterschied, auf den Alex anspielt, ist, dass diese zusätzliche Arbeit, die die Systemtabellen in tempdb verwaltet, dafür verantwortlich ist.


Wenn Sie beide Prozeduren in einer Schleife ausführen, zeigt der Visual Studio Code-Profiler Folgendes an

+-------------------------------+--------------------+-------+-----------+
|           Function            |    Explanation     | Temp  | Table Var |
+-------------------------------+--------------------+-------+-----------+
| CXStmtDML::XretExecute        | Insert ... Select  | 16.93 | 37.31     |
| CXStmtQuery::ErsqExecuteQuery | Select Max         | 8.77  | 23.19     |
+-------------------------------+--------------------+-------+-----------+
| Total                         |                    | 25.7  | 60.5      |
+-------------------------------+--------------------+-------+-----------+

Die Tabellenvariablenversion verbringt etwa 60% der Zeit mit der Ausführung der Einfügeanweisung und der nachfolgenden Auswahl, während die temporäre Tabelle weniger als die Hälfte davon beträgt. Dies steht im Einklang mit den im OP gezeigten Zeitabläufen und der obigen Schlussfolgerung, dass der Leistungsunterschied auf die Zeit zurückzuführen ist, die für die Ausführung von Nebenarbeiten aufgewendet wurde, nicht auf die Zeit, die für die Ausführung der Abfrage selbst aufgewendet wurde.

Die wichtigsten Funktionen, die zu den "fehlenden" 75% in der temporären Tabellenversion beitragen, sind

+------------------------------------+-------------------+
|              Function              | Inclusive Samples |
+------------------------------------+-------------------+
| CXStmtCreateTableDDL::XretExecute  | 26.26%            |
| CXStmtDDL::FinishNormalImp         | 4.17%             |
| TmpObject::Release                 | 27.77%            |
+------------------------------------+-------------------+
| Total                              | 58.20%            |
+------------------------------------+-------------------+

Sowohl unter der Erstellungs- als auch unter der Freigabefunktion wird die Funktion CMEDProxyObject::SetName Mit einem inklusive Beispielwert von 19.6% Angezeigt. Daraus schließe ich, dass 39,2% der Zeit im Fall der temporären Tabelle mit der zuvor beschriebenen Umbenennung belegt sind.

Und die größten in der Tabellenvariablenversion tragen zu den anderen 40% bei

+-----------------------------------+-------------------+
|             Function              | Inclusive Samples |
+-----------------------------------+-------------------+
| CTableCreate::LCreate             | 7.41%             |
| TmpObject::Release                | 12.87%            |
+-----------------------------------+-------------------+
| Total                             | 20.28%            |
+-----------------------------------+-------------------+

Temporäres Tabellenprofil

(enter image description here

Tabellenvariablenprofil

(enter image description here

31
Martin Smith

Disco Inferno

Da dies eine ältere Frage ist, habe ich beschlossen, das Problem in neueren Versionen von SQL Server erneut zu untersuchen, um festzustellen, ob das gleiche Leistungsprofil noch vorhanden ist oder ob sich die Merkmale überhaupt geändert haben.

Insbesondere das Hinzufügen von In-Memory-Systemtabellen für SQL Server 2019 scheint eine lohnende Gelegenheit zum erneuten Testen zu sein.

Ich verwende ein etwas anderes Testgeschirr, da ich bei der Arbeit an etwas anderem auf dieses Problem gestoßen bin.

Test, Test

Mit dem 2013-Version von Stack Overflow habe ich diesen Index und diese beiden Verfahren:

Index:

CREATE INDEX ix_whatever 
    ON dbo.Posts(OwnerUserId) INCLUDE(Score);
GO

Temp Tabelle:

    CREATE OR ALTER PROCEDURE dbo.TempTableTest(@Id INT)
    AS
    BEGIN
    SET NOCOUNT ON;

        CREATE TABLE #t(i INT NOT NULL);
        DECLARE @i INT;

        INSERT #t ( i )
        SELECT p.Score
        FROM dbo.Posts AS p
        WHERE p.OwnerUserId = @Id;

        SELECT @i = AVG(t.i)
        FROM #t AS t;

    END;
    GO 

Tabellenvariable:

    CREATE OR ALTER PROCEDURE dbo.TableVariableTest(@Id INT)
    AS
    BEGIN
    SET NOCOUNT ON;

        DECLARE @t TABLE (i INT NOT NULL);
        DECLARE @i INT;

        INSERT @t ( i )
        SELECT p.Score
        FROM dbo.Posts AS p
        WHERE p.OwnerUserId = @Id;

        SELECT @i = AVG(t.i)
        FROM @t AS t;

    END;
    GO 

Um potenzielle ASYNC_NETWORK_IO wartet zu verhindern, verwende ich Wrapper-Prozeduren.

CREATE PROCEDURE #TT AS
SET NOCOUNT ON;
    DECLARE @i INT = 1;
    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();

    WHILE @i <= 50000
        BEGIN
            EXEC dbo.TempTableTest @Id = @i;
            SET @i += 1;
        END;
    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];
GO

CREATE PROCEDURE #TV AS
SET NOCOUNT ON;
    DECLARE @i INT = 1;
    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();

    WHILE @i <= 50000
        BEGIN
            EXEC dbo.TableVariableTest @Id = @i;
            SET @i += 1;
        END;
    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];
GO

SQL Server 2017

Da 2014 und 2016 zu diesem Zeitpunkt im Grunde genommen RELICS sind, beginne ich meine Tests mit 2017. Der Kürze halber springe ich direkt zum Profilieren des Codes mit Perfview . Im wirklichen Leben habe ich mir Wartezeiten, Riegel, Spinlocks, verrückte Spurenfahnen und andere Dinge angesehen.

Das Profilieren des Codes ist das einzige, was etwas Interessantes enthüllte.

Zeitunterschied:

  • Temp Tabelle: 17891 ms
  • Tabellenvariable: 5891 ms

Immer noch ein sehr deutlicher Unterschied, oder? Aber was schlägt SQL Server jetzt?

(NUTS

Wenn wir uns die beiden höchsten Erhöhungen in den unterschiedlichen Stichproben ansehen, sehen wir, dass sqlmin und sqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket Die beiden größten Straftäter sind.

(NUTS

Nach den Namen in den Aufrufstapeln zu urteilen, scheint das Bereinigen und interne Umbenennen von temporären Tabellen die größte Zeit zu sein, die der temporäre Tabellenaufruf im Vergleich zum Tabellenvariablenaufruf kostet.

Obwohl Tabellenvariablen intern von temporären Tabellen unterstützt werden, scheint dies kein Problem zu sein.

SET STATISTICS IO ON;
DECLARE @t TABLE(id INT);
SELECT * FROM @t AS t;

Tabelle '# B98CE339'. Scananzahl 1

Wenn Sie in den Aufrufstapeln nach dem Tabellenvariablentest suchen, wird keiner der Haupttäter angezeigt:

(NUTS

SQL Server 2019 (Vanilla)

Okay, dies ist also immer noch ein Problem in SQL Server 2017. Ist 2019 etwas anderes sofort einsatzbereit?

Erstens, um zu zeigen, dass nichts in meinem Ärmel ist:

SELECT c.name,
       c.value_in_use,
       c.description
FROM sys.configurations AS c
WHERE c.name = 'tempdb metadata memory-optimized';

(NUTS

Zeitunterschied:

  • Temp-Tabelle: 15765 ms
  • Tabellenvariable: 7250 ms

Beide Verfahren waren unterschiedlich. Der temporäre Tabellenaufruf war einige Sekunden schneller und der Aufruf der Tabellenvariablen war etwa 1,5 Sekunden langsamer. Die Verlangsamung der Tabellenvariablen kann teilweise durch verzögerte Kompilierung der Tabellenvariablen erklärt werden, eine neue Optimierungsoption im Jahr 2019.

Wenn man sich den Unterschied in Perfview ansieht, hat er sich ein wenig geändert - sqlmin ist nicht mehr da - aber sqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket Ist es.

(NUTS

SQL Server 2019 (In-Memory Tempdb-Systemtabellen)

Was ist mit dieser neuen Sache in der Speichersystemtabelle? Hm? Sup damit?

Lass es uns einschalten!

EXEC sys.sp_configure @configname = 'advanced', 
                      @configvalue = 1  
RECONFIGURE;

EXEC sys.sp_configure @configname = 'tempdb metadata memory-optimized', 
                      @configvalue = 1 
RECONFIGURE;

Beachten Sie, dass hierfür ein Neustart von SQL Server erforderlich ist. Verzeihen Sie mir, während ich SQL an diesem schönen Freitagnachmittag neu starte.

Jetzt sieht es anders aus:

SELECT c.name,
       c.value_in_use,
       c.description
FROM sys.configurations AS c
WHERE c.name = 'tempdb metadata memory-optimized';

SELECT *, 
       OBJECT_NAME(object_id) AS object_name, 
       @@VERSION AS sql_server_version
FROM tempdb.sys.memory_optimized_tables_internal_attributes;

(NUTS

Zeitunterschied:

  • Temp-Tabelle: 11638 ms
  • Tabellenvariable: 7403 ms

Die temporären Tabellen waren ungefähr 4 Sekunden besser! Das ist etwas.

Ich mag etwas.

Diesmal ist der Perfview Diff nicht sehr interessant. Nebeneinander ist es interessant festzustellen, wie nah die Zeiten auf der ganzen Linie sind:

(NUTS

Ein interessanter Punkt im Unterschied sind die Aufrufe von hkengine!, Die offensichtlich erscheinen können, da jetzt hekatonische Funktionen verwendet werden.

(NUTS

Was die beiden obersten Punkte im Diff angeht, kann ich aus ntoskrnl!? Nicht viel machen:

(NUTS

Oder sqltses!CSqlSortManager_80::GetSortKey, Aber Smrtr Ppl ™ kann sie sich ansehen:

(NUTS

Beachten Sie, dass es ein undokumentiertes und definitiv nicht sicheres Produkt gibt. Verwenden Sie es daher nicht Start-Trace-Flag Sie können zusätzliche temporäre Systemsysteme (sysrowsets, sysallocunits und sysseobjvalues) in das Programm aufnehmen In-Memory-Funktion, aber in diesem Fall hat sich die Ausführungszeit nicht merklich verändert.

Zusammenfassen

Selbst in neueren Versionen von SQL Server sind Hochfrequenzaufrufe an Tabellenvariablen viel schneller als Hochfrequenzaufrufe an temporäre Tabellen.

Obwohl es verlockend ist, Kompilierungen, Neukompilierungen, automatische Statistiken, Latches, Spinlocks, Caching oder andere Probleme zu beschuldigen, liegt das Problem eindeutig immer noch in der Verwaltung der temporären Tabellenbereinigung.

In SQL Server 2019 ist es ein engerer Aufruf mit aktivierten In-Memory-Systemtabellen, aber Tabellenvariablen funktionieren immer noch besser, wenn die Anrufhäufigkeit hoch ist.

Natürlich, wie ein vaping Salbei einmal überlegte: "Verwenden Sie Tabellenvariablen, wenn die Planauswahl kein Problem darstellt".

10
Erik Darling