it-swarm-eu.dev

Jaký je rozdíl mezi dočasnou tabulkou a proměnnou tabulky v SQL Serveru?

Zdá se, že se jedná o oblast s několika mýty a protichůdnými názory.

Jaký je tedy rozdíl mezi proměnnou tabulky a místní dočasnou tabulkou na serveru SQL?

459
Martin Smith

Obsah

Contents

Upozornění

Tato odpověď pojednává o „klasických“ tabulkových proměnných zavedených v SQL Server 2000. SQL Server 2014 v paměti OLTP zavádí typy tabulek optimalizovaných pro paměť. Instance proměnných tabulky se v mnoha ohledech liší od diskutovaných) níže ( více podrobností ).

Umístění úložiště

Žádný rozdíl. Oba jsou uloženy v tempdb.

Viděl jsem, že to naznačuje, že u proměnných tabulky tomu tak není vždy, ale lze to ověřit níže

DECLARE @T TABLE(X INT)

INSERT INTO @T VALUES(1),(2)

SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T

Příklad výsledků (zobrazující umístění v tempdb 2 řádky jsou uloženy)

File:Page:Slot
----------------
(1:148:0)
(1:148:1)

Logické umístění

@table_variables Se chovají více, jako by byly součástí aktuální databáze, než tabulky #temp. Pro sloupcové sloupcové proměnné (od roku 2005) bude sloupcové řazení, pokud není výslovně uvedeno, stejné jako u aktuální databáze, zatímco pro tabulky #temp Použije výchozí řazení tempdb ( Další podrobnosti ). Uživatelsky definované typy dat a kolekce XML musí být v tempdb, aby mohly být použity pro tabulky #temp, Ale proměnné tabulky je mohou použít z aktuální databáze ( Source ).

SQL Server 2012 představuje obsažené databáze. chování dočasných tabulek v těchto se liší (h/t Aaron)

V uzavřené databázi jsou data dočasných tabulek seřazena do řazení uzavřené databáze.

  • Všechna metadata spojená s dočasnými tabulkami (například názvy tabulek a sloupců, indexy atd.) Budou ve třídění katalogů.
  • Pojmenovaná omezení nelze použít v dočasných tabulkách.
  • Dočasné tabulky se nemusí vztahovat na uživatelem definované typy, kolekce schémat XML ani na uživatelem definované funkce.

Viditelnost do různých rozsahů

@table_variables Lze získat pouze v rámci dávky a rozsahu, ve kterém jsou deklarovány. #temp_tables Jsou přístupné v rámci podřízených šarží (vnořené spouště, procedura, volání exec). #temp_tables Vytvořený ve vnějším oboru (@@NESTLEVEL=0) Může také překrývat šarže, protože přetrvávají až do ukončení relace. Žádný typ objektu nelze vytvořit v podřízené dávce a přistupovat k němu ve volajícím rozsahu, jak je popsáno dále (globální ##temp Tabulky mohou být).

Životnost

@table_variables Jsou vytvářeny implicitně, když je spuštěna dávka obsahující příkaz DECLARE @.. TABLE (Před spuštěním jakéhokoli uživatelského kódu v této dávce) a na konci jsou implicitně zrušena.

Ačkoli vám analyzátor neumožňuje vyzkoušet a použít proměnnou tabulky před příkazem DECLARE, implicitní vytvoření je vidět níže.

IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END

--Works fine
SELECT *
FROM @T

#temp_tables Jsou vytvářeny explicitně, když je zaznamenán TSQL CREATE TABLE Příkaz a lze je explicitně zrušit pomocí DROP TABLE Nebo budou implicitně zrušeny po ukončení dávky (pokud byly vytvořeny v podřízené dávce s @@NESTLEVEL > 0) Nebo když relace skončí jinak.

Pozn .: V rámci uložených rutin oba typy objektů lze uložit do mezipaměti , spíše než opakovaně vytvářet a rušit nové tabulky. Existují určitá omezení, kdy může dojít k tomuto ukládání do mezipaměti, ale je možné je porušit pro #temp_tables, Ale kterým omezením @table_variables Přesto zabránit. Režijní náklady pro tabulky #temp V mezipaměti jsou mírně větší než pro proměnné tabulky jak je zde znázorněno .

Metadata objektu

To je v podstatě stejné pro oba typy objektů. Je uložen v základních tabulkách systému v tempdb. Je jednodušší vidět tabulku #temp, Protože OBJECT_ID('tempdb..#T') může být použito pro vložení do systémových tabulek a interně generovaný název je těsněji korelován s názvem definovaným v [$ CREATE TABLE = CODE!] Prohlášení. Pro tabulkové proměnné funkce object_id Nefunguje a interní název je generován výhradně systémem bez vztahu k názvu proměnné. Níže ukazuje, že metadata jsou stále přítomna zadáním názvu (doufejme, jedinečného) sloupce. U tabulek bez jedinečných názvů sloupců lze object_id určit pomocí DBCC PAGE , pokud nejsou prázdné.

/*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'

Výstup

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                  |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+

Transakce

Operace na @table_variables Jsou prováděny jako systémové transakce, nezávislé na jakékoli transakci externího uživatele, zatímco ekvivalentní operace #temp Tabulky by byly prováděny jako součást samotné uživatelské transakce. Z tohoto důvodu bude příkaz ROLLBACK ovlivňovat tabulku #temp, Ale @table_variable Zůstane nedotčen.

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

Protokolování

Oba generují záznamy protokolu do protokolu transakcí tempdb. Běžná mylná představa je, že tomu tak není v případě proměnných tabulky, takže skript prokazující, že je níže, deklaruje proměnnou tabulky, přidá několik řádků, poté je aktualizuje a odstraní.

Protože proměnná tabulky je vytvořena a zrušena implicitně na začátku a na konci dávky, je nutné použít více šarží, aby bylo možné zobrazit úplné protokolování.

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 

Vrací se

Detailní pohled

Screenshot of results

Souhrnné zobrazení (zahrnuje protokolování pro implicitní drop a základní tabulky systému)

Screenshot of results

Pokud jsem byl schopen rozeznat operace na obou generují zhruba stejné množství protokolování.

Zatímco množství protokolování je velmi podobné, jeden důležitý rozdíl spočívá v tom, že záznamy protokolu související s tabulkami #temp Nemohou být vymazány, dokud žádná transakce obsahující uživatele nedokončí tak dlouho trvající transakci, která v určitém okamžiku zápisy do tabulek #temp zabrání zkrácení protokolu v tempdb, zatímco autonomní transakce vytvořené pro proměnné tabulky nikoli.

Proměnné tabulky nepodporují TRUNCATE, takže mohou být v nevýhodě protokolování, když je vyžadováno odebrání všech řádků z tabulky (i když u velmi malých tabulek DELETEpřesto to může fungovat lépe) )

kardinálnost

Mnoho plánů provádění zahrnujících proměnné tabulky zobrazí jediný řádek odhadovaný jako výstup z nich. Kontrola vlastností proměnné tabulky ukazuje, že SQL Server věří, že proměnná tabulky má nulové řádky (Proč je odhadováno, že 1 řádek bude emitován z tabulky nulových řádků) od @Paul White zde ).

Výsledky zobrazené v předchozí části však ukazují přesný počet rows v sys.partitions. Problém je, že ve většině případů jsou příkazy odkazující na proměnné tabulky kompilovány, zatímco je tabulka prázdná. Pokud je příkaz (znovu) zkompilován po naplnění @table_variable, Bude místo toho použito pro kardinálnost tabulky (k tomu může dojít v důsledku explicitního recompile nebo snad proto, že příkaz také odkazuje na jiný objekt což způsobí odložené kompilace nebo kompilace.)

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

Plán ukazuje přesný odhadovaný počet řádků po odloženém kompilaci.

Shows accurate row count

V SQL Server 2012 SP2 je zaveden příznak trasování 2453. Více podrobností najdete v části „Relational Engine“ zde .

Je-li tento příznak trasování povolen, může to způsobit, že automatické rekompilace vezmou v úvahu změnou mohutnost, jak bude velmi brzy probráno.

Pozn .: Na Azure v úrovni kompatibility 150 je kompilace příkazu nyní odložena do prvního spuštění . To znamená, že již nebude předmětem problému s odhadem nulové řady.

Žádné statistiky sloupců

Přesnější kardinálnost tabulky však neznamená, že odhadovaný počet řádků bude přesnější (pokud však neprovedete operaci na všech řádcích v tabulce). SQL Server vůbec neudržuje statistiku sloupců pro proměnné tabulky, takže se vrátí zpět k odhadům založeným na predikátu porovnání (např. 10% tabulky bude vráceno za = Proti neobvyklému sloupci nebo 30% pro srovnání >). V tabulkách kontrastů jsou udržovány tabulky #temp.

SQL Server udržuje počet změn provedených v každém sloupci. Pokud počet úprav od kompilace plánu překročí prahovou hodnotu rekompilace (RT), bude plán překompilován a statistiky aktualizovány. Hodnota RT závisí na typu a velikosti tabulky).

Od Plánování mezipaměti v SQL Server 2008

RT se vypočte následujícím způsobem. (n odkazuje na mohutnost tabulky při kompilaci plánu dotazů.)

Stálá tabulka
- Pokud n <= 500, RT = 500.
- Pokud n> 500, RT = 500 + 0,20 * n.

Dočasná tabulka
- Pokud n <6, RT = 6.
- Pokud 6 <= n <= 500, RT = 500.
- Pokud n> 500, RT = 500 + 0,20 * n.
Tabulka proměnná
- RT neexistuje). K opakování proto nedochází kvůli změnám v kardinalitě proměnných tabulky. (Ale viz poznámka o TF 2453 níže)

nápovědu KEEP PLAN lze použít k nastavení tabulek RT pro #temp tabulky stejné jako pro trvalé tabulky.

Čistý efekt toho všeho je, že často jsou plány provádění generované pro tabulky #temp Řádově lepší než pro @table_variables, Když je zapojeno mnoho řádků, protože SQL Server má lepší informace pro práci.

NB1: Proměnné tabulky nemají statistiku, ale stále mohou nastat pod názvem trasovacího příznaku 2453 událost „Statistics Changed“ (neplatí pro „triviální“ plány). Zdá se, že k tomu dochází pod stejnými prahovými hodnotami rekompilace, jaké jsou uvedeny u dočasných tabulek výše s další, pokud N=0 -> RT = 1. tj. všechny příkazy zkompilované, když je proměnná tabulky prázdná, skončí získáním kompilace a opravou TableCardinality při prvním spuštění, když nejsou prázdné. Kardinalita kompilační časové tabulky je uložena v plánu a pokud je příkaz spuštěn znovu se stejnou kardinálností (buď kvůli toku kontrolních příkazů nebo opětovnému použití plánu uloženého v mezipaměti), nenastane žádná kompilace.

Poznámka 2: U dočasných tabulek uložených v mezipaměti uložených procedur je příběh rekompilace mnohem komplikovanější, než je popsáno výše. Viz Dočasné tabulky v uložených procedurách pro všechny krvavé detaily.

Rekompilace

Stejně jako výše popsané rekompily založené na modifikacích popsané výše #temp Tabulky mohou být také spojeny s další kompilace jednoduše proto, že umožňují operace, které jsou zakázány pro proměnné tabulky, které spouštějí kompilaci (např. DDL změny CREATE INDEX, ALTER TABLE)

Zamykání

bylo uvedeno , že proměnné tabulky se neúčastní zamykání. Toto není ten případ. Spuštění níže uvedených výstupů na kartě SSMS zprávy obsahuje podrobnosti o uzamčených a uvolněných zámcích pro příkaz insert.

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)

Pro dotazy, které SELECT z proměnných tabulky, Paul White v komentářích upozorňuje, že tyto automaticky přicházejí s implicitní NOLOCK nápovědou. To je znázorněno níže

DECLARE @T TABLE(X INT); 

SELECT X
FROM @T 
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)

Výstup

*** Output Tree: (trivial plan) ***

        PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002  Hints( NOLOCK )

Dopad toho na uzamykání však může být docela malý.

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

Žádný z těchto návratů nemá za následek pořadí klíčů indexu, což znamená, že SQL Server použil alokace objednané kontroly pro oba.

Spustil jsem výše uvedený skript dvakrát a výsledky pro druhé spuštění jsou níže

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 

Výstup zamykání pro proměnnou tabulky je skutečně extrémně minimální, protože SQL Server pouze získává zámek stability schématu na objektu. Ale pro tabulku #temp Je téměř stejně lehké, že odstraňuje zámek na úrovni objektu S. Při práci s tabulkami READ UNCOMMITTED Lze samozřejmě explicitně určit úroveň izolace NOLOCK nebo #temp.

Podobně jako při protokolování může okolní uživatelská transakce znamenat, že zámky jsou drženy déle pro tabulky #temp. Se skriptem níže

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

při spuštění mimo výslovnou transakci uživatele v obou případech je jediným zámkem vráceným při kontrole sys.dm_tran_locks sdílený zámek na DATABASE.

Při odkomentování BEGIN TRAN ... ROLLBACK Je vráceno 26 řádků, které ukazují, že zámky jsou drženy jak na samotném objektu, tak na řádcích systémových tabulek, aby bylo možné vrátit zpět a zabránit jiným transakcím ve čtení nepřijatých dat. Ekvivalentní operace s proměnnou tabulky nepodléhá návratu s transakcí uživatele a není třeba tyto zámky držet, abychom je mohli zkontrolovat v dalším příkazu, ale trasování zámků získaných a uvolněných v Profiler nebo pomocí příznaku trasování 1200 ukazuje, že stále existuje mnoho zámkových událostí nastat.

Indexy

U verzí před SQL Server 2014 mohou být indexy vytvořeny pouze implicitně na proměnných tabulky jako vedlejší účinek přidání jedinečného omezení nebo primárního klíče. To samozřejmě znamená, že jsou podporovány pouze jedinečné indexy. Neunikající neklastrovaný index v tabulce s jedinečným klastrovaným indexem lze simulovat jednoduchým prohlášením UNIQUE NONCLUSTERED A přidáním klíče CI na konec požadovaného klíče NCI (SQL Server by do to i přesto za scénami , i když lze specifikovat nejedinečnou NCI)

Jak bylo ukázáno dříve, různé index_option S mohou být specifikovány v deklaraci omezení včetně DATA_COMPRESSION, IGNORE_DUP_KEY A FILLFACTOR (ačkoli nemá smysl nastavovat jeden protože by to jen změnilo opětovné vytvoření indexu a nemůžete znovu vytvořit indexy u proměnných tabulky!)

Proměnné tabulky navíc nepodporují sloupce INCLUDE d, filtrované indexy (do roku 2016) ani rozdělení oddílů, tabulky #temp Ano (schéma oddílů musí být vytvořeno v tempdb).

Indexy v SQL Server 2014

Ne jedinečné indexy lze deklarovat vložené v definici proměnné tabulky v SQL Server 2014. Příklad syntaxe je níže.

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*/
);

Indexy v SQL Server 2016

Z CTP 3.1 je nyní možné deklarovat filtrované indexy pro proměnné tabulky. Do RTM it může v případě, že zahrnuté sloupce jsou také povoleny, i když jsou pravděpodobně to nezmění na SQL16 kvůli omezením zdrojů =

DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)

Parallelism

Dotazy, které vkládají do (nebo jinak upravují) @table_variables, Nemohou mít paralelní plán, #temp_tables Nejsou tímto způsobem omezeny.

V tomto přepisu je zřejmé řešení, které umožňuje, aby část SELECT probíhala paralelně, ale to skončilo použitím skryté dočasné tabulky (za scénami)

INSERT INTO @DATA ( ... ) 
EXEC('SELECT .. FROM ...')

V dotazech neexistuje takové omezení, které vybere z proměnných tabulky jak je znázorněno v mé odpovědi zde

Ostatní funkční rozdíly

  • #temp_tables Nelze použít uvnitř funkce. @table_variables Lze použít uvnitř UDF skalárních nebo multi-příkazových tabulek.
  • @table_variables Nemůže mít pojmenovaná omezení.
  • @table_variables Nemůže být SELECT - ed INTO, ALTER - ed, TRUNCATE d nebo být cílem DBCC příkazy jako DBCC CHECKIDENT nebo SET IDENTITY INSERT a nepodporují rady v tabulce, jako je například WITH (FORCESCAN)
  • CHECK omezení na proměnné tabulky nejsou optimalizátorem zvažována pro zjednodušení, implikované predikáty nebo detekci rozporů.
  • Nezdá se, že by proměnné tabulky splňovaly podmínky optimalizace sdílení řádků , což znamená, že plány mazání a aktualizace proti nim mohou narůstat více režijních nákladů a čeká PAGELATCH_EX. ( Příklad )

Pouze paměť?

Jak bylo uvedeno na začátku, oba se uloží na stránky v tempdb. Neřešil jsem však, zda došlo k nějakému rozdílu v chování, pokud jde o zápis těchto stránek na disk.

Teď jsem na tom provedl malé množství testů a zatím jsem žádný takový rozdíl neviděl. Ve specifickém testu, který jsem provedl na své instanci stránek SQL Server 250, se zdá být mezní bod, než se datový soubor zapíše.

Poznámka: K níže uvedenému chování již nedochází v SQL Server 2014 nebo SQL Server 2012 SP1/CU10 nebo SP2/CU1 dychtivý zapisovatel již není tak dychtivý zapisovat stránky na disk. Další podrobnosti o této změně najdete na SQL Server 2014: tempdb Hidden Performance Gem .

Spuštění skriptu níže

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

A monitorování zapisuje do datového souboru tempdb s Process Monitor, který jsem žádný neviděl (s výjimkou příležitostných na spouštěcí stránku databáze na offset 73 728). Po změně 250 Na 251 Jsem začal vidět spisy, jak je uvedeno níže.

ProcMon

Snímek nahoře ukazuje 5 * 32 zápisů stránek a jeden zápis jedné stránky, což znamená, že 161 stránek bylo zapsáno na disk. Při testování pomocí proměnných tabulky jsem dostal stejný mezní bod 250 stránek. Níže uvedený skript ukazuje jiný způsob pohledu na 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 

Výsledek

is_modified page_count
----------- -----------
0           192
1           61

Ukazuje, že na disk bylo zapsáno 192 stránek a špinavá vlajka byla vymazána. Ukazuje také, že zápis na disk neznamená, že stránky budou okamžitě vyklizeny z fondu vyrovnávacích pamětí. Dotazy proti této proměnné tabulky lze stále plně uspokojit z paměti.

Na nečinném serveru s max server memory Nastaveným na 2000 MB A DBCC MEMORYSTATUS Vykazující stránky s vyrovnávací pamětí přidělené jako přibližně 1 843 000 KB (cca 23 000 stránek) jsem vložil do výše uvedených tabulek v dávkách 1 000 řádků/stránek a pro každou zaznamenanou iteraci.

SELECT Count(*)
FROM   sys.dm_os_buffer_descriptors
WHERE  database_id = 2
       AND allocation_unit_id = @allocId
       AND page_type = 'DATA_PAGE' 

Proměnná tabulka i tabulka #temp Daly téměř identické grafy a dokázaly do značné míry vytěsnit fond vyrovnávacích pamětí, než se dostaly do bodu, kdy nebyly zcela uchovány v paměti, takže se nezdá, že by existovaly nějaké zvláštní omezení toho, kolik paměti může spotřebovat.

Pages in Buffer Pool

682
Martin Smith

Je několik věcí, na které bych rád upozornil spíše na konkrétní zkušenosti než na studium. Jako DBA jsem velmi nová, takže mě prosím v případě potřeby opravte.

  1. #temp tabulky ve výchozím nastavení používají výchozí řazení instance serveru SQL. Pokud tedy není uvedeno jinak, můžete narazit na problémy s porovnáváním nebo aktualizací hodnot mezi tabulkami #temp a databázovými tabulkami, pokud má masterdb jiné řazení než databáze. Viz: http://www.mssqltips.com/sqlservertip/2440/create-sql-server-temporary-tables-with-the-correct-collation/
  2. Zdá se, že dostupná paměť je zcela založena na osobních zkušenostech a má vliv na lepší výkon. MSDN doporučuje používat proměnné tabulky pro ukládání menších sad výsledků, ale většinou není rozdíl ani patrný. Ve větších sadách je však v některých případech zřejmé, že proměnné tabulky jsou mnohem náročnější na paměť a mohou dotaz zpomalit na procházení.
41
Kahn