Vergleichen von zwei Abfragen in SQL Server 2012

14

Ich vergleiche zwei Abfragen in SQL Server 2012. Ziel ist es, alle relevanten Informationen des Abfrageoptimierers für die Auswahl der besten Abfrage zu verwenden. Beide Abfragen führen zu denselben Ergebnissen. die maximale Bestellnummer für alle Kunden.

Das Löschen des Pufferpools wurde vor dem Ausführen jeder Abfrage mit FREEPROCCACHE und DROPCLEANBUFFERS durchgeführt

Welche Abfrage ist unter Verwendung der unten angegebenen Informationen die bessere Wahl?

-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
                 FROM Sales.Orders AS O2
                 WHERE O2.custid = O1.custid);


-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid

STATISTIKEN ZEIT

Abfrage 1 STATISTIKZEIT: CPU-Zeit = 0 ms, verstrichene Zeit = 24 ms

Abfrage 2 STATISTIKZEIT: CPU-Zeit = 0 ms, verstrichene Zeit = 23 ms

STATISTIKEN IO

Abfrage 1 STATISTICS IO: Tabelle 'Orders'. Scananzahl 1, logische Lesevorgänge 5, physische Lesevorgänge 2, Vorauslesevorgänge 0, logische Lobs-Lesevorgänge 0, physikalische Lobs-Lesevorgänge 0, Lobs-Vorauslesevorgänge 0.

Abfrage 2 STATISTIK IO: Tabelle 'Bestellungen'. Scananzahl 1, logische Lesevorgänge 4, physische Lesevorgänge 1, Vorauslesevorgänge 8, logische Lobs-Lesevorgänge 0, physische Lobs-Lesevorgänge 0, Lobs-Vorauslesevorgänge 0.

Ausführungspläne

Bildbeschreibung hier eingeben

SELECT-Eigenschaften Abfrage 1

Bildbeschreibung hier eingeben

SELECT Eigenschaften Abfrage 2

Bildbeschreibung hier eingeben

Schlussfolgerungen:

Abfrage 1

  1. Batch-Kosten 48%
  2. Logische Lesevorgänge 5
  3. Physische Lesevorgänge 2
  4. Vorauslesen Liest: 0
  5. CPU-Zeit: 0ms
  6. Verstrichene Zeit 24ms
  7. Geschätzte Teilbaumkosten: 0.0050276
  8. CompileCPU: 2
  9. CompileMemory: 384
  10. CompileTime: 2

Abfrage 2

  1. Batch-Kosten 52%
  2. Logische Lesevorgänge 4
  3. Körperlesungen 1
  4. Vorauslesen Liest: 8
  5. CPU-Zeit 0
  6. Abgelaufene Zeit 23ms
  7. Geschätzte Teilbaumkosten: 0.0054782
  8. CompileCPU: 0
  9. CompileMemory: 192
  10. CompileTime: 0

Persönlich ist Abfrage 1 effizienter, obwohl Abfrage 2 laut Grafikplan höhere Batch-Kosten verursacht. Dies liegt daran, dass Abfrage 2 weniger logische Lesevorgänge erfordert, die verstrichene Zeit etwas kürzer ist als die Werte für Compilecpu, Compilememory und Compiletime niedriger. Read-Ahead-Reads sind 8 für Abfrage 2 und 0 für Abfrage 1.

Update 12.03

Definition des Clustered Index

ALTER TABLE [Sales].[Orders] ADD  CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Nicht gruppierter Index idx_nc_custid

CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
    [custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Craig Efrein
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Paul White setzt Monica wieder ein

Antworten:

10

Ich freue mich über Ihre Vorgehensweise bei der sorgfältigen Prüfung der Abfrageoptimierung und Überprüfung von Optionen und Plänen. Ich wünschte, mehr Entwickler hätten das getan. Eine Warnung wäre: Testen Sie immer mit vielen Zeilen und achten Sie auf die logischen Werte. Dies ist eine kleine Tabelle. Versuchen Sie, eine Beispiellast zu generieren und führen Sie die Abfrage erneut aus. Ein kleines Problem - in Ihrer oberen Abfrage fragen Sie nicht nach einer Bestellung von, in Ihrer unteren Abfrage sind Sie. Sie sollten sie jeweils mit der Bestellung vergleichen und gegenüberstellen.

Ich habe gerade schnell einen SalesOrders-Tisch mit 200.000 Kundenaufträgen erstellt. Und lief die Abfragen mit dem ORDER BY in jedem. Ich habe auch ein bisschen mit Indizes gespielt.

Ohne gruppierten Index für OrderID, nur einen nicht gruppierten Index für CustID. Die zweite Abfrage hat eine Outperformance erzielt . Vor allem bei der Bestellung von jeweils enthalten. Bei der ersten Abfrage wurden doppelt so viele Lesevorgänge ausgeführt wie bei der zweiten Abfrage, und die prozentualen Kosten zwischen den Abfragen betrugen 67% / 33%.

Mit einem Clustered-Index für OrderID und einem Non-Clustered-Index nur für CustID wurden sie mit einer ähnlichen Geschwindigkeit und genau der gleichen Anzahl von Lesevorgängen ausgeführt.

Daher würde ich vorschlagen, dass Sie die Anzahl der Zeilen erhöhen und weitere Tests durchführen. Aber meine abschließende Analyse Ihrer Fragen -

Sie können feststellen, dass sie sich ähnlicher verhalten, als Sie denken, wenn Sie die Zeilen vergrößern. Denken Sie also an diese Einschränkung, und testen Sie sie auf diese Weise.

Wenn alles, was Sie jemals zurückgeben möchten, die maximale Bestell-ID für jeden Kunden ist und Sie feststellen möchten, dass die Bestell-ID die größte Bestell-ID ist, ist die zweite Abfrage von diesen beiden der beste Weg aus meiner Sicht - es ist ein bisschen Einfacher und auf der Grundlage der Teilbaumkosten etwas teurer, ist es jedoch einfacher und schneller, Aussagen zu entschlüsseln. Möchten Sie eines Tages weitere Spalten zu Ihrer Ergebnismenge hinzufügen? Dann können Sie das mit der ersten Abfrage tun.

Aktualisiert: Einer Ihrer Kommentare zu Ihrer Frage lautete:

Bitte beachten Sie, dass das Finden der besten Abfrage in dieser Frage ein Mittel ist, um die zum Vergleichen verwendeten Techniken zu verfeinern.

Der beste Weg, dies zu tun - testen Sie mit mehr Daten - stellt immer sicher, dass Sie Daten haben, die mit der Produktion und der erwarteten zukünftigen Produktion konsistent sind. Abfragepläne beginnen mit der Suche nach Daten, wenn Sie den Tabellen mehr Zeilen zuweisen und versuchen, die erwartete Verteilung in der Produktion beizubehalten. Und achten Sie auf Dinge wie "Order By" oder "Order By". Ich denke, dass es am Ende keinen großen Unterschied macht, aber es lohnt sich trotzdem, sich damit auseinanderzusetzen.

Ihr Ansatz, diese Detail- und Datenebene zu vergleichen, ist gut. Subtree-Kosten sind meist willkürlich und bedeutungslos, aber es lohnt sich immer noch, zumindest einen Vergleich zwischen Bearbeitungen / Änderungen oder sogar zwischen Abfragen anzustellen. Ein Blick auf die Zeitstatistik und das IO ist sehr wichtig, ebenso wie ein Blick auf den Plan für alles, was sich für die Größe der Daten, mit denen Sie arbeiten, und für das, was Sie tun möchten, als unangebracht anfühlt.

Mike Walsh
quelle
Hallo nochmal, vielen Dank für Ihre Hinweise zur Verwendung größerer Datenmengen. Dies ist nicht das erste Mal, dass jemand darauf eingeht. Beim letzten Mal wurde jedoch eine mögliche Fragmentierung durch Seitenteile in Betracht gezogen. Haben Sie in Ihrem Beispiel mit 200.000 Zeilen die Fragmentierung überprüft?
Craig Efrein
Nun, in meinem kleinen, schnellen Beispiel mit 200.000 Zeilen habe ich mich nicht auf Fragmentierung konzentriert, nein. Aber so wie ich es gemacht habe, würde es keine geben. Ich habe eine Tabelle erstellt, sie ausgefüllt und dann die Indizes erstellt. Es handelt sich also um neu erstellte Indizes. Und das ändert nichts an dem Ansatz, die Abfragepläne zu betrachten, was die Hauptfrage zu sein scheint. Das Datenvolumen ist groß - wirklich groß - wenn man Abfragepläne genau betrachtet. Ich habe oft Fälle gesehen, in denen es in dev (mit 1-10 Zeilen) großartig aussah und in prod mit echten Daten schrecklich war. Aber Ihr Ansatz ist gut und hoffentlich hilft diese Info und das Gespräch in Kommentaren
Mike Walsh
Wie haben Sie die custid-Werte zufällig gewählt, da wir sie nach custid gruppieren? Eine Sache, an die ich mich aus meinen Lesungen erinnere, ist die Wichtigkeit bestimmter Werte. Wenn custid nur eine kleine Anzahl unterschiedlicher Kunden hätte, wären die Kosten für das Stream-Aggregat unrealistisch.
Craig Efrein
Ich habe gerade die RAND-Funktion verwendet, um 100 Kunden zu erstellen und jeder orderID zufällig eine zuzuweisen. Ich habe eine schnelle Überprüfung durchgeführt. :)
Mike Walsh
Danke Mike für all deine Hilfe. Eine letzte Frage. Auf welche Werte achten Sie in den Bildschirmen für SELECT-Eigenschaften aus dem Ausführungsplan 2012, den ich in meiner Frage angegeben habe?
Craig Efrein