Compartir a través de


Índices

Los índices son un concepto común en muchos almacenes de datos. Aunque su implementación en el almacén de datos puede variar, se usan para hacer búsquedas basadas en una columna (o conjunto de columnas) más eficaz. Consulte la sección de índices en la documentación de rendimiento para obtener más información sobre el uso eficaz de índices.

Puede especificar un índice sobre una columna de la siguiente manera:

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

Nota:

Por convención, se crea un índice en cada propiedad (o conjunto de propiedades) que se usan como clave externa.

Índice compuesto

Un índice también puede abarcar más de una columna:

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

Los índices de varias columnas, también denominados índices compuestos, aceleran las consultas que filtran por las columnas del índice, pero también las consultas que solo filtran por las primeras columnas que cubre el índice. Para más información, vea la documentación de rendimiento.

Unicidad del índice

De forma predeterminada, los índices no son únicos: se permite que varias filas tengan los mismos valores para el conjunto de columnas del índice. Puede hacer que un índice sea único de la siguiente manera:

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

Si intenta insertar más de una entidad con los mismos valores para el conjunto de columnas del índice, se producirá una excepción.

Criterio de ordenación de índice

En la mayoría de las bases de datos, cada columna cubierta por un índice puede ser ascendente o descendente. En el caso de los índices que abarcan solo una columna, esto normalmente no importa: la base de datos puede atravesar el índice en orden inverso según sea necesario. Sin embargo, para los índices compuestos, la ordenación puede ser fundamental para un buen rendimiento y puede significar la diferencia entre un índice que se usa en una consulta o no. En general, los órdenes de clasificación de las columnas de índice deben corresponder a aquellos especificados en la cláusula ORDER BY de tu consulta.

El criterio de ordenación de índice es ascendente de forma predeterminada. Puede hacer que todas las columnas tengan un orden descendente de la siguiente manera:

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

También puede especificar el criterio de ordenación en una columna por columna de la siguiente manera:

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

Nomenclatura de índices y varios índices

Por convención, los índices creados en una base de datos relacional se denominan IX_<type name>_<property name>. En cuanto a los índices compuestos, <property name> se convierte en una lista de nombres de propiedad separados por caracteres de subrayado.

Puede establecer el nombre del índice creado en la base de datos:

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

Tenga en cuenta que si llama a HasIndex más de una vez en el mismo conjunto de propiedades, sigue configurando un único índice en lugar de crear uno nuevo:

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

Dado que la segunda HasIndex llamada invalida la primera, esto crea un único índice descendente. Esto puede ser útil para configurar aún más un índice creado por convención.

Para crear varios índices en el mismo conjunto de propiedades, pase un nombre al HasIndex, que se usará para identificar el índice en el modelo de EF y para distinguirlo de otros índices en las mismas propiedades:

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

Tenga en cuenta que este nombre también se usa como valor predeterminado para el nombre de la base de datos, por lo que no es necesario llamar explícitamente a HasDatabaseName.

Filtro de índice

Algunas bases de datos relacionales permiten especificar un índice filtrado o parcial. Esto permite indexar solo un subconjunto de valores de una columna, lo que reduce el tamaño del índice y mejora el rendimiento y el uso del espacio en disco. Para obtener más información sobre los índices filtrados de SQL Server, consulte la documentación.

Puede usar fluent API para especificar un filtro en un índice, proporcionado como expresión SQL:

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

Cuando se usa el proveedor de SQL Server, EF agrega un filtro 'IS NOT NULL' para todas las columnas anulables que forman parte de un índice único. Para invalidar esta convención, puede proporcionar un valor de null.

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

Columnas incluidas

Algunas bases de datos relacionales permiten configurar un conjunto de columnas que se incluyen en el índice, pero no forman parte de su "clave". Esto puede mejorar significativamente el rendimiento de las consultas cuando todas las columnas de la consulta se incluyen en el índice como columnas clave o no clave, ya que no es necesario tener acceso a la propia tabla. Para obtener más información sobre las columnas incluidas de SQL Server, consulte la documentación.

En el ejemplo siguiente, la columna Url forma parte de la clave de índice, por lo que cualquier filtrado de consultas en esa columna puede usar el índice. Pero además, las consultas que acceden solo a las columnas Title y PublishedOn no tendrán que acceder a la tabla y se ejecutarán de forma más eficaz:

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

Comprobación de restricciones

Las restricciones Check son una característica relacional estándar que permite definir una condición que debe contener todas las filas de una tabla; cualquier intento de insertar o modificar datos que infringen la restricción producirá un error. Las restricciones Check son similares a las restricciones que no son NULL (que prohíben valores NULL en una columna) o a restricciones únicas (que prohíben duplicados), pero permiten definir expresiones SQL arbitrarias.

Puede usar fluent API para especificar una restricción check en una tabla, proporcionada como expresión SQL:

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

Se pueden definir varias restricciones de verificación en la misma tabla, cada una con su propio nombre.

Nota: algunas restricciones de comprobación comunes se pueden configurar a través del paquete de comunidad EFCore.CheckConstraints.