Articles

UNION ALL Optimization

két vagy több adatkészlet összefűzése leggyakrabban T-SQL-ben fejeződik ki a UNION ALL záradék használatával. Tekintettel arra, hogy az SQL Server optimizer gyakran átrendezni a dolgokat, mint csatlakozik, valamint aggregátumok, hogy a teljesítmény javítása érdekében, ez teljesen reális, hogy az SQL Server is figyelembe vennék átrendezéséhez összefűzés bemenet, amennyiben ez előnyt. Például az optimalizáló figyelembe veheti a A UNION ALL B mint B UNION ALL Aátírásának előnyeit.

valójában az SQL Server optimizer ezt nem teszi meg. Pontosabban, volt némi korlátozott támogatást concatenation bemenet átrendezése SQL Server kiadások akár 2008 R2, de ezt eltávolították az SQL Server 2012, és nem bukkant fel azóta.

SQL Server 2008 R2

intuitív módon a konkatenációs bemenetek sorrendje csak akkor számít, ha van sorcél. Alapértelmezés szerint az SQL Server optimalizálja a végrehajtási terveket annak alapján, hogy az összes minősítő sor visszatér az ügyfélhez. Amikor egy sorcél van érvényben, az optimalizáló megpróbál olyan végrehajtási tervet találni, amely gyorsan elkészíti az első néhány sort.

A Sorcélok többféle módon állíthatók be, például a TOP, a FAST n lekérdezési tipp, vagy a EXISTS használatával (amelyet természeténél fogva legfeljebb egy sorban kell megtalálni). Ahol nincs sorcél (azaz az ügyfél minden sort igényel), általában nem számít, hogy a konkatenációs bemenetek melyik sorrendben olvasódnak: minden bemenet végül minden esetben teljes mértékben feldolgozásra kerül.

az SQL Server 2008 R2-ig terjedő verziók korlátozott támogatása akkor érvényes, ha pontosan egy sor célja van. Ebben a konkrét körülmények között az SQL Server átrendezi a konkatenációs bemeneteket a várható költségek alapján.

Ez nem a költségalapú optimalizálás során történik( ahogy az elvárható), hanem inkább a normál optimalizáló kimenet utolsó perces optimalizálási átírása. Ennek az elrendezésnek az az előnye, hogy nem növeli a költségalapú tervkeresési helyet (potenciálisan egy alternatíva minden lehetséges átrendezéshez), miközben még mindig olyan tervet készít, amely optimalizálva van az első sor gyors visszatéréséhez.

példák

a következő példák két azonos tartalmú táblát használnak: egy millió sor egész szám egytől egy millióig. Az egyik asztal egy halom, nem záródó indexekkel; a másiknak egyedi fürtözött indexe van:

CREATE TABLE dbo.Expensive( Val bigint NOT NULL); CREATE TABLE dbo.Cheap( Val bigint NOT NULL, CONSTRAINT UNIQUE CLUSTERED (Val));GOINSERT dbo.Cheap WITH (TABLOCKX) (Val)SELECT TOP (1000000) Val = ROW_NUMBER() OVER (ORDER BY SV1.number)FROM master.dbo.spt_values AS SV1CROSS JOIN master.dbo.spt_values AS SV2ORDER BY ValOPTION (MAXDOP 1);GOINSERT dbo.Expensive WITH (TABLOCKX) (Val)SELECT C.ValFROM dbo.Cheap AS COPTION (MAXDOP 1);

Nem Sorban Cél

A következő lekérdezést úgy, hogy ugyanazt a sort minden asztalon, majd visszatér az összefűzés a két szett:

SELECT E.Val FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005 UNION ALL SELECT C.ValFROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005;

A végrehajtási tervet készített, amelyet a lekérdezés-optimalizáló:

UNION ALL without a row goal

a figyelmeztetés a gyökér SELECT Üzemeltető figyelmezteti minket, hogy a nyilvánvaló hiányzó index a halom asztalra. A táblázat szkennelési operátorának figyelmeztetését a Sentry One Plan Explorer adja hozzá. Felhívja a figyelmünket a vizsgálat során rejtett maradék predikátum I/O költségére.

a Konkatenáció bemeneteinek sorrendje itt nem számít, mert nem állítottunk be sorcélt. Mindkét bemenet teljesen olvasható lesz az összes eredménysor visszaadásához. Érdekes (bár ez nem garantált) észreveszi, hogy a bemenetek sorrendje az eredeti lekérdezés szöveges sorrendjét követi. Vegye figyelembe azt is, hogy a végeredmény sorainak sorrendje sem kerül meghatározásra, mivel nem használtunk felső szintű ORDER BY záradékot. Feltételezzük, hogy ez szándékos, a végső megrendelés pedig lényegtelen a feladathoz.

ha megfordítjuk a lekérdezésben szereplő táblázatok írásbeli sorrendjét:

SELECT C.ValFROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005 UNION ALL SELECT E.Val FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005;

A végrehajtási tervet követi a változás elérése a fürtözött táblázat első (ez megint csak nem garantált): UNIÓ MINDEN fordított ráfordítások

a két lekérdezések is várható, hogy ugyanaz a teljesítmény jellemzők, mint ők végezze el ugyanazokat a műveleteket, csak más sorrendben.

egy sor cél

nyilvánvaló, hogy az indexelés hiánya a halom asztalon általában bizonyos sorok megtalálását drágábbá teszi, összehasonlítva a fürtözött asztalon végzett ugyanazon művelettel. Ha megkérdezzük az optimalizáló egy terv, hogy visszatér az első sorban gyorsan elvárjuk SQL Server átrendezni az összefűzés bemenet, így az olcsó fürtözött táblázat a megkérdezett első.

a heap táblát először megemlítő lekérdezés használatával, valamint egy gyors 1 lekérdezési tipp segítségével adja meg a sor célját:

SELECT E.Val FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005 UNION ALL SELECT C.ValFROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005OPTION (FAST 1);

A becsült végrehajtási tervet készített egy SQL Server 2008 R2: UNIÓ MINDEN egy sorban cél 2008 R2

Figyeljük meg, hogy az összefűzés bemenet más helyre került, hogy csökkentse a becsült költség az adatszolgáltató az első sorban. Vegye figyelembe azt is, hogy a hiányzó index és a maradék I/O figyelmeztetések eltűntek. Egyik kérdés sem következménye ennek a tervformának, amikor a cél az, hogy egy sort a lehető leggyorsabban visszaadjunk.

ugyanaz A lekérdezés ki az SQL Server 2016 (a számosság becslési modell) az, hogy:

UNIÓ MINDEN egy sorban cél a 2016

SQL Server 2016 nem reordered az összefűzés bemenet. A Plan Explorer I/O figyelmeztetés visszatért, de sajnos az optimalizáló ezúttal nem készített hiányzó index figyelmeztetést (bár releváns).

Általános átrendezés

mint már említettük, az optimalizálás utáni újraírás, amely átrendezi a konkatenációs bemeneteket, csak a következőkre hatékony:

  • SQL Server 2008 R2, illetve a korábbi
  • Egy sor cél pontosan egy

Ha valóban csak egy sort vissza, ahelyett, hogy egy terv optimalizált vissza az első sorban gyorsan (de ami végső soron mindig vissza sor), használhatjuk a TOP záradék a származtatott tábla vagy a közös asztal kifejezés (CTE):

SELECT TOP (1) UA.ValFROM( SELECT E.Val FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005 UNION ALL SELECT C.Val FROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005) AS UA;

A SQL Server 2008 R2 vagy korábban, igy az optimális reordered-input terv:

UNION ALL with TOP on 2008 R2

SQL Server 2012, 2014, and 2016 no post-optimization átrendezés történik:

UNION ALL with TOP on 2012-2016

Ha szeretnénk több mint egy sor vissza, például aTOP (2), a kívánt újraírást nem kell alkalmazni az SQL Server 2008 R2 akkor is, ha a FAST 1 tipp is használható. Ebben a helyzetben olyan trükköket kell használnunk, mint például a TOP változó és egy OPTIMIZE FOR tipp:

DECLARE @TopRows bigint = 2; -- Number of rows actually needed SELECT TOP (@TopRows) UA.ValFROM( SELECT E.Val FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005 UNION ALL SELECT C.Val FROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005) AS UAOPTION (OPTIMIZE FOR (@TopRows = 1)); -- Just a hint

a lekérdezési tipp elegendő egy sor céljának beállításához, míg a változó futási értéke biztosítja a kívánt sorok számát (2).

az SQL Server 2008 R2 tényleges végrehajtási terve a következő:

UNION ALL with variable and OPTIMIZE FOR on 2008 R2

mindkét visszaadott sor az újrarendelt Keresési bemenetből származik, és a táblázat beolvasása egyáltalán nem kerül végrehajtásra. Plan Explorer mutatja a sor számít piros, mert a becslés volt egy sorban (miatt a célzást), míg a két sor találkozott futási időben.

UNION ALL nélkül

Ez a probléma nem korlátozódik a UNION ALL – vel kifejezetten írt lekérdezésekre. Más konstrukciók, mint például a EXISTSés OR, szintén eredményezhetik az optimalizáló concatenation operátort, amely a bemeneti átrendezés hiányától szenvedhet. Volt egy friss kérdés adatbázis rendszergazdák Stack Exchange pontosan ezt a kérdést. Átalakítja a lekérdezés a kérdés, hogy használja a példa táblázatok:

SELECT CASE WHEN EXISTS ( SELECT 1 FROM dbo.Expensive AS E WHERE E.Val BETWEEN 751000 AND 751005 ) OR EXISTS ( SELECT 1 FROM dbo.Cheap AS C WHERE C.Val BETWEEN 751000 AND 751005 ) THEN 1 ELSE 0 END;

A végrehajtási tervet az SQL Server 2016 van a kupac táblázat az első bemenet: ESETBEN subquery a 2016

A SQL Server 2008 R2 a rend a bemenet van optimalizálva, hogy az tükrözze a sorban cél a félig csatlakozni:

ESETBEN subquery a 2008 R2

a több optimális terv, a halom vizsgálat nem is lehet végrehajtani.

Workarounds

bizonyos esetekben a lekérdezőíró számára nyilvánvaló lesz, hogy az egyik összefűzési bemenet mindig olcsóbb lesz a futtatáshoz, mint a többi. Ha ez igaz, akkor nagyon érvényes a lekérdezés átírása, hogy az olcsóbb összefűzési bemenetek először írásbeli sorrendben jelenjenek meg. Ez természetesen azt jelenti, hogy a lekérdező írónak tisztában kell lennie ezzel az optimalizáló korlátozással, és készen kell állnia arra, hogy dokumentálatlan viselkedésre támaszkodjon.

nehezebb kérdés merül fel, amikor a konkatenációs bemenetek költsége a körülményektől függ, talán a paraméterértékektől függően. Használata OPTION (RECOMPILE) nem segít az SQL Server 2012 vagy újabb. Ez az opció segíthet az SQL Server 2008 R2 vagy korábbi, de csak akkor, ha az egysoros cél követelmény is teljesül.

Ha vannak aggodalmak támaszkodva megfigyelt viselkedés (lekérdezési terv összefűzés bemenetek megfelelő a lekérdezés szöveges rend) egy terv útmutató lehet használni, hogy a hatályos terv alakja. Ahol a különböző bemeneti megbízások optimálisak a különböző körülményekhez, több tervvezetőt lehet használni, ahol a feltételek előre pontosan kódolhatók. Ez azonban aligha ideális.

végső gondolatok

az SQL Server query optimizer valójában tartalmaz egy költségalapú feltárási szabályt, UNIAReorderInputs, amely képes konkatenációs bemeneti rendelési variációk létrehozására és alternatívák feltárására a költségalapú optimalizálás során (nem egy lövés utáni optimalizálási újraírás).

Ez a szabály jelenleg nincs engedélyezve általános használatra. Amennyire meg tudom mondani, csak akkor aktiválódik, ha egy terv útmutató vagy USE PLAN tipp van jelen. Ez lehetővé teszi, hogy a motor sikeresen erő egy terv, hogy keletkezett egy lekérdezést, amely alkalmas a bemenet-átrendezéséhez átírni, még akkor is, ha az aktuális lekérdezés nem jogosult.

az az érzésem, hogy ez a felfedezés szabály szándékosan korlátozódik erre a célra, mert lekérdezések, hogy előnyös lenne a összefűzés bemeneti átrendezéséhez részeként költség alapú optimalizálás megállapítható, hogy nem kellően gyakori, vagy talán azért, mert fennáll a veszély, hogy az extra erőfeszítés, nem fizeti ki. Saját véleményem az, hogy a Konkatenációs operátor bemeneti átrendezését mindig fel kell tárni, amikor egy sor célja van érvényben.

szégyen, hogy a (korlátozottabb) optimalizálás utáni újraírás nem hatékony az SQL Server 2012 vagy újabb verziójában. Ennek oka lehet, hogy egy finom hiba, de nem találtam semmit erről a dokumentációban, Tudásbázis, vagy a Connect. Itt hozzáadtam egy új csatlakozási elemet.

2017. augusztus 9. frissítése: ezt most a 4199-es nyomkövetési jel alatt rögzítik az SQL Server 2014 és 2016 esetében, lásd KB 4023419:

FIX: Az UNION ALL és a row goal lekérdezése lassabb lehet az SQL Server 2014 vagy újabb verziókban, ha összehasonlítjuk az SQL Server 2008 R2