인덱스

인덱스는 여러 데이터 저장소에서 일반적인 개념입니다. 데이터 저장소에서의 구현은 다를 수 있지만 열(또는 열 집합)을 기반으로 조회를 보다 효율적으로 만드는 데 사용됩니다. 좋은 인덱스 사용에 관한 자세한 내용을 보려면 성능 설명서에서 인덱스 섹션을 참조하세요.

다음과 같이 열에 대한 인덱스를 지정할 수 있습니다.

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

인덱스의 열 집합에 대해 동일한 값을 사용하여 둘 이상의 엔터티를 삽입하려고 하면 예외가 throw됩니다.

인덱스 정렬 순서

참고 항목

이 기능은 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<Blog>()
    .HasIndex(b => new { b.FirstName, b.LastName })
    .HasDatabaseName("IX_Names_Ascending");

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

두 번째 HasIndex 호출은 첫 번째 호출을 재정의하므로 단일 내림차순 인덱스만 만듭니다. 이 기능은 규칙에서 만든 인덱스 추가 구성에 유용할 수 있습니다.

동일한 속성 집합에 대해 여러 인덱스를 만들려면 EF 모델에서 인덱스를 식별하고 동일한 속성에 대한 다른 인덱스와 구분하는 데 사용되는 HasIndex에 이름을 전달합니다.

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

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

이 이름은 데이터베이스 이름의 기본값으로도 사용되므로 명시적으로 HasDatabaseName을(를) 호출할 필요가 없습니다.

인덱스 필터

일부 관계형 데이터베이스에서는 필터링된 인덱스 또는 부분 인덱스를 지정할 수 있습니다. 이렇게 하면 열 값의 하위 집합만 인덱싱할 수 있으므로 인덱스의 크기를 줄이고 성능과 디스크 공간 사용을 모두 향상할 수 있습니다. SQL Server 필터링된 인덱스에 대한 자세한 내용은 설명서를 참조하세요.

Fluent API를 사용하여 SQL 식으로 제공되는 인덱스에 필터를 지정할 수 있습니다.

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

SQL Server 공급자를 사용하는 경우 EF는 고유한 인덱스의 일부인 null 허용 열에 대한 'IS NOT NULL' 필터를 추가합니다. 이 규칙을 재정의하기 위해 null 값을 제공할 수 있습니다.

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

포함된 열

일부 관계형 데이터베이스를 사용하면 인덱스에 포함되지만 “키”의 일부가 아닌 열 집합을 구성할 수 있습니다. 이렇게 하면 테이블 자체에 액세스할 필요가 없으므로 쿼리의 모든 열이 키 또는 키가 아닌 열로 인덱스에 포함될 때 쿼리 성능이 크게 향상될 수 있습니다. SQL Server 포함된 열에 대한 자세한 내용은 설명서를 참조하세요.

다음 예에서는 Url 열이 인덱스 키의 일부이므로 해당 열에 대한 모든 쿼리 필터링에서 인덱스를 사용할 수 있습니다. 하지만 TitlePublishedOn 열만 액세스하는 쿼리는 테이블에 액세스할 필요가 없으므로 더 효율적으로 실행됩니다.

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

CHECK 제약 조건

check 제약 조건은 테이블의 모든 행에 대해 보유해야 하는 조건을 정의할 수 있는 표준 관계형 기능입니다. 제약 조건을 위반하는 데이터를 삽입하거나 수정하려고 하면 실패합니다. check 제약 조건은 null이 아닌 제약 조건(열에서 null을 금지함) 또는 고유 제약 조건(중복을 금지함)과 유사하지만 임의의 SQL 식을 정의할 수 있습니다.

Fluent API를 사용하여 SQL 식으로 제공되는 테이블에 check 제약 조건을 지정할 수 있습니다.

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

여러 check 제약 조건은 각각 고유한 이름을 가진 동일한 테이블에 정의될 수 있습니다.

참고: 일부 일반적인 check 제약 조건은 커뮤니티 패키지 EFCore.CheckConstraints를 통해 구성할 수 있습니다.