Ini tampaknya merupakan area dengan beberapa mitos dan pandangan yang saling bertentangan.
Jadi apa perbedaan antara variabel tabel dan tabel sementara lokal di SQL Server?
Ini tampaknya merupakan area dengan beberapa mitos dan pandangan yang saling bertentangan.
Jadi apa perbedaan antara variabel tabel dan tabel sementara lokal di SQL Server?
Jawaban:
Isi
Peringatan
Jawaban ini membahas variabel tabel "klasik" yang diperkenalkan dalam SQL Server 2000. SQL Server 2014 di memori OLTP memperkenalkan Tipe Tabel yang Dioptimalkan Memori. Contoh variabel tabel berbeda dalam banyak hal dengan yang dibahas di bawah ini! ( lebih detail ).
Lokasi penyimpanan
Tidak ada perbedaan. Keduanya disimpan di tempdb
.
Saya telah melihatnya menyarankan bahwa untuk variabel tabel ini tidak selalu terjadi tetapi ini dapat diverifikasi dari bawah
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Contoh Hasil (menunjukkan lokasi dalam tempdb
2 baris disimpan)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Lokasi yang logis
@table_variables
berperilaku lebih seolah-olah mereka adalah bagian dari database saat ini daripada #temp
tabel. Untuk variabel tabel (sejak 2005), jika tidak ditentukan secara eksplisit akan menjadi basis data saat ini sedangkan untuk #temp
tabel akan menggunakan susunan default tempdb
( Lebih detail ). Selain itu tipe data yang ditentukan pengguna dan koleksi XML harus dalam tempdb untuk digunakan untuk #temp
tabel tetapi variabel tabel dapat menggunakannya dari database saat ini ( Sumber ).
SQL Server 2012 memperkenalkan basis data yang terkandung. perilaku tabel sementara dalam perbedaan ini (h / t Aaron)
Dalam basis data yang terkandung, data tabel sementara dikumpulkan dalam pengumpulan basis data yang terkandung.
- Semua metadata yang terkait dengan tabel sementara (misalnya, nama tabel dan kolom, indeks, dan sebagainya) akan ada dalam susunan katalog.
- Kendala yang disebutkan tidak dapat digunakan dalam tabel sementara.
- Tabel sementara mungkin tidak merujuk ke tipe yang ditentukan pengguna, koleksi skema XML, atau fungsi yang ditentukan pengguna.
Visibilitas ke lingkup yang berbeda
@table_variables
hanya dapat diakses dalam kumpulan dan ruang lingkup di mana mereka dinyatakan. #temp_tables
dapat diakses dalam batch anak (pemicu bersarang, prosedur, exec
panggilan). #temp_tables
dibuat di lingkup luar ( @@NESTLEVEL=0
) dapat menjangkau bets juga karena mereka bertahan sampai sesi berakhir. Tidak ada tipe objek yang dapat dibuat dalam batch anak dan diakses dalam ruang lingkup panggilan namun seperti yang dibahas selanjutnya ( ##temp
tabel global bisa jadi).
Seumur hidup
@table_variables
dibuat secara implisit ketika kumpulan yang berisi DECLARE @.. TABLE
pernyataan dieksekusi (sebelum kode pengguna apa pun dalam kumpulan tersebut berjalan) dan dihapus secara implisit di akhir.
Meskipun parser tidak akan memungkinkan Anda untuk mencoba dan menggunakan variabel tabel sebelum DECLARE
pernyataan, pembuatan implisit dapat dilihat di bawah ini.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
dibuat secara eksplisit ketika CREATE TABLE
pernyataan TSQL ditemukan dan dapat dijatuhkan secara eksplisit dengan DROP TABLE
atau akan dijatuhkan secara implisit ketika batch berakhir (jika dibuat dalam batch anak dengan @@NESTLEVEL > 0
) atau ketika sesi berakhir sebaliknya.
NB: Dalam rutinitas tersimpan kedua jenis objek dapat di-cache daripada berulang kali membuat dan menjatuhkan tabel baru. Ada pembatasan kapan caching ini dapat terjadi namun yang mungkin dilanggar #temp_tables
tetapi pembatasan @table_variables
tetap mencegah. Overhead pemeliharaan untuk #temp
tabel cache sedikit lebih besar daripada untuk variabel tabel seperti yang diilustrasikan di sini .
Metadata objek
Ini pada dasarnya sama untuk kedua jenis objek. Ini disimpan dalam tabel basis sistem di tempdb
. Akan lebih mudah untuk melihat #temp
tabel tetapi seperti yang OBJECT_ID('tempdb..#T')
dapat digunakan untuk memasukkan ke dalam tabel sistem dan nama yang dihasilkan secara internal berkorelasi lebih erat dengan nama yang didefinisikan dalam CREATE TABLE
pernyataan. Untuk variabel tabel, object_id
fungsi tidak bekerja dan nama internal sepenuhnya dihasilkan oleh sistem tanpa hubungan dengan nama variabel. Di bawah ini menunjukkan metadata masih ada namun dengan memasukkan nama kolom (semoga unik). Untuk tabel tanpa nama kolom unik, object_id dapat ditentukan menggunakan DBCC PAGE
asalkan tidak kosong.
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
Keluaran
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
Transaksi
Operasi pada @table_variables
dilakukan sebagai transaksi sistem, independen dari setiap transaksi pengguna luar, sedangkan #temp
operasi tabel yang setara akan dilakukan sebagai bagian dari transaksi pengguna itu sendiri. Karena alasan ini suatu ROLLBACK
perintah akan memengaruhi sebuah #temp
tabel tetapi membiarkannya @table_variable
tidak tersentuh.
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
Penebangan
Keduanya menghasilkan catatan log ke tempdb
log transaksi. Kesalahpahaman yang umum adalah bahwa ini bukan kasus untuk variabel tabel sehingga skrip yang menunjukkan ini di bawah ini, itu menyatakan variabel tabel, menambahkan beberapa baris kemudian memperbarui dan menghapusnya.
Karena variabel tabel dibuat dan dihapus secara implisit pada awal dan akhir batch, perlu menggunakan beberapa batch untuk melihat log lengkap.
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
Kembali
Sejauh yang saya bisa membedakan operasi pada keduanya menghasilkan jumlah penebangan yang kira-kira sama.
Sementara jumlah logging sangat mirip satu perbedaan penting adalah bahwa catatan log terkait dengan #temp
tabel tidak dapat dihapus sampai transaksi yang mengandung pengguna selesai sehingga transaksi yang berjalan lama yang pada beberapa titik menulis ke #temp
tabel akan mencegah pemotongan log di tempdb
mana transaksi otonom menelurkan untuk variabel tabel tidak.
Variabel tabel tidak mendukung TRUNCATE
sehingga dapat berada pada kerugian logging ketika persyaratannya adalah untuk menghapus semua baris dari tabel (meskipun untuk tabel yang sangat kecil DELETE
dapat bekerja dengan lebih baik )
Kardinalitas
Banyak dari rencana eksekusi yang melibatkan variabel tabel akan memperlihatkan satu baris yang diestimasi sebagai output dari mereka. Memeriksa properti variabel tabel menunjukkan bahwa SQL Server percaya variabel tabel memiliki baris nol (Mengapa memperkirakan 1 baris akan dipancarkan dari tabel baris nol dijelaskan oleh @Paul White di sini ).
Namun hasil yang ditunjukkan pada bagian sebelumnya memang menunjukkan rows
jumlah yang akurat di sys.partitions
. Masalahnya adalah bahwa pada kebanyakan kesempatan pernyataan referensi variabel tabel dikompilasi ketika tabel kosong. Jika pernyataan itu (kembali) dikompilasi setelah @table_variable
dihuni maka ini akan digunakan untuk kardinalitas tabel sebagai gantinya (Ini mungkin terjadi karena eksplisit recompile
atau mungkin karena pernyataan itu juga referensi objek lain yang menyebabkan kompilasi yang ditangguhkan atau kompilasi ulang.)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
Paket menunjukkan perkiraan jumlah baris akurat setelah kompilasi ditangguhkan.
Dalam SQL Server 2012 SP2, bendera jejak 2453 diperkenalkan. Rincian lebih lanjut berada di bawah "Mesin Relasional" di sini .
Ketika flag trace ini diaktifkan, ia dapat menyebabkan kompilasi ulang otomatis untuk memperhitungkan perubahan kardinalitas sebagaimana dibahas lebih lanjut dengan sangat singkat.
NB: Pada Azure di tingkat kompatibilitas, kompilasi pernyataan tersebut sekarang ditangguhkan hingga eksekusi pertama . Ini berarti bahwa itu tidak akan lagi tunduk pada masalah estimasi baris nol.
Tidak ada statistik kolom
Akan tetapi, memiliki kardinalitas tabel yang lebih akurat tidak berarti bahwa perkiraan jumlah baris akan lebih akurat (kecuali melakukan operasi pada semua baris dalam tabel). SQL Server sama sekali tidak memelihara statistik kolom untuk variabel tabel sehingga akan kembali pada dugaan berdasarkan predikat perbandingan (mis. 10% dari tabel akan dikembalikan untuk =
kolom yang tidak unik atau 30% untuk >
perbandingan). Sebaliknya statistik kolom yang dipertahankan untuk #temp
tabel.
SQL Server memelihara hitungan jumlah modifikasi yang dibuat untuk setiap kolom. Jika jumlah modifikasi sejak rencana disusun melebihi ambang recompilation (RT) maka rencana akan dikompilasi ulang dan statistik diperbarui. RT tergantung pada jenis dan ukuran tabel.
Dari Plan Caching di SQL Server 2008
RT dihitung sebagai berikut. (n merujuk pada kardinalitas tabel ketika rencana kueri dikompilasi.)
Tabel permanen
- Jika n <= 500, RT = 500.
- Jika n> 500, RT = 500 + 0.20 * n.Tabel sementara
- Jika n <6, RT = 6.
- Jika 6 <= n <= 500, RT = 500.
- Jika n> 500, RT = 500 + 0.20 * n.
Variabel tabel
- RT tidak ada. Oleh karena itu, kompilasi ulang tidak terjadi karena perubahan kardinalitas variabel tabel. (Tetapi lihat catatan tentang TF 2453 di bawah)
yang KEEP PLAN
sedikit dapat digunakan untuk mengatur RT untuk #temp
tabel sama dengan tabel permanen.
Efek bersih dari semua ini adalah bahwa seringkali rencana eksekusi yang dihasilkan untuk #temp
tabel adalah urutan besarnya lebih baik daripada @table_variables
ketika banyak baris terlibat karena SQL Server memiliki informasi yang lebih baik untuk bekerja dengannya.
NB1: Variabel tabel tidak memiliki statistik tetapi masih dapat menimbulkan peristiwa kompilasi "Statistik Berubah" di bawah bendera jejak 2453 (tidak berlaku untuk rencana "sepele") Ini tampaknya terjadi di bawah ambang batas kompilasi ulang yang sama seperti yang ditunjukkan untuk tabel temp di atas dengan satu lagi yang jika N=0 -> RT = 1
. yaitu semua pernyataan yang dikompilasi ketika variabel tabel kosong akan berakhir mendapatkan kompilasi ulang dan dikoreksi TableCardinality
saat pertama kali dieksekusi ketika non kosong. Kardinalitas tabel waktu kompilasi disimpan dalam rencana dan jika pernyataan dieksekusi lagi dengan kardinalitas yang sama (baik karena aliran pernyataan kontrol atau penggunaan kembali rencana cache) tidak ada kompilasi ulang yang terjadi.
NB2: Untuk tabel sementara yang di-cache dalam prosedur tersimpan cerita rekompilasi jauh lebih rumit daripada yang dijelaskan di atas. Lihat Tabel Sementara dalam Prosedur yang Disimpan untuk semua detail berdarah.
Rekompilasi
Serta kompilasi berdasarkan modifikasi yang dijelaskan di atas #temp
tabel juga dapat dikaitkan dengan kompilasi tambahan hanya karena mereka memungkinkan operasi yang dilarang untuk variabel tabel yang memicu kompilasi (misalnya perubahan DDL CREATE INDEX
, ALTER TABLE
)
Mengunci
Telah dinyatakan bahwa variabel tabel tidak berpartisipasi dalam penguncian. Ini bukan kasusnya. Menjalankan output di bawah ini ke tab pesan SSMS rincian kunci yang diambil dan dirilis untuk pernyataan penyisipan.
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
Untuk pertanyaan yang SELECT
dari variabel tabel Paul White menunjukkan dalam komentar bahwa ini secara otomatis datang dengan NOLOCK
petunjuk implisit . Ini ditunjukkan di bawah ini
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
Dampak dari ini pada penguncian mungkin sangat kecil.
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
Tidak satu pun dari hasil ini dalam urutan kunci indeks yang menunjukkan bahwa SQL Server menggunakan pemindaian alokasi yang dialokasikan untuk keduanya.
Saya menjalankan skrip di atas dua kali dan hasil untuk menjalankan kedua di bawah
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
Output penguncian untuk variabel tabel memang sangat minim karena SQL Server baru saja memperoleh kunci stabilitas skema pada objek. Tetapi untuk sebuah #temp
meja hampir sama ringannya dengan mengeluarkan S
kunci level objek . Sebuah NOLOCK
petunjuk atau READ UNCOMMITTED
isolasi tingkat tentu saja dapat ditentukan secara eksplisit ketika bekerja dengan #temp
tabel juga.
Demikian pula dengan masalah pencatatan transaksi pengguna di sekitarnya dapat berarti bahwa kunci ditahan lebih lama untuk #temp
tabel. Dengan skrip di bawah ini
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
ketika dijalankan di luar transaksi pengguna eksplisit untuk kedua kasus, satu - satunya kunci yang dikembalikan saat memeriksa sys.dm_tran_locks
adalah kunci bersama di DATABASE
.
Pada uncomment, BEGIN TRAN ... ROLLBACK
26 baris dikembalikan yang menunjukkan bahwa kunci dipegang pada objek itu sendiri dan pada baris sistem tabel untuk memungkinkan rollback dan mencegah transaksi lain dari membaca data yang tidak dikomit. Operasi variabel tabel ekuivalen tidak tunduk pada kembalikan dengan transaksi pengguna dan tidak perlu menahan kunci-kunci ini untuk kami periksa dalam pernyataan berikutnya tetapi melacak kunci yang diperoleh dan dirilis di Profiler atau menggunakan jejak bendera 1200 menunjukkan banyak peristiwa penguncian masih terjadi.
Indeks
Untuk versi sebelum indeks SQL Server 2014 hanya dapat dibuat secara implisit pada variabel tabel sebagai efek samping dari menambahkan batasan unik atau kunci utama. Ini tentu saja berarti bahwa hanya indeks unik yang didukung. Indeks non-clustered non unik pada tabel dengan indeks clustered unik dapat disimulasikan namun dengan hanya mendeklarasikannya UNIQUE NONCLUSTERED
dan menambahkan kunci CI ke akhir kunci NCI yang diinginkan (SQL Server tetap akan melakukan ini di belakang layar meskipun tidak unik NCI dapat ditentukan)
Seperti yang diperlihatkan sebelumnya berbagai index_option
s dapat ditentukan dalam deklarasi kendala termasuk DATA_COMPRESSION
,, IGNORE_DUP_KEY
dan FILLFACTOR
(meskipun tidak ada gunanya menetapkan bahwa satu karena hanya akan membuat perbedaan pada indeks membangun kembali dan Anda tidak dapat membangun kembali indeks pada variabel tabel!)
Selain itu variabel tabel tidak mendukung INCLUDE
d kolom, indeks yang difilter (hingga 2016) atau #temp
tabel , yang dilakukan tabel (skema partisi harus dibuat di tempdb
).
Indeks dalam SQL Server 2014
Indeks non unik dapat dinyatakan sebaris dalam definisi variabel tabel di SQL Server 2014. Contoh sintaks untuk ini adalah di bawah ini.
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
Indeks dalam SQL Server 2016
Dari CTP 3.1 sekarang dimungkinkan untuk mendeklarasikan indeks yang difilter untuk variabel tabel. Dengan RTM itu mungkin kasus yang menyertakan kolom juga diperbolehkan meskipun mereka kemungkinan tidak akan masuk ke SQL16 karena kendala sumber daya
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
Paralelisme
Kueri yang disisipkan ke (atau memodifikasi) @table_variables
tidak dapat memiliki rencana paralel, #temp_tables
tidak dibatasi dengan cara ini.
Ada solusi nyata dalam penulisan ulang sebagai berikut yang memungkinkan SELECT
bagian terjadi secara paralel tetapi akhirnya menggunakan tabel sementara yang tersembunyi (di belakang layar)
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
Tidak ada batasan dalam kueri yang memilih dari variabel tabel seperti yang diilustrasikan dalam jawaban saya di sini
Perbedaan Fungsional Lainnya
#temp_tables
tidak dapat digunakan di dalam suatu fungsi. @table_variables
dapat digunakan di dalam skalar atau tabel multi-pernyataan UDF.@table_variables
tidak dapat menyebutkan batasan.@table_variables
tidak dapat SELECT
-ed INTO
, ALTER
-ed, TRUNCATE
d atau menjadi target dari DBCC
perintah seperti DBCC CHECKIDENT
atau dari SET IDENTITY INSERT
dan tidak mendukung petunjuk tabel sepertiWITH (FORCESCAN)
CHECK
kendala pada variabel tabel tidak dipertimbangkan oleh pengoptimal untuk penyederhanaan, predikat tersirat atau deteksi kontradiksi.PAGELATCH_EX
menunggu. ( Contoh )Hanya Memori?
Seperti yang dinyatakan di awal, keduanya disimpan di halaman tempdb
. Namun saya tidak membahas apakah ada perbedaan perilaku ketika menulis halaman ini ke disk.
Saya telah melakukan sejumlah kecil pengujian pada ini sekarang dan sejauh ini tidak melihat perbedaan seperti itu. Dalam tes khusus yang saya lakukan pada contoh saya SQL Server 250 halaman tampaknya menjadi titik potong sebelum file data ditulis.
NB: Perilaku di bawah ini tidak lagi terjadi di SQL Server 2014 atau SQL Server 2012 SP1 / CU10 atau SP2 / CU1 penulis bersemangat tidak lagi ingin menulis halaman ke disk. Lebih detail tentang perubahan itu di SQL Server 2014: tempdb Hidden Performance Gem .
Menjalankan skrip di bawah ini
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
Dan pemantauan menulis ke tempdb
file data dengan Proses Monitor saya tidak melihat (kecuali yang kadang-kadang ke halaman boot database di offset 73.728). Setelah berganti 250
ke 251
saya mulai melihat tulisan seperti di bawah ini.
Tangkapan layar di atas menunjukkan 5 * 32 halaman menulis dan satu halaman menulis yang menunjukkan bahwa 161 halaman ditulis ke disk. Saya mendapat titik potong yang sama dari 250 halaman ketika menguji dengan variabel tabel juga. Script di bawah ini menunjukkan cara yang berbeda dengan melihatnyasys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
Menunjukkan bahwa 192 halaman ditulis ke disk dan bendera kotor dibersihkan. Ini juga menunjukkan bahwa sedang ditulis ke disk tidak berarti bahwa halaman akan segera diusir dari kumpulan buffer. Kueri terhadap variabel tabel ini masih bisa dipenuhi sepenuhnya dari memori.
Pada server idle dengan max server memory
set ke 2000 MB
dan DBCC MEMORYSTATUS
pelaporan Buffer Pool Pages Dialokasikan sebagai sekitar 1.843.000 KB (c. 23.000 halaman) saya menyisipkan ke tabel di atas dalam batch 1.000 baris / halaman dan untuk setiap iterasi yang direkam.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
Variabel tabel dan #temp
tabel memberikan grafik yang hampir sama dan berhasil memaksimalkan buffer pool sebelum mencapai titik bahwa mereka tidak sepenuhnya disimpan dalam memori sehingga tampaknya tidak ada batasan khusus pada seberapa banyak memori baik dapat mengkonsumsi.
Ada beberapa hal yang ingin saya tunjukkan lebih berdasarkan pengalaman tertentu daripada belajar. Sebagai seorang DBA, saya sangat baru jadi tolong perbaiki saya jika diperlukan.