Önbellek (tembel makara) CTE sonucunu önbelleğe almak için bir plan kılavuzu oluşturun


19

Ben normalde ilk önce doğru planı kullanan bir sorgu inşa ederek ve bunu değil benzer sorguya kopyalayarak plan kılavuzları oluşturmak. Ancak, özellikle sorgu tam olarak aynı değilse, bu bazen zordur. Sıfırdan plan kılavuzları oluşturmanın doğru yolu nedir?

SQLKiwi, SSIS planlarını belirtti, SQL Server için iyi bir plan düzenlemeye yardımcı olmak için bir yol veya yararlı bir araç var mı?

Söz konusu örnek şu CTE'dir : SQLFiddle

with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;

Sonucu tam olarak 3 farklı guids ile ortaya çıkarmanın HERHANGİ bir yolu var mı ? Bazı SQL Server CTE tuhaflıkları aşmak için birden çok kez başvurulan CTE türü sorguları ile plan kılavuzları ekleyerek gelecekte soruları daha iyi cevap verebilmeyi umuyorum.


Yanıtlar:


14

Sonucu tam olarak 3 farklı kılavuzla ortaya çıkarmanın HERHANGİ bir yolu var mı? Bazı SQL Server CTE tuhaflıkları aşmak için birden çok kez başvurulan CTE türü sorguları ile plan kılavuzları ekleyerek gelecekte soruları daha iyi cevap verebilmeyi umuyorum.

Bugün değil. Özyinelemesiz ortak tablo ifadeleri (CTE'ler) satır içi görünüm tanımları olarak ele alınır ve optimizasyondan önce referans verildikleri her yerde (normal görünüm tanımları gibi) mantıksal sorgu ağacına genişletilir. Sorgunuz için mantıksal ağaç:

LogOp_OrderByCOL: Union1007 ASC COL: Union1015 ASC 
    LogOp_Project COL: Union1006 COL: Union1007 COL: Union1014 COL: Union1015
        LogOp_Join
            LogOp_ViewAnchor
                LogOp_UnionAll
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const

            LogOp_ViewAnchor
                LogOp_UnionAll
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const

Optimizasyon başlamadan önce iki Görünüm Bağlantısına ve iç fonksiyona yapılan altı çağrıya dikkat edin newid. Bununla birlikte, birçok kişi, optimize edicinin genişletilmiş alt ağaçların başlangıçta tek bir referans nesne olduğunu belirleyebileceğini ve buna göre basitleştirebileceğini düşünmektedir. Bir CTE'nin veya türetilmiş tablonun açık bir şekilde gerçekleştirilmesine izin vermek için birkaç Connect isteği de vardır .

Daha genel bir uygulama, optimize edicinin performansı artırmak için keyfi ortak ifadeleri gerçekleştirmeyi düşünmesini sağlayacaktır ( CASEbir alt sorgu ile sorunların bugün ortaya çıkabileceği başka bir örnektir ). Microsoft Research , 2007'de bu konuda bir makale (PDF) yayınladı, ancak bugüne kadar uygulanmadı. Şimdilik, tablo değişkenleri ve geçici tablolar gibi açık materyalizasyonla sınırlıyız.

SQLKiwi, SSIS planlarını belirtti, SQL Server için iyi bir plan düzenlemeye yardımcı olmak için bir yol veya yararlı bir araç var mı?

Bu sadece benim tarafımdan düşünmekti ve plan kılavuzlarını değiştirme fikrinin ötesine geçti. Prensip olarak, şov planı XML'sini doğrudan manipüle etmek için bir araç yazmak mümkündür, ancak aracı kullanmak için belirli bir optimize edici enstrüman olmadan kullanıcı için sinir bozucu bir deneyim olacaktır (ve geliştirici bunu düşünmeye gelir).

Bu sorunun özel bağlamında, böyle bir araç hala CTE içeriğini birden fazla tüketici tarafından kullanılabilecek şekilde gerçekleştiremez (bu durumda her iki girişi de çapraz birleştirmeye beslemek için). Optimize edici ve yürütme motoru, çok tüketici makaralarını destekler, ancak yalnızca belirli amaçlar için - bunların hiçbiri bu özel örneğe uygulanamaz.

Emin olmasam da, sorgu planla tam olarak aynı olmasa bile RelOps'un (İç İçe Döngü, Tembel Makara) izlenebileceği oldukça güçlü bir önsezim var - örneğin CTE'ye 4 ve 5 eklediyseniz , hala aynı planı kullanmaya devam ediyor (görünüşte - SQL Server 2012 RTM Express'te test edildi).

Burada makul bir esneklik var. XML planının geniş şekli, son bir planın aranmasına rehberlik etmek için kullanılır (ancak birçok özellik tamamen yok sayılır, örneğin borsalarda bölümleme türü) ve normal arama kuralları da oldukça rahatlar. Örneğin, maliyet değerlendirmelerine dayalı alternatiflerin erken budanması devre dışı bırakılır, çapraz eklemelerin açık bir şekilde girilmesine izin verilir ve skaler işlemler göz ardı edilir.

Orada derinlemesine girmek çok fazla ayrıntı vardır, ama Filtreler ve Compute Skalarların yerleştirme zorla edilemez ve formun yüklemler column = valuebir plan içeren böylece genelleştirilir X = 1veya X = @Xiçeren bir sorguya uygulanabilir X = 502ya X = @Y. Bu özel esneklik, zorlamak için doğal bir plan bulmada büyük ölçüde yardımcı olabilir.

Özel örnekte, sürekli Birlik Hepsi her zaman Sabit Tarama olarak uygulanabilir; Birliğe girişlerin sayısı önemli değildir.


3

CTE'nin her iki oluşumu için tek bir makarayı yeniden kullanmanın bir yolu yoktur (2012'ye kadar SQL Server sürümleri). Ayrıntılar SQLKiwi'nin cevabında bulunabilir. Ayrıca , CTE'yi iki kez gerçekleştirmenin iki yolu vardır; bu, sorgunun doğası için kaçınılmazdır. Her iki seçenek de 6 net net kılavuz sayısı ile sonuçlanır.

Martin'in bir CTE'ye rehberlik eden planla ilgili bir blogdaki Quassnoi'nin sitesine yaptığı bağlantı, bu soru için kısmi ilham kaynağı oldu. Bir korelasyonun birden çok kez değerlendirilmesine neden olabilmesine rağmen, sadece bir kez referans verilen ilişkili bir alt sorgu amacıyla bir CTE gerçekleştirmenin bir yolunu açıklar. Bu, sorudaki sorgu için geçerli değildir.

Seçenek 1 - Plan Kılavuzu

SQLKiwi'nin cevabından ipuçları alarak, kılavuzu hala işi yapacak çıplak bir minimum seviyeye indirdim, örneğin ConstantScandüğümler sadece herhangi bir sayıya yeterince genişleyebilen 2 skaler operatörü listeler.

;with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
OPTION(USE PLAN
N'<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.2" Build="11.0.2100.60" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="1" StatementEstRows="1600" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.0444433" StatementText="with cte(guid,other) as (&#xD;&#xA;  select newid(),1 union all&#xD;&#xA;  select newid(),2 union all&#xD;&#xA;  select newid(),3&#xD;&#xA;select a.guid, a.other, b.guid guidb, b.other otherb&#xD;&#xA;from cte a&#xD;&#xA;cross join cte b&#xD;&#xA;order by a.other, b.other;&#xD;&#xA;" StatementType="SELECT" QueryHash="0x43D93EF17C8E55DD" QueryPlanHash="0xF8E3B336792D84" RetrievedFromCache="true">
          <StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
          <QueryPlan NonParallelPlanReason="EstimatedDOPIsOne" CachedPlanSize="96" CompileTime="13" CompileCPU="13" CompileMemory="1152">
            <MemoryGrantInfo SerialRequiredMemory="0" SerialDesiredMemory="0" />
            <OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="157240" EstimatedPagesCached="1420" EstimatedAvailableDegreeOfParallelism="1" />
            <RelOp AvgRowSize="47" EstimateCPU="0.006688" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="1600" LogicalOp="Inner Join" NodeId="0" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.0444433">
              <OutputList>
                <ColumnReference Column="Union1163" />
              </OutputList>
              <Warnings NoJoinPredicate="true" />
              <NestedLoops Optimized="false">
                <RelOp AvgRowSize="27" EstimateCPU="0.000432115" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Sort" NodeId="1" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0117335">
                  <OutputList>
                    <ColumnReference Column="Union1080" />
                    <ColumnReference Column="Union1081" />
                  </OutputList>
                  <MemoryFractions Input="0" Output="0" />
                  <Sort Distinct="false">
                    <OrderBy>
                      <OrderByColumn Ascending="true">
                        <ColumnReference Column="Union1081" />
                      </OrderByColumn>
                    </OrderBy>
                    <RelOp AvgRowSize="27" EstimateCPU="4.0157E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Constant Scan" NodeId="2" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="4.0157E-05">
                      <OutputList>
                        <ColumnReference Column="Union1080" />
                        <ColumnReference Column="Union1081" />
                      </OutputList>
                      <ConstantScan>
                        <Values>
                          <Row>
                            <ScalarOperator ScalarString="newid()">
                              <Intrinsic FunctionName="newid" />
                            </ScalarOperator>
                            <ScalarOperator ScalarString="(1)">
                              <Const ConstValue="(1)" />
                            </ScalarOperator>
                          </Row>
                          <Row>
                            <ScalarOperator ScalarString="newid()">
                              <Intrinsic FunctionName="newid" />
                            </ScalarOperator>
                            <ScalarOperator ScalarString="(2)">
                              <Const ConstValue="(2)" />
                            </ScalarOperator>
                          </Row>
                        </Values>
                      </ConstantScan>
                    </RelOp>
                  </Sort>
                </RelOp>
                <RelOp AvgRowSize="27" EstimateCPU="0.0001074" EstimateIO="0.01" EstimateRebinds="0" EstimateRewinds="39" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Lazy Spool" NodeId="83" Parallel="false" PhysicalOp="Table Spool" EstimatedTotalSubtreeCost="0.0260217">
                  <OutputList>
                    <ColumnReference Column="Union1162" />
                    <ColumnReference Column="Union1163" />
                  </OutputList>
                  <Spool>
                    <RelOp AvgRowSize="27" EstimateCPU="0.000432115" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Sort" NodeId="84" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0117335">
                      <OutputList>
                        <ColumnReference Column="Union1162" />
                        <ColumnReference Column="Union1163" />
                      </OutputList>
                      <MemoryFractions Input="0" Output="0" />
                      <Sort Distinct="false">
                        <OrderBy>
                          <OrderByColumn Ascending="true">
                            <ColumnReference Column="Union1163" />
                          </OrderByColumn>
                        </OrderBy>
                        <RelOp AvgRowSize="27" EstimateCPU="4.0157E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Constant Scan" NodeId="85" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="4.0157E-05">
                          <OutputList>
                            <ColumnReference Column="Union1162" />
                            <ColumnReference Column="Union1163" />
                          </OutputList>
                          <ConstantScan>
                            <Values>
                              <Row>
                                <ScalarOperator ScalarString="newid()">
                                  <Intrinsic FunctionName="newid" />
                                </ScalarOperator>
                                <ScalarOperator ScalarString="(1)">
                                  <Const ConstValue="(1)" />
                                </ScalarOperator>
                              </Row>
                              <Row>
                                <ScalarOperator ScalarString="newid()">
                                  <Intrinsic FunctionName="newid" />
                                </ScalarOperator>
                                <ScalarOperator ScalarString="(2)">
                                  <Const ConstValue="(2)" />
                                </ScalarOperator>
                              </Row>
                            </Values>
                          </ConstantScan>
                        </RelOp>
                      </Sort>
                    </RelOp>
                  </Spool>
                </RelOp>
              </NestedLoops>
            </RelOp>
          </QueryPlan>
        </StmtSimple>
      </Statements>
    </Batch>
  </BatchSequence>
</ShowPlanXML>'
);

Seçenek 2 - Uzaktan Tarama

Sorgunun masrafını artırarak ve bir Uzaktan Tarama ekleyerek, sonuç gerçekleşir.

with cte(guid,other) as (
  select *
  from OPENQUERY([TESTSQL\V2012], '
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3') x)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;

2

Tüm ciddiyetle, xml yürütme planlarını sıfırdan kesemezsiniz. Onları SSIS kullanarak yaratmak bilim kurgu. Evet hepsi XML, ama farklı evrenlerden. Paul'un bu konudaki bloguna bakarken, "SSIS'in izin verdiği ölçüde ..." diyor, muhtemelen yanlış anladınız mı? "Planlar oluşturmak için SSIS kullan" dediğini düşünmüyorum, daha ziyade " SSIS gibi bir sürükle ve bırak arayüzünü kullanarak planlar oluşturmak harika olmaz " dedi. Belki, çok basit bir sorgu için, bunu hemen yönetebilirsiniz, ancak bu bir streç, hatta muhtemelen zaman kaybıdır. Söyleyebileceğiniz yoğun iş.

Bir KULLANIM PLANI ipucu veya plan kılavuzu için bir plan oluşturuyorsam, birkaç yaklaşımım var. Örneğin, istatistikleri etkilemek ve optimize ediciyi farklı bir karar vermeye teşvik etmek için kayıtları tablolardan kaldırabilirim (örn. Db'nin bir kopyasında). Ayrıca optimizer her tablo 1 kayıt içerdiğini düşünüyor sorgudaki tüm tablo yerine tablo değişkenleri kullandım. Ardından oluşturulan planda, tüm tablo değişkenlerini orijinal tablo adlarıyla değiştirin ve plan olarak değiştirin. Başka bir seçenek, veri tabanlarının yalnızca istatistik kopyalarını kopyalarken kullanılan yöntem olan parodi istatistiklerini güncellemek için UPDATE STATISTICS 'in WITH STATS_STREAM seçeneğini kullanmak olacaktır.

UPDATE STATISTICS 
    [dbo].[yourTable]([PK_yourTable]) 
WITH 
    STATS_STREAM = 0x0100etc, 
    ROWCOUNT = 10000, 
    PAGECOUNT = 93

Geçmişte xml yürütme planları ile tinkering biraz zaman geçirdim ve sonunda, SQL sadece "ben kullanmıyorum" gider ve yine de nasıl istediği sorguyu çalıştırdığını bulduk.

Belirli bir örnek için, bu sonucu almak için sorguda set rowcount 3 veya TOP 3 kullanabileceğinizi bildiğinizden eminim, ama sanırım bu sizin amacınız değil. Doğru cevap gerçekten şöyle olacaktır: Geçici bir tablo kullanın. Ben doğru bir cevap değil:) Doğru bir cevap "zaten bile işe yaramayacak CTE için tembel bir biriktirme yapmak için optimize edici kandırmak için kendi özel XML yürütme planı keserek saatler bile gün geçirmek olurdu, zeki görünecek aynı zamanda sürdürülmesi de imkansız olurdu ".

Orada yapıcı olmaya çalışmıyorum, sadece benim fikrim - yardımcı olacağını umuyorum.


Cidden, XML Planları cahil mi?!, Bütün mesele olduğunu düşündüm? Geçersiz ise atmalıdır.
crokusek

Plan Kılavuzu Başarısız etkinliğine atıfta bulunuyordum.
wBob

2

HERHANGİ BİR yolu var mı ...

Son olarak SQL 2016 CTP 3.0'da bir yol vardır:

İzleme bayrağı ve Dmitry Pilugin tarafından ayrıntılı olarak Genişletilmiş Olaylar kullanarak burada , sen (biraz keyfi) sorgu yürütme ara aşamalarından üç benzersiz GUID'lerine dışarı balık.

Not: Bu kod, CTE planı zorlamasıyla ilgili üretim veya ciddi kullanım için DEĞİLDİR , sadece yeni bir izleme bayrağına açık yürekli bir bakış ve farklı şeyler yapmanın bir yolu:

-- Configure the XEvents session; with ring buffer target so we can collect it
CREATE EVENT SESSION [query_trace_column_values] ON SERVER 
ADD EVENT sqlserver.query_trace_column_values
ADD TARGET package0.ring_buffer( SET max_memory = 2048 )
WITH ( MAX_MEMORY = 4096 KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY = 30 SECONDS, MAX_EVENT_SIZE = 0 KB, MEMORY_PARTITION_MODE = NONE, TRACK_CAUSALITY = OFF , STARTUP_STATE = OFF )
GO

-- Start the session
ALTER EVENT SESSION [query_trace_column_values] ON SERVER
STATE = START;
GO

-- Run the query, including traceflag
DBCC TRACEON(2486);
SET STATISTICS XML ON;
GO

-- Original query
;with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
option ( recompile )
go

SET STATISTICS XML OFF;
DBCC TRACEOFF(2486);
GO

DECLARE @target_data XML

SELECT @target_data = CAST( target_data AS XML )
FROM sys.dm_xe_sessions AS s 
    INNER JOIN sys.dm_xe_session_targets AS t ON t.event_session_address = s.address
WHERE s.name = 'query_trace_column_values'


--SELECT @target_data td

-- Arbitrarily fish out 3 unique guids from intermediate stage of the query as collected by XEvent session
;WITH cte AS
(
SELECT
    n.c.value('(data[@name = "row_id"]/value/text())[1]', 'int') row_id,
    n.c.value('(data[@name = "column_value"]/value/text())[1]', 'char(36)') [guid]
FROM @target_data.nodes('//event[data[@name="column_id"]/value[. = 1]][data[@name="row_number"]/value[. < 4]][data[@name="node_name"]/value[. = "Nested Loops"]]') n(c)
)
SELECT *
FROM cte a
    CROSS JOIN cte b
GO

-- Stop the session
ALTER EVENT SESSION [query_trace_column_values] ON SERVER
STATE = STOP;
GO

-- Drop the session
IF EXISTS ( select * from sys.server_event_sessions where name = 'query_trace_column_values' )
DROP EVENT SESSION [query_trace_column_values] ON SERVER 
GO

Sürüm (CTP3.2) - 13.0.900.73 (x64) üzerinde test edildi, sadece eğlence için.


1

Traceflag 8649'un (paralel paralel plan) 2008, R2 ve 2012 örneklerimdeki sol kılavuz sütun için bu davranışı tetiklediğini buldum. CTE'nin doğru davrandığı SQL 2005'te bayrağı kullanmama gerek yoktu. SQL 2005'te oluşturulan planı daha yüksek örneklerde kullanmayı denedim ama geçerli olmaz.

with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
option ( querytraceon 8649 )

İpucu kullanarak, ipucu içeren bir plan kılavuzu kullanarak veya sorgu tarafından oluşturulan planı bir USE PLAN vb. cte newid


Tekrar denediğiniz için teşekkürler. Sorgu, 2008/2012 tarihinde bu izleme bayrağıyla veya bu izleme bayrağı olmadan farklı görünmüyor. SQL Server örneklerimin veya göstermeye çalıştığınız şeyin gerçekten olduğundan emin değilim. Hala 18 rehber görüyorum. Ne görüyorsun?
孔夫子

Sol tarafta 3 ayrı kılavuz (kılavuz sütun), her biri üç kez tekrarlanır. Sağ tarafta 9 benzersiz kılavuz (guidb sütun), bu yüzden en azından sol bit lol nasıl istediğinizi davranıyor. Umarım biraz açıklığa kavuşturmak için diğer cevaba bir resim ekledim. Küçük adımlar. SQL 2005'te de not etmeliyim, solda 3, sağda 3 benzersiz kılavuz var.
wBob

Ayrıca, 'hepsini' kaldırmanın her biri 6 olmak üzere 6 benzersiz kılavuzu da aldığını fark ettim.
wBob

Sunucu maxdop 1 ile traceflag çalışamaz hale getirebilir
wBob
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.