Поделиться через


Индексы

Индексы — это общая концепция во многих хранилищах данных. Хотя их реализация в хранилище данных может различаться, они используются для обеспечения более эффективного поиска на основе столбца (или набора столбцов). Дополнительные сведения об использовании хороших индексов см. в разделе "Индексы" в документации по производительности.

Можно указать индекс по столбцу следующим образом:

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

Примечание.

По соглашению индекс создается в каждом свойстве (или наборе свойств), которые используются в качестве внешнего ключа.

Составной индекс

Индекс также может охватывать несколько столбцов:

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

Индексы по нескольким столбцам, также известные как составные индексы, ускоряют запросы, которые фильтруют столбцы индекса, но и запросы, которые фильтруют только по первым столбцам, охваченным индексом. Дополнительные сведения см. в документации по производительности .

Уникальность индекса

По умолчанию индексы не являются уникальными: несколько строк могут иметь одинаковые значения для набора столбцов индекса. Индекс можно сделать уникальным следующим образом:

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

Попытка вставки нескольких сущностей с одинаковыми значениями для набора столбцов индекса приведет к возникновению исключения.

Порядок сортировки индекса

Примечание.

Эта функция представлена в EF Core 7.0.

В большинстве баз данных каждый столбец, охватываемый индексом, может быть как по возрастанию, так и по убыванию. Для индексов, охватывающих только один столбец, обычно это не имеет значения: база данных может пройти по индексу в обратном порядке по мере необходимости. Однако для составных индексов порядок может иметь решающее значение для хорошей производительности и может означать разницу между индексом, используемым запросом или нет. Как правило, порядок сортировки столбцов индекса должен соответствовать указанному в предложении запроса ORDER BY.

Порядок сортировки индекса по умолчанию возрастает. Вы можете сделать все столбцы упорядоченными по убыванию следующим образом:

[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; }
}

Можно также указать порядок сортировки по столбцам следующим образом:

[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; }
}

Именование индексов и множество индексов

По соглашению индексы, созданные в реляционной базе данных, называются IX_<type name>_<property name>. Для составных индексов <property name> становится списком имен свойств, разделённым подчеркиванием.

Имя индекса, созданного в базе данных, можно задать:

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

Обратите внимание, что если вы вызываете HasIndex несколько раз для одного и того же набора свойств, это продолжает настраивать один индекс, а не создавать новый.

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();

Так как второй HasIndex вызов переопределяет первый, он создает только один убывающий индекс. Это может быть полезно для дальнейшей настройки индекса, созданного соглашением.

Чтобы создать несколько индексов по тому же набору свойств, передайте имя в HasIndex, которое будет использоваться для идентификации индекса в модели EF и чтобы отличить его от других индексов, использующих те же свойства.

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();

Обратите внимание, что это имя также используется в качестве значения по умолчанию для имени базы данных, поэтому явное вызов HasDatabaseName не требуется.

Фильтр индекса

Некоторые реляционные базы данных позволяют указать отфильтрованный или частичный индекс. Это позволяет индексировать только подмножество значений столбца, уменьшая размер индекса и повышая производительность и использование дискового пространства. Дополнительные сведения об отфильтрованных индексах SQL Server, см. в документации.

API Fluent можно использовать для указания фильтра в индексе, предоставленного в виде выражения SQL:

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

При использовании поставщика SQL Server EF добавляет 'IS NOT NULL' фильтр для всех столбцов с NULL-значением, которые являются частью уникального индекса. Чтобы переопределить это соглашение, можно указать null значение.

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

Включенные столбцы

Некоторые реляционные базы данных позволяют настроить набор столбцов, которые включаются в индекс, но не являются частью его ключа. Это может значительно повысить производительность запросов, если все столбцы в запросе включены в индекс как ключевые или неключевые столбцы, так как к самой таблице не требуется обращаться. Дополнительные сведения о включенных столбцах SQL Server см. в документации.

В следующем примере Url столбец является частью ключа индекса, поэтому любой фильтр запросов в этом столбце может использовать индекс. Кроме того, запросы, обращающиеся только к столбцам Title и PublishedOn, не требуют доступа к таблице и будут работать более эффективно.

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

Проверка ограничений

Ограничения проверки — это стандартная реляционная функция, которая позволяет определить условие, которое должно храниться для всех строк в таблице; любая попытка вставки или изменения данных, которые нарушают ограничение, завершится ошибкой. Ограничения проверки похожи на ограничения, отличные от NULL (которые запрещают значения NULL в столбце) или уникальные ограничения (которые запрещают дубликаты), но позволяют определять произвольное выражение SQL.

Api Fluent можно использовать для указания ограничения проверки таблицы, предоставленного в виде выражения SQL:

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

Несколько ограничений проверки можно определить в одной таблице, каждая из которых имеет собственное имя.

Примечание. Некоторые распространенные ограничения проверки можно настроить с помощью пакета community EFCore.CheckConstraints.