sql >> Databáze >  >> RDS >> Sqlserver

Výkon proměnných tabulky v SQL Server

V tomto článku se dotkneme tématu výkonu tabulkových proměnných. V SQL Serveru můžeme vytvářet proměnné, které budou fungovat jako kompletní tabulky. Možná, že jiné databáze mají stejné možnosti, ale já jsem takové proměnné použil pouze v MS SQL Server.

Můžete tedy napsat následující:

declare @t as table (int value)

Zde deklarujeme proměnnou @t jako tabulku, která bude obsahovat jeden sloupec Value typu Integer. Je možné vytvářet i složitější tabulky, ale v našem příkladu stačí k prozkoumání optimalizace jeden sloupec.

Nyní můžeme tuto proměnnou použít v našich dotazech. Můžeme do ní přidat mnoho dat a provádět načítání dat z této proměnné:

insert into @t
select UserID
from User
or
select * from @t

Všiml jsem si, že proměnné tabulky se používají, když je potřeba načíst data pro velký výběr. V kódu je například dotaz, který vrací uživatele webu. Nyní shromáždíte ID všech uživatelů, přidáte je do proměnné tabulky a můžete vyhledávat adresy těchto uživatelů. Možná se někdo může zeptat, proč neprovedeme jeden dotaz na databázi a nedostaneme vše hned? Mám jednoduchý příklad.

Předpokládejme, že uživatelé pocházejí z webové služby, zatímco jejich adresy jsou uloženy ve vaší databázi. V tomto případě není cesty ven. Ze služby jsme získali spoustu uživatelských ID a abychom se vyhnuli dotazování na databázi, někdo se rozhodl, že je jednodušší přidat všechna ID do parametru dotazu jako proměnnou tabulky a dotaz bude vypadat úhledně:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
os

To vše funguje správně. V kódu C# můžete rychle spojit výsledky obou datových polí do jednoho objektu pomocí LINQ. Výkon dotazu však může utrpět.

Faktem je, že tabulkové proměnné nebyly navrženy pro zpracování velkých objemů dat. Pokud se nemýlím, optimalizátor dotazů bude vždy používat metodu provádění LOOP. Pro každé ID z @t tedy dojde k hledání v tabulce adres. Pokud je v @t 1000 záznamů, server naskenuje adresu 1000krát.

Pokud jde o provádění, kvůli šílenému počtu skenování server jednoduše přestane hledat data.

Mnohem efektivnější je naskenovat celou tabulku adres a najít všechny uživatele najednou. Tato metoda se nazývá MERGE. SQL Server si jej však vybere, pokud existuje mnoho seřazených dat. V tomto případě optimalizátor neví, kolik a jaká data budou přidána do proměnné a zda existuje řazení, protože taková proměnná neobsahuje indexy.

Pokud je v proměnné tabulka málo dat a nevložíte do ní tisíce řádků, je vše v pořádku. Pokud však takové proměnné rádi používáte a přidáváte k nim obrovské množství dat, musíte pokračovat ve čtení.

I když proměnnou tabulky nahradíte SQL, výrazně to zrychlí výkon dotazů:

select *
from (
 Select 10377 as UserID
 Union all
 Select 73736
 Union all
 Select 7474748
 ….
  ) as users 
   join Address a on a.UserID = users.UserID

Takových příkazů SELECT může být tisíc a text dotazu bude obrovský, ale pro velké množství dat bude proveden tisíckrát rychleji, protože SQL Server si může vybrat efektivní plán provádění.

Tento dotaz nevypadá skvěle. Jeho plán provádění však nelze uložit do mezipaměti, protože změna pouze jednoho ID změní také celý text dotazu a nelze použít parametry.

Myslím, že Microsoft neočekával, že uživatelé budou používat tabulkové proměnné tímto způsobem, ale existuje pěkné řešení.

Existuje několik způsobů, jak tento problém vyřešit. Výkonově nejefektivnější je však dle mého názoru přidat OPTION (RECOMPILE) na konec dotazu:

select *
from @t as users 
   join Address a on a.UserID = users.UserID
OPTION (RECOMPILE)

Tato možnost je přidána jednou na úplný konec dotazu po ORDER BY. Účelem této možnosti je zajistit, aby SQL Server znovu zkompiloval dotaz při každém spuštění.

Pokud poté změříme výkon dotazu, čas na provedení vyhledávání se s největší pravděpodobností zkrátí. U velkých dat může být zlepšení výkonu výrazné, od desítek minut až po sekundy. Nyní server zkompiluje svůj kód před spuštěním každého dotazu a nepoužívá plán provádění z mezipaměti, ale vygeneruje nový, v závislosti na množství dat v proměnné, což obvykle hodně pomáhá.

Nevýhodou je, že plán provádění není uložen a server musí pokaždé zkompilovat dotaz a hledat účinný plán provádění. Neviděl jsem však dotazy, kde tento proces trval déle než 100 ms.

Je špatný nápad používat proměnné tabulky? Ne to není. Pamatujte, že nebyly vytvořeny pro velká data. Někdy je lepší vytvořit dočasnou tabulku, pokud je dat hodně, a vložit data do této tabulky nebo dokonce vytvořit index za běhu. Musel jsem to udělat se zprávami, i když pouze jednou. Tehdy jsem zkrátil čas na generování jednoho přehledu ze 3 hodin na 20 minut.

Dávám přednost použití jednoho velkého dotazu místo jeho rozdělení na několik dotazů a ukládání výsledků do proměnných. Umožněte SQL Serveru vyladit výkon velkého dotazu a nezklame vás. Vezměte prosím na vědomí, že byste se měli uchýlit k proměnným tabulky pouze v extrémních případech, kdy skutečně vidíte jejich výhody.


  1. Porovnání řešení Oracle RAC HA s Galera Cluster pro MySQL nebo MariaDB

  2. Ovladač HikariCP Postgresql tvrdí, že nepřijímá JDBC URL

  3. NASTAVIT NÁZVY utf8 v MySQL?

  4. Jak najít nejvýkonnější dotazy v SQL Server 2008?