21.01.2010
23:05

Entity Framework Abfragen optimieren

Wer den erzeugten SQL-Code des Entity Framework näher betrachtet hat, der kann sicherlich verstehen, warum es an einigen Stellen zu Performanceeinbussen kommen kann. In den Fachzeitschriften werden immer wieder Tipps und Tricks gezeigt, wie die Symptome behandelt werden können. Für die Version 4 wurden Verbesserungen angekündigt, auch am SQL-Code wurde gearbeitet. Die Beta2 habe ich bereits installiert und bei einem Projekt werde ich diese Neuerungen auch testen können.

Für die aktuelle Version hier ein einfaches Beispiel:

Für ein Objekt mit einer definierten ID sollen alle zugeordneten Mitarbeiter ermittelt werden.

C#
var result = (from e in this.Context.Employees.
              Include("TargetSettings.TargetDesign.DesignRoot")
              where e.TargetSettings.Any(
                t => t.TargetDesign.DesignRoot.Id == id && 
                t.TargetDesign.DesignRoot.BusinessUnit.Id == buId)
              select new 
              {
                 Id = e.Id,
                 Name = e.Surname + " " + e.Givenname
              }).ToList();

Dieses Statement liefert die Mitarbeiter zurück. Das Resultat ist in diesem Zusammenhang nicht von Bedeutung. Interessant ist der erzeugte SQL-Code, den EF1 erstellt hat.

T-SQL
SELECT 
1 AS [C1], 
[Extent1].[EM_AutoID] AS [EM_AutoID], 
[Extent1].[EM_Surname] + N' ' + [Extent1].[EM_Givenname] AS [C2]
FROM [dbo].[tbEmployee] AS [Extent1]
WHERE  EXISTS (SELECT 
	cast(1 as bit) AS [C1]
	FROM   (SELECT [Extent2].[TS_AutoID] AS [TS_AutoID], [Extent2].[TD_AutoID] AS [TD_AutoID1], 
                               ... Sehr viele Spalten entfernt ... [TD_User] AS [TD_User2]
		FROM   [dbo].[tbTargetSetting] AS [Extent2]
		INNER JOIN [dbo].[tbTargetDesign] AS [Extent3] ON [Extent2].[TD_AutoID] = [Extent3].[TD_AutoID]
		LEFT OUTER JOIN [dbo].[tbTargetDesign] AS [Extent4] ON [Extent2].[TD_AutoID] = [Extent4].[TD_AutoID]
		WHERE 1 = [Extent3].[DR_AutoID] ) AS [Filter1]
	INNER JOIN [dbo].[tbDesignRoot] AS [Extent5] ON [Filter1].[DR_AutoID2] = [Extent5].[DR_AutoID]
	WHERE ([Extent1].[EM_AutoID] = [Filter1].[EM_AutoID]) AND (3 = [Extent5].[BU_AutoID])
)

Im SQL Management Studio habe ich eine Abfrage erstellt, so wie ich mir diese vorstellen würde, diese liefert das gleiche Ergebnis wie der Linq-Ausdruck zurück und hat folgenden Aufbau:

T-SQL
SELECT 
	EM_Surname + ' ' + EM_GivenName
	,tbEmployee.EM_AutoID
FROM tbEmployee
WHERE EM_AUTOID IN (
	SELECT EM_AUTOID
	FROM tbTargetSetting
	inner join tbTargetDesign on tbTargetSetting.TD_AutoID = tbTargetDesign.TD_AutoID
	inner join tbDesignRoot on tbDesignRoot.DR_AutoID = tbTargetDesign.DR_AutoID
	where tbTargetDesign.dr_autoID = 1 and BU_AutoID = 3
)

Werden diese beiden Abfragen im Ausführungsplan gegenübergestellt, ist meine Abfrage ein bisschen besser unterwegs.

Ausführungsplan 1
Ausführungsplan 1: Abfragekosten meiner Abfrage in Relation zur EF-Abfrage: 33%
Ausführungsplan 2
Ausführungsplan 2: Abfragekosten der EF-Abfrage in Relation: 67%

In meinem Szenario spielt es jedoch noch keine Bedeutung, es zeigt aber auf, dass Tricks wie zum Beispiel eine statische Verbindung bzw. Compiled Queries zwar eine Verbesserung mit sich bringen, aber nicht unbedingt zum gewünschten Erfolg führen. Es sollte also geprüft werden, inwieweit sich eine Abfrage optimieren lässt, bevor tiefer in die Trickkiste gegriffen und der Quellcode zu kreativ wird.

Wie sieht es jetzt in diesem Beispiel aus. Die Unterabfrage des vom Entity Framework erzeugten SQL-Codes enthält alle Spalten der benötigten Unterobjekte. Das ist sehr viel Ballast, der hier unnötig erzeugt wird. Dieser kommt zustande, weil ein Feld auf dem Objekt DesignRoot abgefragt wird.

In diesem Beispiel entferne ich die Abfrage nach der Id der Business Unit. Der erste Optimierungsversuch wäre dementsprechend zu prüfen, ob wirklich alle Parameter benötigt werden bzw. Abfragen auf die 3. Hierarchieebene zu vermeiden.

C#
var result = (from e in this.Context.Employees.
              Include("TargetSettings.TargetDesign.DesignRoot")
              where e.TargetSettings.Any(t => t.TargetDesign.DesignRoot.Id == id)
              select new 
              {
                 Id = e.Id,
                 Name = e.Surname + " " + e.Givenname
              }).ToList();

Der vom Entity Framework erzeugte SQL-Code sieht danach so aus:

T-SQL
SELECT 
1 AS [C1], 
[Extent1].[EM_AutoID] AS [EM_AutoID], 
[Extent1].[EM_Surname] + N' ' + [Extent1].[EM_Givenname] AS [C2]
FROM [dbo].[tbEmployee] AS [Extent1]
WHERE  EXISTS (SELECT 
	cast(1 as bit) AS [C1]
	FROM  [dbo].[tbTargetSetting] AS [Extent2]
	INNER JOIN [dbo].[tbTargetDesign] AS [Extent3] ON [Extent2].[TD_AutoID] = [Extent3].[TD_AutoID]
	WHERE ([Extent1].[EM_AutoID] = [Extent2].[EM_AutoID]) AND (1 = [Extent3].[DR_AutoID])
)

Auf dem ersten Blick ist dieser Code schon etwas übersichtlicher. Aber wie sieht es mit den Abfragekosten in Relation zu meiner Abfrage aus?

Ausführungsplan 3
Ausführungsplan 3: EF-Abfrage nach Optimierung in Realation: 50%

Auf dem zweiten Blick sind beide Abfragen gleichwertig. Ohne einen tiefen Griff in die Trickkiste zu machen, konnte die Abfrage optimiert werden.

  •  
  • 0 Kommentar(e)
  •  

Mein Kommentar

Über jeden weiteren Kommentar in diesem Post benachrichtigen.

Zurück

Translate this page

Kategorien

  • [-].NET Development (215)
  • [-]Datenbank (26)
  • HTML (1)
  • Konfiguration (12)
  • Mind Map (10)
  • Off-topic (9)
  • Open Source (3)
  • Qualität (7)
  • Sharepoint (6)
  • Sicherheit (2)

Archiv

Social Bookmarking

Bookmark bei: Mr. Wong Bookmark bei: Webnews Bookmark bei: Icio Bookmark bei: Oneview Bookmark bei: Linkarena Bookmark bei: Favoriten Bookmark bei: Seekxl Bookmark bei: Favit Bookmark bei: Social Bookmarking Tool Bookmark bei: Power Oldie Bookmark bei: Bookmarks.cc Bookmark bei: Newskick Bookmark bei: Newsider Bookmark bei: Linksilo Bookmark bei: Readster Bookmark bei: Folkd Bookmark bei: Yigg Bookmark bei: Digg Bookmark bei: Del.icio.us Bookmark bei: Reddit Bookmark bei: Simpy Bookmark bei: StumbleUpon Bookmark bei: Slashdot Bookmark bei: Netscape Bookmark bei: Furl Bookmark bei: Yahoo Bookmark bei: Spurl Bookmark bei: Google Bookmark bei: Blinklist Bookmark bei: Blogmarks Bookmark bei: Diigo Bookmark bei: Technorati Bookmark bei: Newsvine Bookmark bei: Blinkbits Bookmark bei: Ma.Gnolia Bookmark bei: Smarking Bookmark bei: Netvouz Information