Mengapa menggunakan variabel tabel lebih dari dua kali lebih cepat dari tabel #temp dalam kasus khusus ini?

37

Saya sedang melihat artikel di sini Tabel Sementara vs Variabel Tabel dan Efeknya pada Kinerja SQL Server dan pada SQL Server 2008 mampu mereproduksi hasil yang serupa dengan yang ditunjukkan di sana untuk tahun 2005.

Saat menjalankan prosedur tersimpan (definisi di bawah) dengan hanya 10 baris versi variabel tabel keluar melakukan versi tabel sementara lebih dari dua kali.

Saya membersihkan cache prosedur dan menjalankan kedua prosedur yang tersimpan 10.000 kali kemudian mengulangi proses untuk 4 berjalan lagi. Hasil di bawah (waktu dalam ms per batch)

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

Pertanyaan saya adalah: Apa alasan untuk kinerja versi variabel tabel yang lebih baik?

Saya sudah melakukan investigasi. mis. Melihat penghitung kinerja dengan

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

menegaskan bahwa dalam kedua kasus objek sementara sedang di-cache setelah dijalankan pertama seperti yang diharapkan daripada dibuat dari awal lagi untuk setiap doa.

Demikian pula menelusuri Auto Stats, SP:Recompile, SQL:StmtRecompileperistiwa di Profiler (gambar di bawah) menunjukkan bahwa peristiwa ini hanya terjadi sekali (pada doa pertama dari #temptabel disimpan prosedur) dan 9.999 eksekusi lain tidak menaikkan salah satu peristiwa ini. (Versi variabel tabel tidak mendapatkan salah satu dari peristiwa ini)

Jejak

Overhead yang sedikit lebih besar dari proses pertama dari prosedur tersimpan tidak dapat menjelaskan perbedaan keseluruhan yang besar namun karena masih hanya membutuhkan beberapa ms untuk menghapus cache prosedur dan menjalankan kedua prosedur sekali jadi saya tidak percaya statistik atau kompilasi ulang bisa menjadi penyebabnya.

Buat Objek Database yang Diperlukan

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

Skrip Tes

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
Martin Smith
sumber
Jejak profiler menunjukkan bahwa statistik hanya dapat dibuat di atas #tempmeja satu kali meskipun itu dibersihkan dan dihuni kembali sebanyak 9999 kali berikutnya.
Martin Smith

Jawaban:

31

Output SET STATISTICS IO ONuntuk keduanya terlihat serupa

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

Memberi

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

Dan seperti yang ditunjukkan Harun di komentar rencana untuk versi tabel variabel sebenarnya kurang efisien karena sementara keduanya memiliki loop bersarang berencana didorong oleh indeks mencari di dbo.NUMdalam #tempMelakukan versi meja mencari ke dalam indeks pada [#T].n = [dbo].[NUM].[n]dengan predikat sisa [#T].[n]<=[@total]sedangkan variabel tabel versi melakukan pencarian indeks @V.n <= [@total]dengan sisa predikat @V.[n]=[dbo].[NUM].[n]dan memproses lebih banyak baris (itulah sebabnya rencana ini berkinerja sangat buruk untuk jumlah baris yang lebih besar)

Menggunakan Extended Events untuk melihat jenis tunggu untuk spid spesifik memberikan hasil ini untuk 10.000 eksekusiEXEC 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              |
+---------------------+------------+----------------+----------------+----------------+

dan hasil ini untuk 10.000 eksekusi 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              |
+---------------------+------------+----------------+----------------+----------------+

Jadi jelas bahwa jumlah PAGELATCH_SHmenunggu jauh lebih tinggi dalam #tempcase table. Saya tidak mengetahui adanya cara untuk menambahkan sumber daya tunggu ke jejak peristiwa yang diperluas sehingga untuk menyelidiki ini saya lanjutkan

WHILE 1=1
EXEC dbo.T2 10

Sementara di polling koneksi lain 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'

Setelah meninggalkan menjalankan itu selama sekitar 15 detik itu telah mengumpulkan hasil sebagai berikut

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

Kedua halaman ini yang diikat milik (berbeda) indeks tidak berkerumun di tempdb.sys.sysschobjstabel dasar bernama 'nc1'dan 'nc2'.

Permintaan tempdb.sys.fn_dblogselama proses menunjukkan bahwa jumlah catatan log yang ditambahkan oleh eksekusi pertama dari setiap prosedur yang disimpan agak bervariasi tetapi untuk eksekusi berikutnya jumlah yang ditambahkan oleh setiap iterasi sangat konsisten dan dapat diprediksi. Setelah rencana prosedur di-cache, jumlah entri log adalah sekitar setengah dari yang dibutuhkan untuk #tempversi.

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

Melihat entri log transaksi secara lebih terperinci untuk #tempversi tabel SP, setiap pemanggilan berikutnya dari prosedur tersimpan menciptakan tiga transaksi dan satu variabel tabel hanya dua.

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

The INSERT/ TVQUERYtransaksi adalah identik kecuali untuk nama. Ini berisi catatan log untuk masing-masing dari 10 baris yang dimasukkan ke tabel sementara atau variabel tabel ditambah entri LOP_BEGIN_XACT/ LOP_COMMIT_XACT.

The CREATE TABLEtransaksi hanya muncul dalam #Tempversi dan terlihat sebagai berikut.

+-----------------+-------------------+---------------------+
|    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          |                     |
+-----------------+-------------------+---------------------+

The FCheckAndCleanupCachedTempTabletransaksi muncul baik tetapi memiliki 6 entri tambahan di #tempversi. Ini adalah 6 baris yang merujuk sys.sysschobjsdan mereka memiliki pola yang persis sama seperti di atas.

+-----------------+-------------------+----------------------------------------------+
|    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          |                                              |
+-----------------+-------------------+----------------------------------------------+

Melihat 6 baris dalam kedua transaksi ini, mereka berhubungan dengan operasi yang sama. Yang pertama LOP_MODIFY_ROW, LCX_CLUSTEREDadalah pembaruan ke modify_datekolom di sys.objects. Lima baris yang tersisa semuanya berkaitan dengan penggantian nama objek. Karena namemerupakan kolom kunci dari kedua NCI yang terkena dampak ( nc1dan nc2) ini dilakukan sebagai delete / insert untuk yang kemudian kembali ke indeks berkerumun dan memperbarui itu juga.

Tampaknya untuk #tempversi tabel ketika prosedur tersimpan mengakhiri bagian dari pembersihan yang dilakukan oleh FCheckAndCleanupCachedTempTabletransaksi adalah mengubah nama tabel temp dari sesuatu seperti #T__________________________________________________________________________________________________________________00000000E316menjadi nama internal yang berbeda seperti #2F4A0079dan ketika dimasukkan, CREATE TABLEtransaksi mengubah nama kembali. Nama flip flopping ini dapat dilihat dengan di satu koneksi mengeksekusi dbo.T2dalam satu loop sementara yang lain

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

Contoh Hasil

Tangkapan layar

Jadi salah satu penjelasan potensial untuk perbedaan kinerja yang diamati seperti yang disinggung oleh Alex adalah bahwa pekerjaan tambahan ini menjaga tabel sistem tempdbyang bertanggung jawab.


Menjalankan kedua prosedur dalam satu lingkaran, profiler Visual Studio Code mengungkapkan yang berikut ini

+-------------------------------+--------------------+-------+-----------+
|           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      |
+-------------------------------+--------------------+-------+-----------+

Versi variabel tabel menghabiskan sekitar 60% dari waktu melakukan pernyataan penyisipan dan pemilihan berikutnya sedangkan tabel sementara kurang dari setengahnya. Ini sejalan dengan waktu yang ditunjukkan dalam OP dan dengan kesimpulan di atas bahwa perbedaan kinerja adalah waktu yang dihabiskan untuk melakukan pekerjaan tambahan bukan karena waktu yang dihabiskan dalam eksekusi permintaan itu sendiri.

Fungsi terpenting yang berkontribusi terhadap 75% yang "hilang" dalam versi tabel sementara adalah

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

Di bawah kedua fungsi buat dan lepas fungsi CMEDProxyObject::SetNametersebut ditampilkan dengan nilai sampel inklusif dari 19.6%. Dari yang saya simpulkan bahwa 39,2% dari waktu dalam kasus tabel sementara diambil dengan penggantian nama yang dijelaskan sebelumnya.

Dan yang terbesar dalam versi variabel tabel berkontribusi untuk 40% lainnya

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

Profil Tabel Sementara

masukkan deskripsi gambar di sini

Profil Variabel Tabel

masukkan deskripsi gambar di sini

Martin Smith
sumber
10

Disko Inferno

Karena ini adalah pertanyaan yang lebih lama, saya memutuskan untuk meninjau kembali masalah pada versi SQL Server yang lebih baru untuk melihat apakah profil kinerja yang sama masih ada, atau apakah karakteristiknya telah berubah sama sekali.

Secara khusus, penambahan tabel sistem dalam-memori untuk SQL Server 2019 tampaknya merupakan kesempatan yang berharga untuk pengujian ulang.

Saya menggunakan test harness yang sedikit berbeda, karena saya mengalami masalah ini saat mengerjakan sesuatu yang lain.

Pengujian, pengujian

Menggunakan Stack Overflow versi 2013 , saya memiliki indeks ini dan dua prosedur ini:

Indeks:

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

Meja temp:

    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 

Variabel tabel:

    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 

Untuk mencegah kemungkinan menunggu ASYNC_NETWORK_IO , saya menggunakan prosedur pembungkus.

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

Sejak 2014 dan 2016 pada dasarnya RELICS pada titik ini, saya memulai pengujian saya dengan 2017. Juga, untuk singkatnya, saya langsung melompat ke profiling kode dengan Perfview . Dalam kehidupan nyata, saya melihat menunggu, kait, spinlocks, bendera jejak gila, dan hal-hal lainnya.

Membuat profil kode adalah satu-satunya hal yang mengungkapkan minat.

Perbedaan waktu:

  • Tabel Temp: 17891 ms
  • Variabel Tabel: 5891 ms

Masih perbedaan yang sangat jelas, eh? Tapi apa yang memukul SQL Server sekarang?

GILA

Melihat dua peningkatan teratas dalam sampel diffed, kita melihat sqlmindan sqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucketmerupakan dua pelanggar terbesar.

GILA

Dilihat oleh nama-nama dalam tumpukan panggilan, membersihkan dan secara internal mengubah nama tabel temp tampaknya menjadi waktu terbesar menyebalkan dalam panggilan tabel temp vs panggilan variabel tabel.

Meskipun variabel tabel didukung secara internal oleh temp tables, ini tampaknya tidak menjadi masalah.

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

Tabel '# B98CE339'. Pindai hitungan 1

Melihat melalui tumpukan panggilan untuk uji variabel tabel tidak menunjukkan salah satu dari pelaku utama sama sekali:

GILA

SQL Server 2019 (Vanilla)

Baiklah, jadi ini masih menjadi masalah di SQL Server 2017, apakah ada yang berbeda di 2019 di luar kotak?

Pertama, untuk menunjukkan tidak ada apa-apa di lengan saya:

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

GILA

Perbedaan waktu:

  • Tabel temp: 15765 ms
  • Variabel Tabel: 7250 ms

Kedua prosedur berbeda. Panggilan tabel temp adalah beberapa detik lebih cepat, dan panggilan variabel tabel sekitar 1,5 detik lebih lambat. Variabel tabel melambat sebagian dapat dijelaskan oleh kompilasi variabel tabel ditangguhkan , pilihan pengoptimal baru pada 2019.

Melihat perbedaan dalam Perfview, itu telah berubah sedikit - sqlmin sudah tidak ada lagi - tetapi sqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucketada.

GILA

SQL Server 2019 (tabel sistem In-Memory Tempdb)

Bagaimana dengan ini baru dalam hal tabel sistem memori? Hm? Sup dengan itu?

Ayo nyalakan!

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

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

Perhatikan bahwa ini membutuhkan restart Server SQL untuk memulai, jadi maafkan saya ketika saya reboot SQL pada Jumat sore yang indah ini.

Sekarang semuanya terlihat berbeda:

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;

GILA

Perbedaan waktu:

  • Tabel temp: 11638 ms
  • Variabel tabel: 7403 ms

Tabel temp melakukan sekitar 4 detik lebih baik! Itu sesuatu.

Saya suka sesuatu.

Kali ini, perbedaan Perfview tidak terlalu menarik. Berdampingan, menarik untuk dicatat seberapa dekat waktu di papan:

GILA

Satu hal yang menarik di diff adalah panggilan ke hkengine!, yang mungkin tampak jelas karena fitur hekaton-ish sekarang digunakan.

GILA

Sejauh dua item teratas dalam diff, saya tidak dapat membuat banyak dari ntoskrnl!?:

GILA

Atau sqltses!CSqlSortManager_80::GetSortKey, tetapi mereka ada di sini untuk Smrtr Ppl ™ untuk melihat:

GILA

Perhatikan bahwa ada yang tidak terdokumentasi dan jelas tidak aman untuk produksi, jadi jangan gunakan bendera jejak startup yang dapat Anda gunakan untuk memiliki objek sistem tabel temp tambahan (sysrowsets, sysallocunits, dan sysseobjvalues) yang termasuk dalam fitur di dalam memori, tetapi tidak membuat perbedaan nyata dalam waktu eksekusi dalam kasus ini.

Pembulatan

Bahkan dalam versi SQL Server yang lebih baru, panggilan frekuensi tinggi ke variabel tabel jauh lebih cepat daripada panggilan frekuensi tinggi ke tabel temp.

Meskipun tergoda untuk menyalahkan kompilasi, kompilasi ulang, statistik otomatis, kait, spinlocks, caching, atau masalah lainnya, masalah ini jelas masih seputar pengelolaan pembersihan tabel temp.

Ini panggilan yang lebih dekat di SQL Server 2019 dengan tabel sistem di-memori diaktifkan, tetapi variabel tabel masih berkinerja lebih baik ketika frekuensi panggilan tinggi.

Tentu saja, sebagai orang bijak vaping sekali renung: "gunakan variabel tabel ketika pilihan rencana tidak menjadi masalah".

Erik Darling
sumber
Bagus - maaf saya melewatkan bahwa Anda menambahkan jawaban untuk ini sampai hanya mengikuti tautan dalam posting blog "debugging" Anda
Martin Smith