Laden...

Linq: Include Performance-Frage

Erstellt von andreas-82 vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.434 Views
andreas-82 Themenstarter:in
42 Beiträge seit 2006
vor 13 Jahren
Linq: Include Performance-Frage

verwendetes Datenbanksystem: EntityFramework 4 / SqlServer2008

Hallo zusammen!
Ich arbeite mich gerade in Linq-Abfragen gegen ein EF4-Model ein und bin
beim Untersuchen verschiedener Queries mit dem Sql Profiler auf eine Frage gestoßen:

Es geht um eine einfache Kontakte - Adressen - Abfrage.
Die Frage ist, wenn ich in der Abfrage per 'Include' die Adressen mitlade, warum kann ich dann nicht über ein Select nur bestimmt Spalten des Contact-Entities laden? Sobald Include verwendet wird, werden immer auch alle Spalten vom Contact-Entity geladen

var result = from c in context.Contacts.Include("Addresses")
                     select new { c.LastName, c.ContactID, c.Addresses};

SQL-Profiler:

SELECT 
[Project1].[ContactID] AS [ContactID], 
[Project1].[LastName] AS [LastName], 
[Project1].[FirstName] AS [FirstName], 
[Project1].[Title] AS [Title], 
[Project1].[AddDate] AS [AddDate], 
[Project1].[ModifiedDate] AS [ModifiedDate], 
[Project1].[C1] AS [C1], 
[Project1].[addressID] AS [addressID], 
[Project1].[Street1] AS [Street1], 
[Project1].[Street2] AS [Street2], 
[Project1].[City] AS [City], 
[Project1].[StateProvince] AS [StateProvince], 
[Project1].[CountryRegion] AS [CountryRegion], 
[Project1].[PostalCode] AS [PostalCode], 
[Project1].[AddressType] AS [AddressType], 
[Project1].[ContactID1] AS [ContactID1], 
[Project1].[ModifiedDate1] AS [ModifiedDate1]
FROM ( SELECT 
	[Extent1].[ContactID] AS [ContactID], 
	[Extent1].[FirstName] AS [FirstName], 
	[Extent1].[LastName] AS [LastName], 
	[Extent1].[Title] AS [Title], 
	[Extent1].[AddDate] AS [AddDate], 
	[Extent1].[ModifiedDate] AS [ModifiedDate], 
	[Extent2].[addressID] AS [addressID], 
	[Extent2].[Street1] AS [Street1], 
	[Extent2].[Street2] AS [Street2], 
	[Extent2].[City] AS [City], 
	[Extent2].[StateProvince] AS [StateProvince], 
	[Extent2].[CountryRegion] AS [CountryRegion], 
	[Extent2].[PostalCode] AS [PostalCode], 
	[Extent2].[AddressType] AS [AddressType], 
	[Extent2].[ContactID] AS [ContactID1], 
	[Extent2].[ModifiedDate] AS [ModifiedDate1], 
	CASE WHEN ([Extent2].[addressID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
	FROM  [dbo].[Contact] AS [Extent1]
	LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON [Extent1].[ContactID] = [Extent2].[ContactID]
)  AS [Project1]
ORDER BY [Project1].[ContactID] ASC, [Project1].[C1] ASC

Macht sich das in der Performance nicht ab einer bestimmten Zeilen- und Spaltenanzahl bemerkbar, wenn einfach immer alle Spalten geladen werden?
Lässt sich dies mit Include überhaupt einschränken?
Ist es hier alternativ besser ein Nested-Query zu verwenden?

Vielen Dank schon mal für Ideen 😃

~ There's no knowledge that is not power~

F
10.010 Beiträge seit 2004
vor 13 Jahren

Wenn du nicht viele (N)VarChar(MAX) Felder hast, ist die Perfomrmance nur dann betroffen, wenn du wirklich viele Spalten hast.
Und viele Spalten in einer Tabelle deuten meist auf suboptimale Normalisierung hin.

16.806 Beiträge seit 2008
vor 13 Jahren

Hi,

rein theoretisch ist es mit dem EF kein Problem nur bestimmte Spalten zu laden - eben durch

select new {...}

Ich vermute aber, dass Du Deine Entitäten mit dem GUI Designer angelegt hast, in dessen Schema definiert ist, welche Spalten geladen werden - nämlich alle.
Gruß

A
350 Beiträge seit 2010
vor 13 Jahren

Vll hilft dir das auch etwas bei der Performance (Lazy Loading)
Klick Mich

Grüße

W
955 Beiträge seit 2010
vor 13 Jahren

rein theoretisch ist es mit dem EF kein Problem nur bestimmte Spalten zu laden - eben durch

select new {...}  

Hi,

so allgemein formuliert ist es nicht richtig. Wenn er Objektgraphen mit Include() erzeugen will muß das Root-Entity vollständig sein. War zumindest in EF1 so. Wenn er nur "Daten aus einer einzelnen Tabelle" benötigt kann er mit anonymen Objekten / EntityDataReader arbeiten.

W
955 Beiträge seit 2010
vor 13 Jahren

vMacht sich das in der Performance nicht ab einer bestimmten Zeilen- und Spaltenanzahl bemerkbar, wenn einfach immer alle Spalten geladen werden? Ich habe in einer App alle 5000 Patienten aller Stationen aller Krankenhäuser eines Trägers geladen, das Übertragen der Daten und deren Materialisierung hat 2 sec gedauert. Ich würde vorschlagen dass Du mit Deiner Lösung weitermachst und erst optimierst wenn Probleme auftauchen.

andreas-82 Themenstarter:in
42 Beiträge seit 2006
vor 13 Jahren

Danke schon mal für eure Antworten.

Vll hilft dir das auch etwas bei der Performance (Lazy Loading)
Klick Mich Lazy Loading steht auf false.
In meinem (Lern)Beispiel wollte ich mich mit eager loading auseinandersetzen.
...so allgemein formuliert ist es nicht richtig. Wenn er Objektgraphen mit Include() erzeugen will muß das Root-Entity vollständig sein. War zumindest in EF1 so. Wenn er nur "Daten aus einer einzelnen Tabelle" benötigt kann er mit anonymen Objekten / EntityDataReader arbeiten.

Das ist ja das was mich wundert, dass sobald ich ein include setze, dass Root-Entity vollständig zurückgegeben wird.
Wenn ich anstelle des Includes ein Nested Querie verwende, werden auch im Root-Entity nur die im Select-Block angegebenen Spalten ausgelesen:

var result = from c in context.Contacts.Include("Addresses")
                     select new { c.LastName,
                                  c.ContactID, 
                                  City = ((from a in c.Addresses 
                                           where a.ContactID == c.ContactID 
                                           select a.City).FirstOrDefault()) };

SQL-Profiler:

SELECT 
[Extent1].[ContactID] AS [ContactID], 
[Extent1].[LastName] AS [LastName], 
(SELECT TOP (1) 
	[Extent2].[City] AS [City]
	FROM [dbo].[Address] AS [Extent2]
	WHERE ([Extent1].[ContactID] = [Extent2].[ContactID]) AND ([Extent2].[ContactID] = [Extent1].[ContactID])) AS [C1]
FROM [dbo].[Contact] AS [Extent1]

Ich habe in einer App alle 5000 Patienten aller Stationen aller Krankenhäuser eines Trägers geladen, das Übertragen der Daten und deren Materialisierung hat 2 sec gedauert. Ich würde vorschlagen dass Du mit Deiner Lösung weitermachst und erst optimierst wenn Probleme auftauchen. Prinzipiell hast du recht - auch wenn ich alles immer so perfekt wie möglich machen möchte - ich denke die Geschwindigkeitsunterschiede wird man auch bei mehreren Datensätzen nicht merken.
Wundern tut mich die Sache jedoch trotzdem noch 😭

~ There's no knowledge that is not power~

W
955 Beiträge seit 2010
vor 13 Jahren

Hi,

Das ist ja das was mich wundert, dass sobald ich ein include setze, dass Root-Entity vollständig zurückgegeben wird.

Ein ORM hat nunmal die Aufgabe dem Client die Illusion eines unendlichen, nichtflüchtigen Workspace seiner Objekte zu geben. Du willst -so hast Du es modelliert- genau diese Objekte haben und nicht nur mit Fragmenten arbeiten.
Wenn Du Enitities z.B. für Reporting "shapen" willst dann nimm doch diese nested queries oder die Join-Syntax.
Wenn Du es verwenden willst um beispielsweise ein teures Memofeld auszublenden was Du jetzt nicht benötigst, wird es schwierig, da Property-LazyLoading nicht unterstützt wird. Möglicherweise hilft vertikale Partitionierung der Entities oder Vererbung mit TablePerHierarchy.

Wenn ich anstelle des Includes ein Nested Querie verwende, werden auch im Root-Entity nur die im Select-Block angegebenen Spalten ausgelesen: Mag sein, aber die Daten sind dann nicht mehr aktualisierbar.