Auf einem Server mit 32 GB wird SQL Server 2014 SP2 mit einem maximalen Speicher von 25 GB ausgeführt. Wir haben zwei Tabellen. Hier finden Sie eine vereinfachte Struktur beider Tabellen:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
mit folgenden nicht gruppierten Indizes:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
Die Datenbank ist mit compatibility level
120 konfiguriert .
Wenn ich diese Abfrage ausführe, kommt es zu Verschüttungen tempdb
. So führe ich die Abfrage aus:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Wenn Sie das [remark]
Feld nicht auswählen, treten keine Verschüttungen auf. Meine erste Reaktion war, dass die Verschüttungen aufgrund der geringen Anzahl geschätzter Zeilen auf dem Nested-Loop-Operator auftraten.
Also füge ich der Einstellungstabelle 5 datetime- und 5 integer-Spalten hinzu und füge sie meiner select-Anweisung hinzu. Wenn ich die Abfrage ausführe, treten keine Verschüttungen auf.
Warum treten die Verschüttungen nur auf, wenn sie [remark]
ausgewählt sind? Es hat wahrscheinlich etwas damit zu tun, dass dies ein ist varchar(max)
. Was kann ich tun, um ein Verschütten zu vermeiden tempdb
?
Das Hinzufügen OPTION (RECOMPILE)
zur Abfrage macht keinen Unterschied.
quelle
select r.id, LEFT(remark, 512)
Vielleicht können Sie es versuchen (oder welche vernünftige Teilstringlänge auch immer sein mag).Antworten:
Hier wird es mehrere mögliche Problemumgehungen geben.
Sie können die Speicherzuweisung manuell anpassen , obwohl ich diesen Weg wahrscheinlich nicht gehen würde.
Sie können auch einen CTE und TOP verwenden, um die Sortierung nach unten zu verschieben, bevor Sie die Spalte mit der maximalen Länge greifen. Es wird ungefähr so aussehen wie unten.
Proof-of-Concept-Dbfiddle hier . Beispieldaten wären weiterhin willkommen!
Wenn Sie eine hervorragende Analyse von Paul White lesen möchten, lesen Sie hier.
quelle
Der Überlauf tritt auf, wenn Sie diese Spalte einschließen, weil Sie nicht genügend Speicherplatz für die zu sortierenden großen Zeichenfolgendaten erhalten.
Sie erhalten nicht genügend Speicherplatz, da die tatsächliche Anzahl der Zeilen 10x höher ist als die geschätzte Anzahl der Zeilen (1.302 tatsächliche gegenüber 126 geschätzten).
Warum ist die Schätzung falsch? Warum glaubt SQL Server, dass es in dbo.Settings nur eine Zeile mit einer
resourceid
von 38 gibt?Es könnte sich um ein Statistikproblem handeln, das Sie überprüfen können, indem
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
Sie die Anzahl für diesen Histogrammschritt ausführen. Der Ausführungsplan scheint jedoch darauf hinzudeuten, dass die Statistiken so vollständig und aktuell sind, wie sie sein könnten.Da Statistiken nicht helfen, ist Ihre beste Wahl wahrscheinlich ein Umschreiben der Abfrage - das Forrest in seiner Antwort behandelt hat.
quelle
Mir scheint, dass die
where
Klausel in der Abfrage das Problem darstellt und die Ursache für die niedrigen Schätzungen ist, selbst wenn sieOPTION(RECOMPILE)
verwendet wird.Ich habe einige Testdaten erstellt und am Ende zwei Lösungen gefunden, bei denen das
ID
Feldresources
entweder in einer Variablen (wenn es immer eindeutig ist) oder in einer temporären Tabelle gespeichert wird, wenn wir mehr als eine haben könnenID
.Basistestaufzeichnungen
Fügen Sie die 'Seek'-Werte ein, um dieselbe ungefähre Ergebnismenge wie OP zu erhalten (1300 Datensätze).
Ändern Sie die Kompatibilitäts- und Aktualisierungsstatistik entsprechend dem OP
Ursprüngliche Abfrage
Meine Schätzungen sind mit einer geschätzten Zeile noch schlechter , während 1300 zurückgegeben werden. Und wie OP sagte, spielt es keine Rolle, ob ich hinzufüge
OPTION(RECOMPILE)
Es ist wichtig zu beachten, dass die Schätzungen zu 100% korrekt sind, wenn wir die where-Klausel entfernen, was erwartet wird, da wir alle Daten in beiden Tabellen verwenden.
Ich habe die Indizes gezwungen, nur um sicherzustellen, dass wir dieselben wie in der vorherigen Abfrage verwenden, um den Punkt zu beweisen
Wie erwartet gute Schätzungen.
Was könnten wir also ändern, um bessere Schätzungen zu erhalten und dennoch nach unseren Werten zu suchen?
WENN @UID eindeutig ist, wie im Beispiel, das OP gegeben hat, könnten wir die Single
id
, von der zurückgegeben wurde,resources
in eine Variable einfügen und diese Variable dann mit einer OPTION (RECOMPILE) suchen.Das gibt 100% genaue Schätzungen
Was aber, wenn mehrere Ressourcen-UIDs in Ressourcen enthalten sind?
Fügen Sie einige Testdaten hinzu
Dies könnte mit einer temporären Tabelle behoben werden
Wieder mit genauen Schätzungen .
Dies wurde mit meinem eigenen Datensatz YMMV durchgeführt.
Geschrieben mit sp_executesql
Mit einer Variablen
Mit einem temporären Tisch
Immer noch 100% korrekte Schätzungen zu meinem Test
quelle