En av de tillgängliga algoritmerna för att sammanfoga två tabeller i SQL Server är Nested Loops. De kapslade loops-joinerna använder en join-ingång som den yttre inmatningstabellen och en som den inre indatatabellen. Den yttre slingan itererar den yttre inmatningstabellen rad för rad. Den inre slingan, som körs för varje yttre rad, söker efter matchande rader i den inre inmatningstabellen.
Detta kallas en naiv kapslad loop-join.
Om du har ett index på kopplingsvillkor i den inre inmatningstabellen, är det inte nödvändigt att utföra en inre loop för varje rad i den yttre tabellen. Istället kan du skicka värdet från den externa tabellen som ett sökargument och koppla alla returnerade rader i den inre tabellen till raderna från den yttre tabellen.
Sökningen av den inre tabellen är en slumpmässig tillgång. SQL-servern, från och med version 2005, har batchsorteringsoptimering (förväxla inte med sorteringsoperatorn i batchläge för Columnstore-index). Syftet med optimeringen är att beställa söknycklar från den externa tabellen innan data hämtas från den interna. Således kommer en slumpmässig åtkomst att vara sekventiell.
Utförandeplanen visar inte batchsorteringsoperationen som en separat operatör. Istället kan du se egenskapen Optimized=true i operatören Nested Loops. Om det vore möjligt att se satssortering som en separat operatör i planen skulle det se ut så här:
I denna pseudoplan läser vi data från det icke-klustrade ix_CustomerID-indexet i ordningen för denna CustomerID-indexnyckel. Sedan måste vi utföra Key Lookup i det klustrade indexet, eftersom ix_CustomerID inte är ett täckande index. Key Lookup är en klustrad indexnyckelsökning – en slumpmässig åtkomst. För att göra det sekventiellt kan SQL Server utföra batchsortering med den klustrade indexnyckeln.
För att lära dig mer om satssortering, se min artikel Satssortering och kapslade slingor.
Denna optimering ger ett stort uppsving med ett tillräckligt antal rader. Du kan läsa mer om dess testresultat i bloggen OPTIMIZED Nested Loops Joins, skapad av Craig Freedman, en optimizerutvecklare.
Men om det faktiska antalet rader är färre än det förväntade, kan ytterligare kostnader för CPU för att bygga denna sort dölja dess fördelar, öka CPU-förbrukningen och minska dess prestanda.
Tänk på det här specifika exemplet:
use tempdb; go -- create a test table (SalesOrderID - clustered PK) create table dbo.SalesOrder(SalesOrderID int identity primary key, CustomerID int not null, SomeData char(200) not null); go -- add test data with n as (select top(1000000) rn = row_number() over(order by (select null)) from sys.all_columns c1,sys.all_columns c2) insert dbo.SalesOrder(CustomerID, SomeData) select rn%500000, str(rn,100) from n; -- create a clustered index create index ix_c on dbo.Salesorder(CustomerID); go -- the batch sort optimization is enabled by default (Nested Loops: Optimized = true) select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000; -- disable it with the DISABLE_OPTIMIZED_NESTED_LOOP hint (Nested Loops: Optimized = false) select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP')); go
Det returnerade resultatet:
Jag skulle vilja göra dig uppmärksam på den olika ordningen på rader i utgången. Servern returnerar rader i den ordning den behandlar dem eftersom vi inte uttryckligen har specificerat ORDER BY. I det första fallet läser vi gradvis från ix_c-indexet. Men för att optimera slumpmässiga avläsningar från det klustrade indexet, filtrerar vi rader efter den klustrade SalesOrderID-indexnyckeln. I det andra fallet finns det ingen sortering, och läsningar görs i CustomerID-nyckelordningen för det icke-klustrade indexet ix_c.
Skillnad från 2340-spårningsflaggan
Trots att dokumentationen anger 2340-spårningsflaggan som en motsvarighet till DISABLE_OPTIMIZED_NESTED_LOOP-tipset, är det faktiskt inte sant.
Tänk på följande exempel där jag kommer att använda kommandot UPDATE STATISTICS ... WITH PAGECOUNT undocumented för att lura optimeraren genom att säga att tabellen tar fler sidor än den faktiskt har. Ta sedan en titt på dessa frågor:
- Utan några tips (jag har lagt till MAXDOP för att hålla en enkel plan);
- Med DISABLE_OPTIMIZED_NESTED_LOOP-tipset;
- Med 2340-spårningsflaggan.
-- create a huge table update statistics dbo.SalesOrder with pagecount = 100000; go set showplan_xml on; go -- 1. without hints select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(maxdop 1); -- 2. hint select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP'), maxdop 1); -- 3. trace flag select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(querytraceon 2340, maxdop 1); go set showplan_xml off; go
Som ett resultat har vi följande planer:
Kapslade loopar har egenskapen Optimized =false i alla tre planerna. Faktum är att genom att öka tabellbredden ökar vi också kostnaden för dataåtkomst. När kostnaden är tillräckligt hög kan SQL Server använda den explicita sorteringsoperatorn snarare än den implicita batchsorteringsoperatorn. Vi kan se detta i den första frågeplanen.
I den andra frågan använde vi DISABLE_OPTIMIZED_NESTED_LOOP-tipset som stänger av den implicita batchsorteringen. Det tar dock bort en explicit sortering av en separat operatör.
I den tredje planen kan vi se att sorteringsoperatorn existerar trots att vi lägger till 2340-spårningsflaggan.
Skillnaden mellan ledtråden och flaggan är alltså följande:en ledtråd inaktiverar optimeringen genom att omvandla en slumpmässig åtkomst till en seriell beroende på om servern implementerar den med den implicita batchsorteringen eller med den separata sorteringsoperatören.
P.S. Planerna kan bero på utrustningen. Därför, om du misslyckas med att köra dessa frågor, försök att öka eller minska storleken på SomeData char(200)-kolumnen i dbo.SalesOrder-tabellbeskrivningen.
Artikeln översattes av Codingsight-teamet med tillstånd från författaren.