SQL Server'da temp table ve table değişkeni arasındaki fark nedir?


Yanıtlar:


668

içindekiler

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

Detaylı görünüm

Sonuçların ekran görüntüsü

Özet Görünümü (örtük düşüş ve sistem tabanı tabloları için günlük kaydı içerir)

Sonuçların ekran görüntüsü

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.

Doğru 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)

Çıktı

*** 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.
  • Tablo değişkenleri satır kümesi optimizasyonu için uygun görünmüyor, yani bunlara karşı planları silmek ve güncellemek daha fazla ek yüke ve 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.

ProcMon

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 

Sonuçlar

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.

Tampon Havuzu'ndaki Sayfalar


SQL Server'ın geçici tabloları (önbelleğe alma ile bile) eşdeğer bir tablo değişkenine göre oluştururken önemli ölçüde daha fazla mandal aldığını buldum. Latch_acquired XE hata ayıklamasını kullanarak ve yaklaşık 35 sütun içeren bir tablo oluşturarak test edebilirsiniz. Tablo değişkenini 4 mandal alacak şekilde, geçici tablonun ise 70 mandal etrafında bir yere götüreceğini gördüm.
Joe Obbish

40

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

  1. #temp tabloları varsayılan olarak SQL Server örneğinin varsayılan harmanlamasını kullanır. Bu nedenle, aksi belirtilmediği sürece, eğer masterdb'nin veritabanından farklı bir harmanlaması varsa, #temp tabloları ve veritabanı tabloları arasındaki değerleri karşılaştırma ya da güncelleme problemleriyle karşılaşabilirsiniz. Bakınız: http://www.mssqltips.com/sqlservertip/2440/create-sql-server-temporary-tables-with-the-correct-collation/
  2. Tamamen kişisel tecrübeye dayanarak, mevcut hafızanın daha iyi performans gösteren bir etkisi olduğu görülüyor. MSDN, daha küçük sonuç kümelerini depolamak için tablo değişkenlerini kullanmanızı önerir, ancak çoğu zaman fark bile fark edilmez. Bununla birlikte, daha büyük kümelerde, tablo değişkenlerinin çok daha fazla bellek yoğun olduğu ve sorguyu bir taramaya kadar yavaşlatabileceği bazı durumlarda belirginleşti.

6
Ayrıca #temp tablolarda bu harmanlama not edebilirsiniz SQL Server 2012 kullanıyorsanız ve veritabanı yer ise çağıran veritabanının harmanlamayı devralır.
Aaron Bertrand

Küçük ve büyük setler için # 2 ile ilgili açıklama stackoverflow.com/a/14465163/5224021
GibralterTop
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.