Bu, epeyce mit ve çelişkili görüşleri olan bir alan gibi görünüyor.
Öyleyse SQL Server'da bir tablo değişkeni ile yerel bir geçici tablo arasındaki fark nedir?
Bu, epeyce mit ve çelişkili görüşleri olan bir alan gibi görünüyor.
Öyleyse SQL Server'da bir tablo değişkeni ile yerel bir geçici tablo arasındaki fark nedir?
Yanıtlar:
içindekiler

Uyarı
Bu cevap, SQL Server 2000'de tanıtılan "klasik" tablo değişkenlerini tartışmaktadır. Bunların tablo değişken örnekleri, birçok açıdan aşağıda tartışılanlardan farklıdır! ( daha fazla detay ).
Depolama yeri
Fark yok. Her ikisi de içinde saklanır tempdb.
Tablo değişkenleri için bunun her zaman böyle olmadığını, ancak bunun aşağıdan doğrulanabileceğini öne sürdüm.
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Örnek Sonuçlar ( tempdb2 satırdaki yerleri gösterir)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Mantıksal Konum
@table_variablesGeçerli veritabanının bir parçasıymış gibi #temptablolardan daha fazla davranırlar . Tablo değişkenleri için (2005'ten beri) açıkça belirtilmemişse sütun harmanlamaları mevcut veritabanına ait olur, oysa #temptablolar için varsayılan harmanlamayı kullanır tempdb( Daha fazla ayrıntı ). Ek olarak, kullanıcı tanımlı veri tipleri ve XML koleksiyonları #temptablolar için kullanılacak tempdb içinde olmalıdır, fakat tablo değişkenleri bunları mevcut veritabanından ( Kaynak ) kullanabilir.
SQL Server 2012, içerilen veritabanlarını tanıtır. geçici tabloların bu davranışlardaki davranışları farklıdır (s / h Aaron)
İçeren bir veritabanında geçici tablo verileri, içerilen veritabanının harmanlanmasında harmanlanır.
- Geçici tablolarla ilişkili tüm meta veriler (örneğin, tablo ve sütun adları, dizinler vb.) Katalog harmanlamada olur.
- Adlandırılmış kısıtlamalar geçici tablolarda kullanılamaz.
- Geçici tablolar, kullanıcı tanımlı türler, XML şema koleksiyonları veya kullanıcı tanımlı işlevler için geçerli olmayabilir.
Farklı kapsamlara görünürlük
@table_variablesyalnızca bildirildikleri parti ve kapsam dahilinde erişilebilir. #temp_tablesalt gruplar içinde erişilebilir (iç içe tetikleyiciler, prosedür, execçağrılar). #temp_tablesdış kapsamda oluşturulan ( @@NESTLEVEL=0), oturum sona erinceye kadar devam ettiği sürece kümeleri de kapsayabilir. Alt grupta hiçbir nesne türü oluşturulamaz ve daha sonra tartışıldığı gibi arama kapsamına erişilebilir (genel ##temptablolar olabilir ).
Ömür
@table_variablesBir DECLARE @.. TABLEifade içeren bir toplu iş yürütüldüğünde (bu toplu iş içindeki herhangi bir kullanıcı kodu çalıştırılmadan önce) ve dolaylı olarak en sonunda bırakıldığında örtük olarak oluşturulur.
Ayrıştırıcı, deyimden önce tablo değişkenini denemenize ve kullanmanıza izin vermese de DECLARE, örtük oluşturma aşağıda görülebilir.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tablesTSQL CREATE TABLEdeyimine rastlandığında ve açıkça bırakılabiliyorsa açıkça DROP TABLEyaratılır @@NESTLEVEL > 0veya toplu işlem bittiğinde (bir çocuk partisinde oluşturulmuşsa ) veya oturum başka bir şekilde bittiğinde dolaylı olarak iptal edilir .
Not: Saklı rutinlerde, her iki nesne türü de tekrar tekrar yeni tablolar oluşturmak ve bırakmak yerine önbelleğe alınabilir . Bu önbelleklemenin ne zaman gerçekleşebileceği, ancak ihlal edilebilecek, #temp_tablesancak @table_variablesyine de kısıtlamaların engelleneceği konusunda kısıtlamalar vardır . Önbelleğe alınmış #temptablolar için bakım ek yükü, burada gösterilen tablo değişkenlerinden biraz daha büyüktür .
Nesne Meta Verileri
Bu aslında her iki nesne türü için de aynıdır. İçindeki sistem taban tablolarında saklanır tempdb. Bir #temptabloyu görmek daha basittir ancak OBJECT_ID('tempdb..#T')sistem tablolarını girmek için kullanılabildiği gibi ve dahili olarak oluşturulan isim de CREATE TABLEifadede tanımlanan isimle daha yakından ilişkilidir . Tablo değişkenleri için object_idişlev çalışmaz ve iç ad tamamen değişken adıyla bir ilişkisi olmayan bir sistemdir. Aşağıdaki, meta verinin hala orada olduğunu, ancak (umarım benzersiz) bir sütun adını girerek gösterir. Benzersiz sütun adlarına sahip olmayan tablolar için object_id DBCC PAGEboş olmadıkları sürece belirlenebilir .
/*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'
Çıktı
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 |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
işlemler
İşlemler, @table_variablesherhangi bir dış kullanıcı işleminden bağımsız olarak sistem işlemleri olarak gerçekleştirilirken, eşdeğer #temptablo işlemleri kullanıcı işleminin bir parçası olarak gerçekleştirilecektir. Bu sebeple bir ROLLBACKkomut bir #temptabloyu etkiler ancak @table_variableel değmemiş halde bırakır .
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
Kerestecilik
Her ikisi de tempdbişlem günlüğüne günlük kayıtları oluşturur . Yaygın bir yanılgı, bunun tablo değişkenleri için geçerli olmadığı, bu yüzden bunu gösteren bir betik olduğunu, bir tablo değişkeni bildirdiğini, birkaç satır eklediğini ve bunları güncellediğini ve siler.
Tablo değişkeni kümenin başlangıcında ve sonunda örtük bir şekilde oluşturulduğundan ve bırakıldığından, tam kayıt işlemini görmek için birden çok kümenin kullanılması gerekir.
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
İade


Her ikisinde de işlemleri görebildiğim kadarıyla, kabaca eşit miktarda günlük kaydı üretiyorum.
Günlüğe kaydetme miktarının çok benzer olmasına rağmen, önemli bir fark, #temptablolarla ilgili günlük kayıtlarının , herhangi bir kullanıcı işlemi bitene kadar silinememesidir; bu nedenle, belirli bir noktada #temptablolara yazan uzun süredir devam eden bir işlem tempdb, özerk işlemlerde günlük kesilmesini önleyecektir. Tablo değişkenleri için üretilmez.
Tablo değişkenleri desteklemiyor, TRUNCATEbu nedenle gereksinim bir tablodaki tüm satırları kaldırmak olduğunda bir günlük dezavantajda olabilir (çok küçük tablolar DELETE için yine de daha iyi sonuç verebilir )
kardinalite
Tablo değişkenlerini içeren uygulama planlarının çoğu, bunlardan çıktı olarak tahmin edilen tek bir satır gösterecektir. Tablo değişkeni özelliklerinin incelenmesi, SQL Server'ın tablo değişkeninin sıfır satır olduğuna inandığını göstermektedir (Neden 1 satırın sıfır satırlı bir tablodan yayılacağını tahmin ediyor? Burada @ Paul White tarafından açıklanmıştır ).
Bununla birlikte, önceki bölümde gösterilen sonuçlar, içinde doğru bir rowssayımı göstermektedir sys.partitions. Sorun, çoğu zaman, tablo boşken tablo değişkenlerine başvuruda bulunan ifadelerin derlenmesidir. Eğer deyim @table_variabledoldurulduktan sonra (yeniden) derlenirse , bu durum bunun yerine tablo kardinalitesi için kullanılacaktır (Bu, açık bir recompilesebepten dolayı olabilir veya belki de ifade, ertelenmiş bir derlemeye veya yeniden derlemeye neden olan başka bir nesneye de başvurduğu için olabilir).
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
Plan, ertelenmiş derlemeyi takiben doğru tahmini satır sayısını gösterir.

SQL Server 2012 SP2'de, 2453 izleme bayrağı tanıtıldı. Daha fazla detay burada "İlişkisel Motor" altında .
Bu izleme bayrağı etkinleştirildiğinde, otomatik olarak yeniden derlenmelerin çok kısa bir süre tartışıldığı gibi değiştirilmiş önemliliği hesaba katmasına neden olabilir.
Not: Uyumluluk seviyesindeki Azure'da, ifadenin 150'nin derlenmesi ilk uygulamaya kadar ertelenir . Bu, artık sıfır satır tahmini soruna tabi olmayacağı anlamına gelir.
Sütun istatistiği yok
Daha kesin bir tablo kardinalitesine sahip olmak, tahmini satır sayısının daha kesin olacağı anlamına gelmez (tablodaki tüm satırlarda bir işlem yapmazsanız). SQL Server, tablo değişkenleri için sütun istatistiklerini hiç tutmaz; bu nedenle, karşılaştırma tahminine dayanan tahminlere geri dönecektir (örneğin, tablonun% 10'unun =benzersiz olmayan bir sütuna karşı döndürülmesi veya bir >karşılaştırma için% 30 olması ). Buna karşılık sütun istatistikleri ,#temp tablolar için korunur .
SQL Server, her sütuna yapılan değişikliklerin sayısını tutar. Planın derlenmesinden sonraki değişikliklerin sayısı yeniden derleme eşiğini (RT) aşarsa, plan yeniden derlenecek ve istatistikler güncellenecektir. RT, masa tipine ve boyutuna bağlıdır.
Gönderen SQL Server 2008 yılında Planı Caching
RT aşağıdaki gibi hesaplanır. (n, bir sorgu planı derlendiğinde bir tablonun önem derecesine karşılık gelir.)
Kalıcı tablo
- Eğer n <= 500, RT = 500.
- n> 500 ise, RT = 500 + 0.20 * n.Geçici tablo
- eğer n <6, RT = 6 ise
- - 6 <= n <= 500, RT = 500
ise . - n> 500 ise, RT = 500 + 0,20 * n.
Tablo değişkeni
- RT mevcut değil. Bu nedenle, tablo değişkenlerinin önem derecelerindeki değişiklikler nedeniyle yeniden derlemeler gerçekleşmez. (Ancak aşağıdaki TF 2453 ile ilgili nota bakınız)
KEEP PLANipucu için RT ayarlamak için kullanılabilir #tempsürekli tabloları için olanla aynıdır tablo.
Tüm bunların net etkisi, #temptablolar için oluşturulan yürütme planlarının çoğu @table_variableszaman SQL Server'ın çalışacak daha iyi bilgiye sahip olması gibi birçok satıra dahil edilenden daha büyük boyutlar siparişleri olmasıdır .
Not: Tablo değişkenleri istatistiklere sahip değildir ancak yine de 2453 izleme bayrağı altında "İstatistik Değişikliği" yeniden derleme olayına neden olabilir ("önemsiz" planlar için geçerli değildir) eğer bir tane daha N=0 -> RT = 1. yani, tablo değişkeni boşken derlenen tüm ifadeler, boş TableCardinalityolmadıklarında ilk çalıştırıldığında yeniden derlenecek ve düzeltilecektir . Derleme zaman çizelgesi kardinalitesi planda saklanır ve eğer ifade aynı kardinalite ile tekrar yürütülürse (kontrol ifadelerinin akışı nedeniyle veya önbelleğe alınmış bir planın yeniden kullanılması nedeniyle) tekrar derleme yapılmaz.
NB2: Saklı yordamlardaki önbelleğe alınmış geçici tablolar için, yeniden derleme öyküsü yukarıda tarif edilenden çok daha karmaşıktır. Tüm kanlı detaylar için Saklı Prosedürlerde Geçici Tablolara bakınız.
recompiles
Yukarıda açıklanan değişikliklere dayalı yeniden derleme işlemlerinin yanı sıra, yalnızca derleme tetikleyen tablo değişkenleri için yasaklanmış işlemlere izin verdikleri için (örneğin DDL değişiklikleri , ) #temptabloları ek derlemelerle de ilişkilendirebilirsiniz.CREATE INDEXALTER TABLE
Kilitleme
Belirtildiği tablo değişkenleri kilitleme katılmak kalmamasıdır. Durum bu değil. Aşağıdaki çıktıların çalıştırılması, SSMS mesajları sekmesine bir insert ifadesi için alınan ve bırakılan kilitlerin ayrıntılarını gösterir.
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)
SELECTTablo değişkenlerinden gelen sorgular için Paul White, yorumların bunların otomatik olarak gizli bir NOLOCKipucu ile geldiğine işaret eder. Bu aşağıda gösterilmiştir
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 )
Bununla birlikte, bunun kilitlenme üzerindeki etkisi oldukça az olabilir.
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
Bu dönüşlerin hiçbiri, SQL Server'ın her ikisi için bir tahsisat siparişi taraması kullandığını belirten dizin anahtarı sırasına göre sonuçlanmaz .
Yukarıdaki betiği iki kez koştum ve ikinci çalıştırma için sonuçlar aşağıda
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
Tablo değişkeni için kilitleme çıktısı gerçekten de asgari düzeydedir, çünkü SQL Server yalnızca nesne üzerinde bir şema kararlılık kilidi alır. Ancak, bir #tempmasa için neredeyse bir nesne seviyesi Skilidini çıkartması kadar hafif . Tabii ki tablolarla çalışırken de bir NOLOCKipucu veya READ UNCOMMITTEDizolasyon seviyesi açıkça belirtilebilir #temp.
Çevredeki bir kullanıcı işleminin günlüğe kaydedilmesiyle benzer şekilde, kilitlerin #temptablolar için daha uzun tutulması anlamına gelebilir . Aşağıdaki komut dosyasıyla
--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
Her iki durumda da açık bir kullanıcı işleminin dışında çalıştırıldığında, kontrol edildiğinde döndürülen tek kilit sys.dm_tran_locks, üzerinde paylaşılan bir kilit olur DATABASE.
Açılmadan önce BEGIN TRAN ... ROLLBACK26 satır, kilitlerin geri çekilmeye izin vermek ve diğer işlemlerin kaydedilmemiş verileri okumasını önlemek için hem nesnenin hem de sistem tablosu satırlarının üzerinde tutulduğunu göstererek döndürülür. Eşdeğer tablo değişkeni işlemi, kullanıcı işlemiyle geri alma işlemine tabi değildir ve bir sonraki ifadeyi kontrol etmemiz için bu kilitleri tutmamıza gerek yoktur, ancak Profiler'de edinilen ve çıkarılan veya izleme bayrağını 1200 kullanarak izlenen kilitlerin hala çok sayıda kilitleme olayı olduğunu gösterir. meydana gelmek.
endeksleri
SQL Server 2014'ten önceki sürümler için, dizinler yalnızca benzersiz bir kısıtlama veya birincil anahtar eklemenin yan etkisi olarak tablo değişkenleri üzerinde örtük olarak yaratılabilir. Bu elbette yalnızca benzersiz dizinlerin desteklendiği anlamına gelir. Benzersiz bir kümelenmiş dizine sahip bir tablodaki benzersiz bir kümelenmemiş dizin, ancak basitçe bildirerek UNIQUE NONCLUSTEREDve CI anahtarını istenen NCI anahtarının sonuna ekleyerek simüle edilebilir (SQL Server, benzersiz olmasa bile , sahnelerin arkasında bunu yapardı) NCI belirtilebilir)
Daha önce de gösterildiği gibi index_option, kısıtlama bildirgesinde , ve de dahil olmak üzere DATA_COMPRESSION, çeşitli s belirtilebilir IGNORE_DUP_KEY, FILLFACTOR( ve bunun sadece indeks yeniden oluşturma üzerinde herhangi bir fark yaratacağı için bir ayar yapmanın bir anlamı yoktur ve tablo değişkenleri üzerindeki indeksleri yeniden oluşturamazsınız!)
Ek olarak, tablo değişkenleri INCLUDEd sütunları, filtrelenmiş dizinleri (2016 yılına kadar) veya bölümlemeyi desteklemez, #temptablolar bunu yapar (bölüm şeması içinde oluşturulmalıdır tempdb).
SQL Server 2014'te Endeksler
Benzersiz olmayan dizinler, SQL Server 2014'teki tablo değişken tanımında satır içi olarak bildirilebilir. Bunun için örnek sözdizimi aşağıdadır.
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*/
);
SQL Server 2016'daki Endeksler
CTP 3.1'den, tablo değişkenleri için filtrelenmiş indeksleri bildirmek artık mümkün. RTM tarafından o olabilir sütunları da onlar olsa izin verilir dahil dava olması muhtemel kaynak sıkıntıları nedeniyle SQL16 içine yapmaz
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
paralellik
İçine giren (veya başka şekilde değiştiren) sorguların @table_variablesparalel bir planı #temp_tablesolamaz, bu şekilde sınırlandırılmaz.
Aşağıdaki yeniden yazmada, SELECTparçanın paralel olarak gerçekleşmesine izin veren, ancak gizli bir geçici tablo kullanarak (sahnelerin arkasında) sona eren açık bir geçici çözüm vardır.
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
Cevabımda gösterildiği gibi tablo değişkenlerinden seçilen sorgularda böyle bir sınırlama yoktur.
Diğer İşlevsel Farklılıklar
#temp_tablesbir fonksiyon içerisinde kullanılamaz. @table_variablesskaler veya çoklu tablo tablosu UDF'lerinde kullanılabilir.@table_variables adlandırılmış kısıtlamalar olamaz.@table_variablesedilemez SELECT-ed INTO, ALTER, -ed TRUNCATEd veya bir hedef DBCCgibi komutlar DBCC CHECKIDENTya da SET IDENTITY INSERTve bu şekilde tablo ipuçlarını desteklemezWITH (FORCESCAN) CHECK Tablo değişkenlerindeki kısıtlamalar, sadeleştirici, ima edilen tahminler veya çelişki tespiti için optimize edici tarafından dikkate alınmaz.PAGELATCH_EXbeklemeye neden olabilir. ( Örnek )Sadece hafıza?
Başlangıçta belirtildiği gibi her ikisi de sayfalarda saklanır tempdb. Ancak bu sayfaları diske yazmaya gelince davranışlarda herhangi bir fark olup olmadığını ele almadım.
Şimdi bunun üzerinde az miktarda test yaptım ve şimdiye kadar böyle bir fark görmedim. Benim örneğimde yaptığım testte SQL Server 250 sayfa veri dosyası yazılmadan önce kesme noktası gibi görünüyor.
Not: Aşağıdaki davranış artık SQL Server 2014 veya SQL Server 2012 SP1 / CU10 veya SP2 / CU1’de gerçekleşmemektedir. SQL Server 2014'teki bu değişiklik hakkında daha fazla ayrıntı : tempdb Gizli Performans Gem .
Aşağıdaki betiği çalıştırıyorum
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
Ve tempdbMonitor Monitor ile veri dosyasına yazma olduğunu görmedim (hiçbiri 73.728'deki veri tabanı önyükleme sayfasına olanlar hariç). Değiştirdikten sonra 250hiç 251I aşağıdaki gibi yazıyor görmeye başladık.

Yukarıdaki ekran görüntüsü 5 * 32 sayfa yazar ve bir sayfanın 161'inin diske yazıldığını gösteren tek bir sayfa yazar. Tablo değişkenleriyle de test ederken 250 sayfadaki kesme noktasını da aldım. Aşağıdaki betiğe bakarak farklı bir yol gösteriliyor.sys.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
192 sayfanın diske yazıldığını ve kirli bayrağın temizlendiğini göstererek. Ayrıca, diske yazılmasının, sayfaların hemen tampon havuzundan çıkarılacağı anlamına gelmediğini gösterir. Bu tablo değişkenine karşı sorgular hala tamamen bellekten sağlanmış olabilir.
Tampon Havuz Sayfalarına max server memoryayarlanmış 2000 MBve DBCC MEMORYSTATUSraporlanmış boşta bir sunucuda Yaklaşık 1.843.000 KB (yaklaşık 23.000 sayfa) olarak tahsis edilmiştir. Yukarıdaki tablolara 1.000 satır / sayfalık gruplar halinde ve kaydedilen her yineleme için ekledim.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
Hem tablo değişkeni hem de #temptablo neredeyse aynı grafikler verdi ve tamamen bellekte tutulmadıkları noktaya gelmeden önce tampon havuzunu azami seviyeye çıkarmayı başardılar; bu nedenle ne kadar bellek için belirli bir sınırlama olmadığı görülüyor ya tüketebilir.

Çalışmaktan ziyade belirli deneyimlere dayanarak belirtmek istediğim birkaç şey var. DBA olarak çok yeniyim, lütfen beni gerektiği yerde düzeltin.