SQL Practice Blog – SQL Server, BizTalk und .NET Erfahrungen

LINQ: Compiled vs. Non-Compiled Queries

with one comment

Ich hatte auf der VSone in München, eine interessante Diskussion bei meiner Session auf der Business Bühne zum Thema “Zukunft und Entwicklung des Entity Frameworks”. Ein Teilnehmer wollte wissen wie man Abfragen mit LINQ to Entities optimieren kann. Eine der Antworten von mir war: Compiled Queries. Jedoch stellte es sich als komplizierter heraus, als es sich zuerst anhörte, da man einige Vorgehensweisen beachten muss. Was ist eigentlich der Unterschied zwischen vorkompilierten und nicht vorkompilierten Queries? Man muss sich beim Einsatz von LINQ to Entities bewusst sein, das der Weg vom LINQ Statement zum eigentlich ausgeführten Query (z.B. T-SQL) sehr lang ist.

Wer sich nun ein wenig mit dem SQL Server auskennt, der weiß das T-SQL Statements wie das folgende relativ langsam sind, wenn man sie mehrfach ausführt.

SELECT Customer.CustomerID, FirstName, LastName, Quantity, Price, Quantity * Price AS [TotalPrice]
FROM SalesDetails
JOIN Customer
ON SalesDetails.CustomerID = Customer.CustomerID
WHERE Quantity > 100

Wieso ist das so? Da der Ausdruck in der WHERE Bedingung kein Parameter ist, erstellt der SQL Server Abfragenoptimierer bei jedem Aufruf dieses Statements einen neuen Abfrageplan. Würde ich an dieser Stelle mit Parametern arbeiten, so würde er beim ersten mal ausführen das Statement kompilieren und den Abfrageplan speichern. Diesen würde der SQL Server bei diesem Statement immer wieder benutzen. Stellt man sich vor das diese Abfrage vielleicht mehrere hundertmal benutzt wird, so dürfte sofort klar sein das sich daraus ein Vorteil in der Abfragegeschwindigkeit ergibt.

Der QueryCompiler des Entity Frameworks verhält sich im Prinzip ganz genauso. Ich habe aber die Möglichkeit über die statische Klasse CompiledQuery mit der Funktion Compile() eine LINQ Abfrage kompilieren. CompiledQuery.Compile() gibt ein Delegate in Form einer Func zurück. Dieser Delegate kann dann zu einem beliebigen Zeitpunkt ausgeführt werden.

Wichtig! Es führt jedoch zu Einbrüchen in der Performance wenn man diese LINQ Abfrage bei jedem Aufruf kompiliert. Das ist ein Fehler, der leider sehr häufig gemacht wird.

Ich werde nun Anhand eines Beispiels erklären, wie man mit dem Entity Framework und LINQ to Entities kompilierte LINQ Abfragen erstellen und nutzen kann. Als erstes legen wir uns dazu ein private Delegate an, wo wir die kompilierte LINQ Abfrage speichern werden.

private_delegate

Func hat hier drei Parameter. Die ersten beiden Parameter stehen für die Inputparameter. Hierbei steht der erste Parameter für den ObjectContext des Entity Frameworks und der zweite Parameter ist der Ausdruck für meine WHERE Bedingung. Der dritte Parameter hingegen ist der Typ des Rückgabewertes.

Jetzt benötige ich eine Funktion in der ich meine LINQ Abfrage kompiliere und dem Delegate zuweise. Danach kann ich den Delegate mit einem Invoke(param1, param2) ausführen.

compiledQueryFunction

Hier wird als erstes geprüft ob das LINQ Statement bereits kompiliert ist, wenn nicht wird es kompiliert und dem Delegate zugewiesen. Danach kann es beliebig oft im kompilierten Zustand ausgeführt werden.

Wer nun eine Stoppuhr zwischen seine Abfragen baut, wird merken das es beim ersten Abfragevorgang länger dauert als mit der unkompilierten Abfrage. Wieso? Bei dem ersten Ausführen der Abfrage muss die Abfrage erst kompiliert werden und braucht somit etwas mehr Zeit. Vergleicht man die Zeiten bei nachfolgenden Abfragen und die Gesamtzeit, bei z.B. 20 000 Abfragen, so wird auffallen das mit einer kompilieren Abfrage deutlich bessere Zeiten erreicht werden können.

Es sollte jedoch beachtet werden, das kompilierte Abfragen nicht immer sinnvoll sind. Führe ich eine Abfrage nur einmal oder wenige Male aus, so habe ich evtl. deutlich schlechtere Zeiten. Vor allem sollte aber beachtet werden, das kompilierte Abfragen irgendwo gespeichert werden müssen. Hier wird der Arbeitsspeicher belastet, was bei sehr vielen großen Abfragen relativ schnell zu einem deutlichen Speicherverlust führen kann.

Für Beispielprojekte oder weitere Infos, könnt ihr gerne Kontakt mit mir aufnehmen.

Written by Robert Meyer

Februar 25, 2010 um 21:09

Eine Antwort

Subscribe to comments with RSS.

  1. hi robert,
    nun bin ich endlich dazu gekommen mal auf deinen blog zu schauen. und von diesem beitrag fühle ich mich doch gleich angesprochen🙂
    vielen dank für die interessante diskussion auf der vsone und natürlich für die schöne anleitung zum kompilieren von linq queries.

    Michael Hinz

    März 11, 2010 at 14:01


Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: