2017-02-21 2 views
1

Ich habe ein Leistungsproblem mit einer TOP (1) (oder EXISTS) Select-Anweisung auf einem Join von 2 Tabellen.Leistung von TOP (1) wählen Sie auf mehrere Tabellen aus

Ich verwende SQL Server 2008 R2.

Ich habe 2 Tabellen:

CREATE TABLE Records(
    Id PRIMARY KEY INT NOT NULL, 
    User INT NOT NULL, 
    RecordType INT NOT NULL) 

CREATE TABLE Values(
    Id PRIMARY KEY BIGINT NOT NULL, 
    RecordId INT NOT NULL, 
    Field INT NOT NULL, 
    Value NVARCHAR(400) NOT NULL, 
    CONSTRAINT FK_Values_Record FOREIGN KEY(RecordId) REFERENCES Records(Id)) 

mit Indizes: noch

CREATE NONCLUSTERED INDEX IDX_Records ON Records(User ASC, RecordType ASC) INCLUDE(Id) 
CREATE NONCLUSTERED INDEX IDX_Values ON Values(RecordId ASC, Field ASC) INCLUDE(Value) 
CREATE NONCLUSTERED INDEX IDX_ValuesByVal ON Values(Field ASC, Value ASC) INCLUDE(RecordId) 

Die Tabellen viele Daten enthalten, etwa 100 Millionen Aufzeichnungen in Aufzeichnungen und 150 Millionen in Werte, und sie sind wachsend. Einige Benutzer haben viele Daten, manche nur eine kleine Menge.

Für einige Benutzer/Feld-Kombination haben wir möglicherweise keine Datensätze in der Values-Tabelle, aber für einige andere Benutzer/Felder haben wir fast so viele Datensätze in der Values-Tabelle wie in der Records-Tabelle für diesen Benutzer.

Ich möchte eine Abfrage testen, wenn ich Daten für eine Benutzer/Feld-Kombination habe. Mein erster Versuch war:

SELECT TOP(1) V.Field 
FROM Records R 
INNER JOIN Values V ON V.RecordId = R.Id 
WHERE R.User = @User 
AND R.RecordType = @RecordType 
AND V.Field = @Field 

Das Problem bei dieser Abfrage war, dass, wenn der Ausführungsplan nicht im Cache des Servers war und der erste Benutzer nicht viele Daten hat der Server würde einen Ausführungsplan setzen Für diese Abfrage, die für einen Benutzer mit vielen Daten nicht gut funktionierte, führte dies zu einem Timeout (mehr als 15 Sekunden). Dasselbe Problem trat bei RecordTypes oder Fields auf. Also musste ich die IDs in der Abfrage fest codieren, anstatt Variablen zu verwenden.

SELECT TOP(1) V.Field 
FROM Records R 
INNER JOIN Values V ON V.RecordId = R.Id 
WHERE R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 

Aber selbst dann würde der Server manchmal einen Tabellenscan durchführen, anstatt die verfügbaren Indizes zu verwenden, was ebenfalls zu Timeouts führte. Also musste ich FORCESEEK auf die Abfrage hinzuzufügen:

SELECT TOP(1) V.Field 
FROM Records R WITH (FORCESEEK) 
INNER JOIN Values V WITH (FORCESEEK) ON V.RecordId = R.Id 
WHERE R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 

Aber auch jetzt, der Server manchmal sucht zuerst in der Aufzeichnungen Tabelle und dann in der Tabelle Werte, anstatt zuerst ein Suchen in der Tabelle Werte und dann in den Aufzeichnungen Tabelle, was auch zu Timeouts führt. Ich weiß nicht, warum dies zu einer Zeitüberschreitung führt, aber das tut es. Als Felder zu einem Record in meinem Modell verknüpft sind, konnte ich die Record Klausel, zwingt den Server des ersten seeking in der Tabelle Werte entfernen

SELECT TOP(1) V.Field 
FROM Records R WITH (FORCESEEK) 
INNER JOIN Values V WITH (FORCESEEK) ON V.RecordId = R.Id 
WHERE R.User = 123 
AND V.Field = 67 

Mit dieser letzten Änderung habe ich nicht mehr alle Timeouts, aber immer noch die Abfrage Nehmen Sie etwa 1 bis 2 Sekunden, manchmal sogar 5 bis 7 Sekunden.

Ich verstehe immer noch nicht, warum das so viel Zeit braucht.

Hat jemand irgendwelche Ideen, wie man diese Abfrage verbessert, um diese langen Abfragezeiten zu vermeiden?

+0

Zuerst versuchen müssen Sie es sinnvoll machen. TOP ohne ORDER BY wird eine unbestimmbare Zeile zurückgeben, also sollten Sie das zuerst entscheiden und diese in die Abfrage einführen. – LoztInSpace

+0

Ich verwende die TOP (1) als EXISTS (beide haben den gleichen Ausführungsplan), es ist mir egal, welche Zeile es zurückgibt, jede Zeile wird tun. Durch das Hinzufügen eines ORDER BY wird der Server gezwungen, alle (Tausende) Zeilen auszuwählen, die der WHERE-Klausel entsprechen, und sie vor der Ausführung der TOP-Anweisung zu sortieren. – Marc

+2

Sie können 'OPTION (RECOMPILE)' verwenden, um die Abfrage für die übergebenen spezifischen Werte neu zu kompilieren. –

Antwort

0

Sollte keinen Unterschied machen, aber für grinst

SELECT TOP(1) 1 
FROM Records R 
JOIN Values V 
    ON V.RecordId = R.Id 
AND R.User = 123 
AND R.RecordType = 45 
AND V.Field = 67 
Verwandte Themen