Vaše otázka ukazuje, že jste podlehli některým běžným mylným představám o proměnných tabulek a dočasných tabulkách.
Napsal jsem poměrně rozsáhlou odpověď na stránce DBA, kde jsem se podíval na rozdíly mezi těmito dvěma typy objektů. To také řeší vaši otázku ohledně disku vs paměti (neviděl jsem žádný významný rozdíl v chování mezi těmito dvěma).
Pokud jde o otázku v názvu, kdy použít proměnnou tabulky vs místní dočasnou tabulku, nemáte vždy na výběr. Ve funkcích je například možné použít pouze proměnnou tabulky a pokud potřebujete zapisovat do tabulky v podřízeném rozsahu, pak pouze #temp
tabulka udělá (parametry s hodnotou tabulky umožňují přístup pouze pro čtení).
Pokud máte na výběr, některé návrhy jsou uvedeny níže (ačkoli nejspolehlivější metodou je jednoduše otestovat obojí s vaší konkrétní pracovní zátěží).
-
Pokud potřebujete index, který nelze vytvořit na proměnné tabulky, budete samozřejmě potřebovat
#temporary
stůl. Podrobnosti jsou však závislé na verzi. Pro SQL Server 2012 a nižší byly jediné indexy, které bylo možné vytvořit pro proměnné tabulky, ty implicitně vytvořené prostřednictvímUNIQUE
neboPRIMARY KEY
omezení. SQL Server 2014 zavedl inline syntaxi indexu pro podmnožinu možností dostupných vCREATE INDEX
. To bylo od té doby rozšířeno, aby umožnilo podmínky filtrovaného indexu. Indexuje pomocíINCLUDE
-d sloupce nebo indexy sloupců však stále není možné vytvořit na proměnných tabulky. -
Pokud budete opakovaně přidávat a odstraňovat velké množství řádků z tabulky, použijte
#temporary
stůl. To podporujeTRUNCATE
(což je efektivnější nežDELETE
pro velké tabulky) a další následné vložení zaTRUNCATE
mohou mít lepší výkon než ty, které následují poDELETE
jak je znázorněno zde. - Pokud budete mazat nebo aktualizovat velký počet řádků, může dočasná tabulka fungovat mnohem lépe než proměnná tabulky – pokud je schopna používat sdílení sady řádků (příklad viz „Efekty sdílení sady řádků“ níže) .
- Pokud se optimální plán pomocí tabulky bude lišit v závislosti na datech, použijte
#temporary
stůl. To podporuje vytváření statistik, které umožňují dynamickou rekompilaci plánu podle dat (ačkoli u dočasných tabulek uložených v mezipaměti v uložených procedurách je třeba chování při rekompilaci chápat samostatně). - Pokud je nepravděpodobné, že by se optimální plán pro dotaz využívající tabulku někdy změnil, můžete zvážit proměnnou tabulky, abyste přeskočili režii vytváření statistik a překompilování (možná bude vyžadovat nápovědu k opravě požadovaného plánu).
- Pokud zdroj dat vložených do tabulky pochází z potenciálně drahého
SELECT
potom zvažte, že použití proměnné tabulky zablokuje možnost tohoto použití paralelního plánu. - Pokud potřebujete, aby data v tabulce přežila vrácení transakce vnějšího uživatele, použijte proměnnou tabulky. Možným případem použití může být protokolování průběhu různých kroků v dlouhé dávce SQL.
- Při použití
#temp
tabulka uvnitř uživatelského transakčního zámku může být držena déle než u proměnných tabulky (potenciálně až do konce transakce vs. konec příkazu v závislosti na typu zámku a úrovni izolace) a také může zabránit zkrácenítempdb
transakční protokol, dokud uživatelská transakce neskončí. To by tedy mohlo upřednostňovat použití proměnných tabulky. - V rámci uložených rutin lze do mezipaměti ukládat jak proměnné tabulky, tak dočasné tabulky. Údržba metadat pro proměnné tabulky uložené v mezipaměti je menší než pro
#temporary
tabulky. Bob Ward poukazuje ve svétempdb
prezentace, že to může způsobit další spory o systémových tabulkách za podmínek vysoké souběžnosti. Navíc při práci s malým množstvím dat to může mít měřitelný rozdíl ve výkonu.
Efekty sdílení sady řádků
DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);
CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);
INSERT INTO @T
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2
SET STATISTICS TIME ON
/*CPU time = 7016 ms, elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;
/*CPU time = 6234 ms, elapsed time = 7236 ms.*/
DELETE FROM @T
/* CPU time = 828 ms, elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;
/*CPU time = 672 ms, elapsed time = 980 ms.*/
DELETE FROM #T
DROP TABLE #T