2009-10-02 7 views
8

Wir verwenden ein ORM, das einen Aufruf von .NET an die sp_executesql gespeicherte SQL Server-Prozedur ausführt.SQL-Abfrage langsam von .NET-Code, aber nicht interaktiv

Wenn der gespeicherte Proc von .NET aufgerufen wird, erhalten wir eine Timeout-Ausnahme.

Wenn ich Profiler betrachte, kann ich sehen, dass die Abfrage tatsächlich sehr lange dauert. im Wesentlichen

Die Abfrage ist:

exec sp_executesql N'SELECT DISTINCT 
FROM [OurDatabase].[dbo].[Contract] [LPLA_1]) [LPA_L1] 
LEFT JOIN [OurDatabase].[dbo].[Customer] [LPA_L2] ON [LPA_L2].[Customer_ID]=[LPA_L1].[CustomerId] AND [LPA_L2].[Data]=[LPA_L1].[Data]) 
WHERE ((((([LPA_L1].[DealerId] = @DealerId1)) 
AND ([LPA_L2].[Last_Name] = @LastName2))))',N'@DealerId1 varchar(18),@LastName2 varchar(25)',@DealerId1='1234',@LastName2='SMITH' 

Das verwirrende Teil für mich ist: Wenn ich kopieren und die Abfrage einfügen, die in SQL Management Studio ein Timeout ist, und führen Sie es interaktiv, es führt nur in Ordnung.

Weiß jemand, warum die gleiche Abfrage bei Ausführung über .NET-Code wesentlich länger dauern würde? (Ich bin in der Lage, dies zu reproduzieren - die Abfrage aus dem Code konsequent ausgeführt, und die Abfrage ausgeführt interaktiv konsistent funktioniert einwandfrei.)

Jede Hilfe wird geschätzt. Vielen Dank!

+0

Wie viele Datenzeilen kehren Sie zurück? Wenn es Tausende und Abertausende von Leitungen gibt, dann könnte es Zeit brauchen, um über das Kabel zum Computer zu gehen und ein Ergebnis zurück zu erwarten. – Miles

+0

Gibt die Abfrage viele Daten zurück? Gibt es irgendwelche Daten, die zwischen dem Server und dem Client gesendet werden, die am Programm beteiligt sind, was im interaktiven Modus nicht passiert? – quosoo

+0

Drei in unserem Testfall. DealerID und Last_Name verfügen außerdem über Indizes. –

Antwort

0

Sind dealerId oder Lastname nvarchar (anders geschrieben als die varchar-Parameter)?

Dies kann die Konvertierung ganzer Indizes verursachen. Wenn dies der Fall ist, hinterlassen Sie einen Kommentar und ich werde Sie näher erläutern.

+0

Nein, sie sind leider beide Varcars in der Datenbank. –

4

Eine Sache, die ich ein paar Mal gesehen habe, ist, wenn Sie einen Unterschied zwischen Nvarchar und Varchar-Typen für einen Abfrageparameter in einem indizierten Feld haben. Dies kann passieren, wenn Sie varchar in Ihrer Datenbank verwenden und nicht explizit den Typ Ihres Parameters in .Net festlegen, der standardmäßig nvarchar annimmt.

In diesem Fall wählt Sql Server die korrektere Option und nicht die leistungsstärkere Option. Statt nur Ihren Parameter in varchar zu konvertieren, was eine einschränkende Konvertierung wäre, die möglicherweise Informationen verlieren könnte, wird die Datenbank gezwungen, jeden Wert für diese Spalte in der Tabelle in einen nvarchar umzuwandeln (was ohne Informationsverlust garantiert ist). . Das ist nicht nur langsam, aber Sql Server wird den Index nicht mehr verwenden können. Es ist unnötig zu sagen, dass die Ausführung der Abfrage viel länger dauert.

+0

Warum ist es wichtig, dass die Abfrage von .NET und nicht von SQL Mgmt Studio ausgeführt wird? –

+0

Hast du meinen Beitrag gelesen? Wenn Sie Parameter für Ihr sqlcommand-Objekt in .Net erstellen (selbst wenn dieser Teil von einem orm verborgen ist) und nicht explizit angeben, welche Arten dieser Parameter vorhanden sind, wird .Net für Sie auswählen und es könnte falsch auswählen. Das Ergebnis ist, dass es nicht genau die gleiche Abfrage ist. –

+0

Ja, aber AlexWalker läuft in SQL Mgmt Studio die Abfrage aus dem Profiler ==> das ist genau die gleiche Abfrage wird der SQL Server schließlich unabhängig von der Urheber (.NET oder Mgmt. Studio) ausgeführt werden. Es sei denn, die Verbindung ist anders konfiguriert ... Ich habe eine Ahnung, dass die Verbindung ist, was den Unterschied ausmacht. –

1

Ich habe die gleichen Probleme, eine Prozedur von .net ausgeführt, die zu viel Zeit in Anspruch nimmt (und nicht viele Zeilen zurückgibt). Ich sende eine Zeichenfolge an sql: "execute stored_procedure @ parameter1 = value1" und ich kopiere dies und laufe auf SQL Management Studio, aber dort läuft alles gut. Das Interessante an diesem Fall ist, dass ich in meiner Anfrage einfach einen LETTER zu einem Parameterwert hinzufüge oder entferne, um es zu verursachen. Ich bin sehr verwirrt.

Für Informationen verwende ich Volltext-Index und temporäre Tabellen por Paging, aber wie ich schon sagte, die gleiche Abfrage (und ich bin mir sicher) läuft perfekt in SQL Management Studio.

1

hatte gerade das gleiche Problem.

Das Umbauen von Indizes löste das Problem.

Vielleicht liegt das Problem in der Art der Parameter Nvarchar vs Index, die auf einer Varchar-Spalte ist ...?

1

Ich denke es ist, weil sp_executelsql kompilierte Abfragepläne wiederverwendet werden soll, so dass es Parameter nicht erneut schnüffelt, wenn die gleiche Abfrage es wieder trifft, so dass es einen Plan verwendet, der sehr langsam sein kann (wird sagen Sie, warum ein langsamer Abfrageplan) mit den aktuellen Parameterwerten.Offensichtlich verwendet sp_executesql eine andere Methode zum Auswählen von Indizes, und im Vergleich zu einer Nur-Text-Abfrage handelt es sich offensichtlich um eine fehlerhafte Methode.

Was unterscheidet das Tabellenupdate für 'Firma (eine Tabelle)' unter einem sp_executesql wird über eine Kette von geschachtelten Schleifen zugeführt, während die Textabfrage über eine Kette von Hash-Übereinstimmungen gespeist wird. Die Konstruktion der Ansichten scheint zwischen den beiden Versionen identisch zu sein (was ich erwarten würde). Leider ist der Rest sehr komplex und sie scheinen in der Mitte radikal andere Dinge zu tun; selbst das Ziehen eines "tatsächlichen" Ausführungsplans ergibt keine tatsächlichen Ausführungszeiten für die verschiedenen Unterkomponenten der Abfrage. Wirklich, ich sehe keinen Grund für sp_executesql, etwas anderes zu wählen, aber es erstellt zuverlässig einen wesentlich langsameren Plan.

Parameter Sniffing ist eine Lösung für diese, so sollten Sie die Namen der Parameter umbenennen oder Sie können sogar die Spaltennamen in where-Klausel, die sp_executesql neu erstellen einen Abfrageplan statt einen alten langsamen Plan neu erstellen, ist dies natürlich nicht die Lösung, aber es wird einen langsameren Plan nicht so lange zwischenspeichern.

Grüße.

+0

Danke, das sieht echt aus –

1

Hier ist was ich gefunden habe. Ich habe eine sehr komplexe gespeicherte Prozedur, die immer Informationen zählt und die Daten in eine Matrix mit 8 Spalten und 17 Zeilen stellt, wie es von Crystal Reports jeden Monat aufgerufen wird. Die Produktdatenbank war auf einem 96 GB verrückten schnellen Server! Kürzlich wurde es auf eine 32 GB virtuelle Maschine verkleinert. Während herunterskaliert - es hat die App in vielerlei Hinsicht langsamer laufen lassen - bis ein paar Indizes hinzugefügt wurden.

Dann kam die Zeit des Monats, um diese 17-Zeilen-Matrix-Monatsbericht zu führen ... und wie Sie sich vorstellen können, hat es abgelaufen!

Der Proc-Aufruf war ziemlich einfach - 3 Parameter. Ein Startdatum, ein Enddatum und ein zu filternder Bereich - null ist gleich ALL. Die 2 Daten wurden von Crystal Reports als Zeichen übergeben und diese gespeicherten PROC-Parameter wurden dann überall in diesem verrückten gespeicherten Proc verwendet.

Jede der 17 Zeilen - verwendet im Wesentlichen WITH-Anweisungen und verrückte Joins, um Datenzeilen vor dem Zählen/Pivotieren der Ergebnisse zu finden ... was in diesem Artikel NICHT wichtig ist.

Also hier ist es vereinfacht ....

CREATE PROCEDURE [dbo].[prcMonthlyStats] 
@bDate   datetime 
,@eDate   datetime 
,@districtStr varchar(120) 
AS 
BEGIN 
SET NOCOUNT ON; 

...

--the @bDate and @eDate params were DIRECTLY used throughout the 2000 lines of SQL, 
--to filter data inside and out of WITH statements and various other selects! 
-- 
--TIMES OUT! 

...

CREATE PROCEDURE [dbo].[prcMonthlyStats] 
@bDateStr  datetime 
,@eDateStr  datetime 
,@districtStr varchar(120) 
AS 
BEGIN 
SET NOCOUNT ON; 

--FIX! Declare 2 date time variables and simply assign the 2 date time parameters to them. 
DECLARE @bDate  datetime 
DECLARE @eDate  datetime 
DECLARE @district varchar(120) 

--SET THE VARIABLES FROM THE PARAMETERS PASSED IN! 
SET @bDate = @bDateStr 
SET @eDate = @eDateStr 
SET @district = @districtStr 
..... 

--PRESTO! The optimizer could once again use indexes as it should. 

So Moral der Geschichte ist - der Optimierer war kann seine Sache mit den DECLARED-Datumsangaben machen.