it-swarm.asia

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

Saya melihat artikel di sini Tabel Sementara vs Variabel Tabel dan Efeknya pada Kinerja SQL Server dan pada SQL Server 2008 dapat 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 ini (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 yang lebih baik dari versi variabel tabel?

Saya sudah melakukan investigasi. misalnya 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 melacak Auto Stats, SP:Recompile, SQL:StmtRecompileevents di Profiler (tangkapan layar di bawah) menunjukkan bahwa peristiwa ini hanya terjadi satu kali (pada permohonan pertama #temp prosedur tersimpan tabel) dan 9999 eksekusi lainnya tidak memunculkan salah satu dari peristiwa ini. (Versi variabel tabel tidak mendapatkan salah satu dari peristiwa ini)

Trace

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.

Membuat Objek Basis Data 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
37
Martin Smith

Output dari SET STATISTICS IO ON untuk 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 Aaron dalam komentar, rencana untuk versi variabel tabel sebenarnya kurang efisien karena keduanya memiliki paket loop bersarang yang digerakkan oleh pencarian indeks pada dbo.NUM the #temp versi tabel melakukan pencarian ke dalam indeks pada [#T].n = [dbo].[NUM].[n] dengan predikat residual [#T].[n]<=[@total] sedangkan versi variabel tabel melakukan pencarian indeks pada @V.n <= [@total] dengan predikat residual @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 eksekusi 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              |
+---------------------+------------+----------------+----------------+----------------+

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_SH menunggu jauh lebih tinggi di #temp kasing meja. 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 berikut

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

Kedua halaman yang diikat milik indeks berbeda (tidak dikelompokkan) pada tempdb.sys.sysschobjs tabel dasar bernama 'nc1' dan 'nc2'.

Permintaan tempdb.sys.fn_dblog selama proses menunjukkan bahwa jumlah catatan log yang ditambahkan oleh eksekusi pertama dari setiap prosedur yang disimpan agak bervariasi tetapi untuk eksekusi selanjutnya 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 #temp Versi: kapan.

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

Melihat entri log transaksi secara lebih rinci untuk #temp tabel versi SP setiap permohonan selanjutnya dari prosedur tersimpan menciptakan tiga transaksi dan variabel tabel hanya dua.

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

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

CREATE TABLE transaksi hanya muncul di #Temp versi 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          |                     |
+-----------------+-------------------+---------------------+

Transaksi FCheckAndCleanupCachedTempTable muncul di keduanya tetapi memiliki 6 entri tambahan di #temp Versi: kapan. Ini adalah 6 baris yang merujuk pada sys.sysschobjs dan mereka memiliki pola yang sama persis 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 ini dalam kedua transaksi mereka berhubungan dengan operasi yang sama. Pertama LOP_MODIFY_ROW, LCX_CLUSTERED adalah pembaruan untuk modify_date kolom dalam sys.objects. Lima baris yang tersisa semuanya berkaitan dengan penggantian nama objek. Karena name adalah kolom kunci dari kedua NCI yang terpengaruh (nc1 dan nc2) ini dilakukan sebagai delete/insert untuk yang kemudian kembali ke indeks berkerumun dan memperbarui itu juga.

Tampaknya untuk #temp versi tabel ketika prosedur tersimpan mengakhiri bagian dari pembersihan yang dilakukan oleh transaksi FCheckAndCleanupCachedTempTable adalah mengubah nama tabel temp dari sesuatu seperti #T__________________________________________________________________________________________________________________00000000E316 ke nama internal yang berbeda seperti #2F4A0079 dan ketika dimasukkan CREATE TABLE transaksi mengganti nama kembali. Nama flip flopping ini dapat dilihat oleh dalam satu koneksi yang menjalankan dbo.T2 dalam satu lingkaran sementara pada yang lain

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

Contoh Hasil

Screenshot

Jadi salah satu penjelasan potensial untuk perbedaan kinerja yang diamati seperti yang disinggung oleh Alex adalah bahwa pekerjaan tambahan ini menjaga tabel sistem dalam tempdb yang 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 pengaturan 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 fungsi create dan release function CMEDProxyObject::SetName ditunjukkan dengan nilai sampel inklusif 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

enter image description here

Profil Variabel Tabel

enter image description here

31
Martin Smith

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 versi 2013 dari Stack Overflow , 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 potensi ASYNC_NETWORK_IO menungg , saya menggunakan prosedur wrapper.

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

Karena 2014 dan 2016 pada dasarnya adalah RELICS pada titik ini, saya memulai pengujian saya dengan 2017. Juga, untuk singkatnya, saya langsung beralih 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?

NUTS

Melihat dua peningkatan teratas dalam sampel yang dibedakan, kita melihat sqlmin dan sqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket adalah dua pelanggar terbesar.

NUTS

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:

NUTS

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';

NUTS

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 mungkin sebagian dijelaskan oleh kompilasi variabel tabel ditangguhkan , pilihan pengoptimal baru pada 2019.

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

NUTS

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;

NUTS

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:

NUTS

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

NUTS

Sejauh dua item teratas dalam diff, saya tidak bisa mendapatkan banyak dari ntoskrnl!?:

NUTS

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

NUTS

Perhatikan bahwa ada yang tidak berdokumen dan pasti tidak aman untuk produksi jadi jangan gunakan bendera jejak startup Anda dapat menggunakan untuk memiliki objek sistem tabel temp tambahan (sysrowsets, sysallocunits, dan sysseobjvalues) yang termasuk dalam fitur 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, masalahnya 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 seorang bijak vaping sekali renung: "gunakan variabel tabel ketika pilihan rencana tidak menjadi masalah".

10
Erik Darling