Warum verwendet die Unterabfrage Parallelität und der Join nicht?


16

Warum verwendet SQL Server Parallelität, wenn diese Abfrage ausgeführt wird, die eine Unterabfrage verwendet, jedoch keine, wenn ein Join verwendet wird? Die Join-Version wird seriell ausgeführt und dauert ca. 30-mal länger.

Join-Version: ~ 30secs

Bildbeschreibung hier eingeben

Unterabfrage-Version: <1 Sekunde

Bildbeschreibung hier eingeben

BEARBEITEN: XML-Versionen des Abfrageplans:

JOIN-Version

SUBQUERY-Version

Antworten:


12

Wie bereits in den Kommentaren angegeben, scheint es, als müssten Sie Ihre Statistiken aktualisieren.

Die geschätzte Anzahl der Zeilen aus dem kommenden Verbindung zwischen locationund testrunsist enorm unterschiedlich zwischen den beiden Plänen.

Schätzung des Beitrittsplans: 1

Plan 1

Schätzungen des Unterabfrageplans: 8.748

Bildbeschreibung hier eingeben

Die tatsächliche Anzahl der Zeilen, die aus dem Join herauskommen, beträgt 14.276.

Natürlich macht es absolut keinen intuitiven Sinn, dass die Join-Version abschätzen sollte, dass 3 Zeilen von locationeiner einzelnen verknüpften Zeile stammen, während die Unterabfrage abschätzt, dass eine einzelne dieser Zeilen 8.748 aus derselben Verknüpfung erzeugt, aber ich war trotzdem in der Lage um dies zu reproduzieren.

Dies scheint zu passieren, wenn bei der Erstellung der Statistiken keine Überkreuzung zwischen den Histogrammen auftritt. Die Join-Version nimmt eine einzelne Zeile an. Und die einzelne Gleichheitssuche der Unterabfrage nimmt die gleichen geschätzten Zeilen an wie eine Gleichheitssuche für eine unbekannte Variable.

Die Kardinalität von Testläufen ist 26244. Unter der Annahme, dass drei unterschiedliche Standort-IDs angegeben sind, schätzt die folgende Abfrage, dass 8,748Zeilen zurückgegeben werden ( 26244/3)

declare @i int

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = @i

Angesichts der Tatsache, dass die Tabelle locationsnur 3 Zeilen enthält, ist es einfach (wenn wir keine Fremdschlüssel annehmen), eine Situation zu konstruieren, in der die Statistiken erstellt und dann die Daten in einer Weise geändert werden, die sich dramatisch auf die tatsächliche Anzahl der zurückgegebenen Zeilen auswirkt, für die dies jedoch nicht ausreicht Lösen Sie die automatische Aktualisierung der Statistiken aus und kompilieren Sie den Schwellenwert neu.

Da SQL Server die Anzahl der Zeilen erhält, die aus diesem Join stammen, werden alle anderen Zeilenschätzungen im Join-Plan massiv unterschätzt. Die Abfrage bedeutet nicht nur, dass Sie einen seriellen Plan erhalten, sondern auch, dass die Speicherzuweisung nicht ausreicht, und dass die Sortierungen und Hash-Verknüpfungen mit verschüttet werden tempdb.

Ein mögliches Szenario, das die tatsächlichen und geschätzten Zeilen in Ihrem Plan wiedergibt, ist unten aufgeführt.

CREATE TABLE location
  (
     id       INT CONSTRAINT locationpk PRIMARY KEY,
     location VARCHAR(MAX) /*From the separate filter think you are using max?*/
  )

/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES      (101, 'Coventry'),
            (102, 'Nottingham'),
            (103, 'Derby')

CREATE TABLE testruns
  (
     location_id INT
  )

CREATE CLUSTERED INDEX IX ON testruns(location_id)

/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM   master..spt_values v1, master..spt_values v2

/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;    
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;

/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET    id = id - 100

Wenn Sie die folgenden Abfragen ausführen, erhalten Sie die gleiche geschätzte Abweichung von der tatsächlichen Abweichung

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = (SELECT id
                         FROM   location
                         WHERE  location = 'Derby')

SELECT *
FROM   testruns AS tr
       JOIN location loc
         ON tr.location_id = loc.id
WHERE  loc.location = ( 'Derby' ) 

Wenn vor Ort eine eindeutige Einschränkung hinzugefügt wird, wird deutlich, dass "=" genau eine Zeile zurückgibt. Dann werden in Ihrem Beispiel die Abfragepläne identisch (Scans -> Suchen): alter table Location add constraint U_Location_Location unique nonclustered (Location);
Crokusek

@crokusek ja. Erkannte was du danach meintest und löschte meinen Kommentar! Erhöht sich die geschätzte Anzahl der Zeilen, damit die Join-Version auch mit der Unterabfrage übereinstimmt? Nicht auf dem PC im Moment zu testen?
Martin Smith

@crokusek Ja. sieht aus wie die gleichen geschätzten Zeilen aus dem Join wie für die Unterabfrage in diesem Singleton-Fall.
Martin Smith

Ja. Identischer Abfrageplan, beide Schätzungen 8748, beide tatsächlichen 14276. Übrigens dachte ich, die locationId vorab zu berechnen, würde diesen Unterschied beheben, tut dies aber nicht.
Crokusek

1
@crokusek - Ich werde auch die eindeutige Einschränkung für die Position und andere ähnliche Stellen in meiner DB hinzufügen. Ich muss zugeben, dass ich nicht realisiert habe, dass dies die Abfrageoptimierung beeinflusst. Ich dachte, es ginge nur darum, die Datenintegrität zu gewährleisten. Vielen Dank für Ihren Beitrag zu dieser Frage.
Chris L
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.