Penso che potrei avere una spiegazione parziale per questo, ma sentiti libero di abbatterlo o pubblicare eventuali alternative. @MartinSmith è decisamente interessato a qualcosa evidenziando l'effetto di TOP nel piano di esecuzione.
In parole povere, "Conteggio righe effettive" non è un conteggio delle righe elaborate da un operatore, è il numero di volte in cui viene chiamato il metodo GetNext () dell'operatore.
Tratto da BOL :
Gli operatori fisici inizializzano, raccolgono dati e chiudono. In particolare, l'operatore fisico può rispondere alle seguenti tre chiamate di metodo:
- Init (): il metodo Init () consente a un operatore fisico di inizializzare se stesso e impostare le strutture di dati richieste. L'operatore fisico può ricevere molte chiamate Init (), sebbene in genere un operatore fisico ne riceva solo una.
- GetNext (): il metodo GetNext () fa sì che un operatore fisico ottenga la prima o la successiva riga di dati. L'operatore fisico può ricevere zero o più chiamate GetNext ().
- Close (): il metodo Close () fa sì che un operatore fisico esegua alcune operazioni di pulizia e si spenga. Un operatore fisico riceve solo una chiamata Close ().
Il metodo GetNext () restituisce una riga di dati e il numero di volte che viene chiamato viene visualizzato come ActualRows nell'output Showplan prodotto utilizzando SET STATISTICS PROFILE ON o SET STATISTICS XML ON.
Per completezza, è utile un piccolo background sugli operatori paralleli. Il lavoro viene distribuito a più flussi in un piano parallelo dal flusso di ripartizione o distribuisce operatori di flusso. Distribuiscono righe o pagine tra i thread utilizzando uno dei quattro meccanismi:
- L'hash distribuisce le righe in base a un hash delle colonne nella riga
- Round-robin distribuisce le righe ripetendo l'elenco dei thread in un ciclo
- Broadcast distribuisce tutte le pagine o righe su tutti i thread
- Il partizionamento della domanda viene utilizzato solo per le scansioni. I thread si girano, richiedono una pagina di dati dall'operatore, li elabora e al termine richiede un'altra pagina.
Il primo operatore di flusso di distribuzione (più a destra nel piano) utilizza il partizionamento della domanda sulle righe provenienti da una scansione costante. Esistono tre thread che chiamano GetNext () 6, 4 e 0 volte per un totale di 10 "Righe effettive":
<RunTimeInformation>
<RunTimeCountersPerThread Thread="2" ActualRows="6" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="1" ActualRows="4" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
</RunTimeInformation>
Al prossimo operatore di distribuzione abbiamo di nuovo tre thread, questa volta con 50, 50 e 0 chiamate a GetNext () per un totale di 100:
<RunTimeInformation>
<RunTimeCountersPerThread Thread="2" ActualRows="50" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="1" ActualRows="50" ActualEndOfScans="1" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
</RunTimeInformation>
È al prossimo operatore parallelo che probabilmente appaiono la causa e la spiegazione.
<RunTimeInformation>
<RunTimeCountersPerThread Thread="2" ActualRows="1" ActualEndOfScans="0" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="1" ActualRows="10" ActualEndOfScans="0" ActualExecutions="1" />
<RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
</RunTimeInformation>
Quindi ora abbiamo 11 chiamate a GetNext (), dove ci aspettavamo di vederne 10.
Modifica: 13/11/2011
Bloccato a questo punto, sono andato a cercare risposte con i chap nell'indice cluster e @MikeWalsh ha gentilmente diretto @SQLKiwi qui .