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 ( tempdb
2 satırdaki yerleri gösterir)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Mantıksal Konum
@table_variables
Geçerli veritabanının bir parçasıymış gibi #temp
tablolardan 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 #temp
tablolar için varsayılan harmanlamayı kullanır tempdb
( Daha fazla ayrıntı ). Ek olarak, kullanıcı tanımlı veri tipleri ve XML koleksiyonları #temp
tablolar 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_variables
yalnızca bildirildikleri parti ve kapsam dahilinde erişilebilir. #temp_tables
alt gruplar içinde erişilebilir (iç içe tetikleyiciler, prosedür, exec
çağrılar). #temp_tables
dış 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 ##temp
tablolar olabilir ).
Ömür
@table_variables
Bir DECLARE @.. TABLE
ifade 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_tables
TSQL CREATE TABLE
deyimine rastlandığında ve açıkça bırakılabiliyorsa açıkça DROP TABLE
yaratılır @@NESTLEVEL > 0
veya 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_tables
ancak @table_variables
yine de kısıtlamaların engelleneceği konusunda kısıtlamalar vardır . Önbelleğe alınmış #temp
tablolar 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 #temp
tabloyu 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 TABLE
ifadede tanımlanan isimle daha yakından ilişkilidir . Tablo değişkenleri için object_id
iş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 PAGE
boş 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_variables
herhangi bir dış kullanıcı işleminden bağımsız olarak sistem işlemleri olarak gerçekleştirilirken, eşdeğer #temp
tablo işlemleri kullanıcı işleminin bir parçası olarak gerçekleştirilecektir. Bu sebeple bir ROLLBACK
komut bir #temp
tabloyu etkiler ancak @table_variable
el 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 tempdb
iş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, #temp
tablolarla ilgili günlük kayıtlarının , herhangi bir kullanıcı işlemi bitene kadar silinememesidir; bu nedenle, belirli bir noktada #temp
tablolara 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, TRUNCATE
bu 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 rows
sayı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_variable
doldurulduktan sonra (yeniden) derlenirse , bu durum bunun yerine tablo kardinalitesi için kullanılacaktır (Bu, açık bir recompile
sebepten 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 PLAN
ipucu için RT ayarlamak için kullanılabilir #temp
sürekli tabloları için olanla aynıdır tablo.
Tüm bunların net etkisi, #temp
tablolar için oluşturulan yürütme planlarının çoğu @table_variables
zaman 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ş TableCardinality
olmadı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 , ) #temp
tabloları ek derlemelerle de ilişkilendirebilirsiniz.CREATE INDEX
ALTER 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)
SELECT
Tablo değişkenlerinden gelen sorgular için Paul White, yorumların bunların otomatik olarak gizli bir NOLOCK
ipucu 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 #temp
masa için neredeyse bir nesne seviyesi S
kilidini çıkartması kadar hafif . Tabii ki tablolarla çalışırken de bir NOLOCK
ipucu veya READ UNCOMMITTED
izolasyon seviyesi açıkça belirtilebilir #temp
.
Çevredeki bir kullanıcı işleminin günlüğe kaydedilmesiyle benzer şekilde, kilitlerin #temp
tablolar 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 ... ROLLBACK
26 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 NONCLUSTERED
ve 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 INCLUDE
d sütunları, filtrelenmiş dizinleri (2016 yılına kadar) veya bölümlemeyi desteklemez, #temp
tablolar 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_variables
paralel bir planı #temp_tables
olamaz, bu şekilde sınırlandırılmaz.
Aşağıdaki yeniden yazmada, SELECT
parç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_tables
bir fonksiyon içerisinde kullanılamaz. @table_variables
skaler veya çoklu tablo tablosu UDF'lerinde kullanılabilir.@table_variables
adlandırılmış kısıtlamalar olamaz.@table_variables
edilemez SELECT
-ed INTO
, ALTER
, -ed TRUNCATE
d veya bir hedef DBCC
gibi komutlar DBCC CHECKIDENT
ya da SET IDENTITY INSERT
ve 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_EX
beklemeye 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 tempdb
Monitor 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 250
hiç 251
I 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 memory
ayarlanmış 2000 MB
ve DBCC MEMORYSTATUS
raporlanmış 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 #temp
tablo 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.