Freigeben über


Indizes

Indizes sind ein gängiges Konzept für viele Datenspeicher. Während die Implementierung im Datenspeicher variieren kann, werden sie verwendet, um Nachschlagevorgänge basierend auf einer Spalte (oder einer Reihe von Spalten) effizienter zu gestalten. Weitere Informationen zur guten Indexverwendung finden Sie im Abschnitt "Indizes" in der Leistungsdokumentation.

Sie können einen Index über einer Spalte wie folgt angeben:

[Index(nameof(Url))]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Hinweis

Standardmäßig wird in jeder Eigenschaft (oder einem Eigenschaftensatz) ein Index erstellt, der als Fremdschlüssel verwendet wird.

Zusammengesetzter Index

Ein Index kann auch mehrere Spalten umfassen:

[Index(nameof(FirstName), nameof(LastName))]
public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Indizes über mehrere Spalten, auch als zusammengesetzte Indizes bezeichnet, beschleunigen Abfragen, die nach Indexspalten filtern, aber auch Abfragen, die nur nach den ersten Spalten filtern, die vom Index abgedeckt werden. Weitere Informationen finden Sie in den Leistungsdokumenten .

Eindeutigkeit des Indexes

Standardmäßig sind Indizes nicht eindeutig: Mehrere Zeilen dürfen für den Spaltensatz des Indexes denselben Wert aufweisen. Sie können einen Index wie folgt eindeutig machen:

[Index(nameof(Url), IsUnique = true)]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Wenn Sie versuchen, mehrere Entitäten mit denselben Werten für den Spaltensatz des Indexes einzufügen, wird eine Ausnahme ausgelöst.

Sortierreihenfolge des Index

In den meisten Datenbanken kann jede Spalte, die von einem Index abgedeckt wird, entweder aufsteigend oder absteigend sein. Bei Indizes, die nur eine Spalte abdecken, spielt dies in der Regel keine Rolle: Die Datenbank kann den Index bei Bedarf in umgekehrter Reihenfolge durchlaufen. Bei zusammengesetzten Indizes kann die Sortierung jedoch für eine gute Leistung von entscheidender Bedeutung sein und kann den Unterschied zwischen einem Index bedeuten, der von einer Abfrage verwendet wird oder nicht. Im Allgemeinen sollten die Sortierreihenfolgen der Indexspalten denen entsprechen, die in der ORDER BY Klausel Der Abfrage angegeben sind.

Die Indexsortierreihenfolge ist standardmäßig aufsteigend. Sie können festlegen, dass alle Spalten wie folgt absteigend angeordnet sind:

[Index(nameof(Url), nameof(Rating), AllDescending = true)]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public int Rating { get; set; }
}

Sie können auch die Sortierreihenfolge auf Spaltenbasis wie folgt angeben:

[Index(nameof(Url), nameof(Rating), IsDescending = new[] { false, true })]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public int Rating { get; set; }
}

Benennung von Indexen und mehrere Indizes

In der Konvention werden in einer relationalen Datenbank erstellte Indizes benannt IX_<type name>_<property name>. Bei zusammengesetzten Indizes wird <property name> zu einer durch Unterstriche getrennten Liste von Eigenschaftennamen.

Sie können den Namen des in der Datenbank erstellten Index festlegen:

[Index(nameof(Url), Name = "Index_Url")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Beachten Sie, dass wenn Sie HasIndex mehr als einmal für dieselbe Gruppe von Eigenschaften aufrufen, dies weiterhin einen einzelnen Index konfiguriert, anstatt einen neuen zu erstellen.

modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.FirstName, p.LastName })
    .HasDatabaseName("IX_Names_Ascending");

modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.FirstName, p.LastName })
    .HasDatabaseName("IX_Names_Descending")
    .IsDescending();

Da der zweite HasIndex Aufruf den ersten überschreibt, wird dadurch nur ein einzelner absteigender Index erstellt. Ein standardmäßig erstellter Index kann durch diese Vorgangsweise weiter konfiguriert werden.

Um mehrere Indizes über denselben Eigenschaftensatz zu erstellen, übergeben Sie einen Namen an den HasIndex, der zum Identifizieren des Indexes im EF-Modell verwendet wird, und um ihn von anderen Indizes über die gleichen Eigenschaften zu unterscheiden:

modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.FirstName, p.LastName }, "IX_Names_Ascending");

modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.FirstName, p.LastName }, "IX_Names_Descending")
    .IsDescending();

Beachten Sie, dass dieser Name auch als Standard für den Datenbanknamen verwendet wird, sodass explizite Aufrufe HasDatabaseName nicht erforderlich sind.

Indexfilter

In einigen relationalen Datenbanken können Sie einen gefilterten oder teilweisen Index angeben. Auf diese Weise können Sie nur eine Teilmenge der Werte einer Spalte indizieren, die Größe des Indexes verringern und sowohl die Leistung als auch die Speicherplatznutzung verbessern. Weitere Informationen zu gefilterten SQL Server-Indizes finden Sie in der Dokumentation.

Sie können die Fluent-API verwenden, um einen Filter für einen Index anzugeben, der als SQL-Ausdruck bereitgestellt wird:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => b.Url)
        .HasFilter("[Url] IS NOT NULL");
}

Bei Verwendung des SQL Server-Anbieters fügt EF einen 'IS NOT NULL' Filter für alle nullfähigen Spalten hinzu, die Teil eines eindeutigen Indexes sind. Um diese Konvention außer Kraft zu setzen, können Sie einen null Wert angeben.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => b.Url)
        .IsUnique()
        .HasFilter(null);
}

Enthaltene Spalten

Einige relationale Datenbanken ermöglichen es Ihnen, einen Satz von Spalten zu konfigurieren, die im Index enthalten sind, aber nicht Teil des "Schlüssels". Dies kann die Abfrageleistung erheblich verbessern, wenn alle Spalten in der Abfrage entweder als Schlüssel- oder Nichtschlüsselspalten im Index enthalten sind, da auf die Tabelle selbst nicht zugegriffen werden muss. Weitere Informationen zu sql Server enthaltenen Spalten finden Sie in der Dokumentation.

Im folgenden Beispiel ist die Url Spalte Teil des Indexschlüssels, sodass jede Abfragefilterung für diese Spalte den Index verwenden kann. Darüber hinaus müssen Abfragen, die nur auf die Title und PublishedOn Spalten zugreifen, nicht auf die Tabelle zugreifen und werden effizienter ausgeführt.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasIndex(p => p.Url)
        .IncludeProperties(
            p => new { p.Title, p.PublishedOn });
}

Einschränkungen überprüfen

Prüfklauseln sind eine standardmäßige relationale Funktion, die es Ihnen ermöglicht, eine Bedingung zu definieren, die für alle Zeilen in einer Tabelle gelten muss; jeder Versuch, Daten einzufügen oder zu ändern, die gegen die Einschränkung verstoßen, wird fehlschlagen. Überprüfungsbeschränkungen ähneln Nicht-Null-Beschränkungen (die Nullwerte in einer Spalte verbieten) oder eindeutigen Beschränkungen (die Duplikate verbieten), erlauben jedoch die Definition beliebiger SQL-Ausdrücke.

Sie können die Fluent-API verwenden, um eine Prüfeinschränkung für eine Tabelle anzugeben, die als SQL-Ausdruck bereitgestellt wird:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Product>()
        .ToTable(b => b.HasCheckConstraint("CK_Prices", "[Price] > [DiscountedPrice]"));
}

Mehrere Prüfeinschränkungen können in derselben Tabelle definiert werden, jeweils mit ihrem eigenen Namen.

Hinweis: Einige gängige Check-Einschränkungen können über das Communitypaket EFCore.CheckConstraints konfiguriert werden.