Cree un índice no exclusivo no agrupado dentro de la instrucción CREATE TABLE con SQL Server

92

Es posible crear una clave principal o un índice único dentro de una instrucción CREATE TABLE de SQL Server. ¿Es posible crear un índice no único dentro de una instrucción CREATE TABLE?

CREATE TABLE MyTable(
    a int NOT NULL
    ,b smallint NOT NULL
    ,c smallint NOT NULL
    ,d smallint NOT NULL
    ,e smallint NOT NULL

    -- This creates a primary key
    ,CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED (a)

    -- This creates a unique nonclustered index on columns b and c
    ,CONSTRAINT IX_MyTable1 UNIQUE (b, c)

    -- Is it possible to create a non-unique index on columns d and e here?
    -- Note: these variations would not work if attempted:
    -- ,CONSTRAINT IX_MyTable2 INDEX (d, e)
    -- ,CONSTRAINT IX_MyTable3 NONCLUSTERED INDEX (d, e)
);
GO

-- The proposed non-unique index should behave identically to
-- an index created after the CREATE TABLE statement. Example:
CREATE NONCLUSTERED INDEX IX_MyTable4 ON MY_TABLE (d, e);
GO

Nuevamente, el objetivo es crear el índice no único dentro de la instrucción CREATE TABLE, no después de ella.

Por lo que vale, no encontré que la [entrada de los Libros en pantalla de SQL Server para CREAR TABLA] sea ​​útil.

Además, [esta pregunta] es casi idéntica, pero la respuesta aceptada no se aplica.

Miguel
fuente

Respuestas:

122

No puedes. CREATE / ALTER TABLE solo acepta CONSTRAINTs que se agregarán, no índices. El hecho de que la clave principal y las restricciones únicas se implementen en términos de un índice es un efecto secundario. Para administrar índices, tiene CREATE / ALTER / DROP INDEX, como bien sabe.

¿Por qué tiene el requisito de agregar índices no únicos y no agrupados en la declaración CREATE TABLE?

Tenga en cuenta que SQL Server 2014 introdujo la opción de creación de índice en línea :

CREATE TABLE MyTable(
    a int NOT NULL
    ,b smallint NOT NULL
    ,c smallint NOT NULL
    ,d smallint NOT NULL
    ,e smallint NOT NULL

    -- This creates a primary key
    ,CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED (a)

    -- This creates a unique nonclustered index on columns b and c
    ,CONSTRAINT IX_MyTable1 UNIQUE (b, c)

    -- This creates a non-clustered index on (d, e)
    ,INDEX IX_MyTable4 NONCLUSTERED (d, e)
);
GO
Remus Rusanu
fuente
17
¡Gracias por la grandiosa explicación! ¿Por qué? Meramente por motivos estéticos. Pensé que sería conveniente para cualquiera que lea el script si todas las restricciones / índices están contenidos en la misma declaración. Personalmente, me gusta saber si las columnas que pertenecen a una clave externa también tienen un índice, y este puede haber sido un buen método para agrupar lógicamente esta información en la misma declaración.
Mike
Tengo Error: (1146) Table 'tablename' doesn't exist, jajaja, irónico
Aminah Nuraini
13

Según la documentación CREATE TABLE de T-SQL , en 2014 la definición de columna admite la definición de un índice:

<column_definition> ::=  
column_name <data_type>  
    ...
    [ <column_index> ]  

y la gramática se define como:

<column_index> ::=   
 INDEX index_name [ CLUSTERED | NONCLUSTERED ]  
    [ WITH ( <index_option> [ ,... n ] ) ]  
    [ ON { partition_scheme_name (column_name )   
         | filegroup_name  
         | default   
         }  
    ]   
    [ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]  

Por lo tanto, mucho de lo que puede hacer como una declaración separada se puede hacer en línea. Noté includeque no es una opción en esta gramática, por lo que algunas cosas no son posibles.

CREATE TABLE MyTable(
    a int NOT NULL
    ,b smallint NOT NULL index IX_MyTable_b nonclustered
    ,c smallint NOT NULL
    ,d smallint NOT NULL
    ,e smallint NOT NULL
)

También puede tener índices en línea definidos como otra línea después de las columnas, pero dentro de la declaración de creación de tabla, y esto permite múltiples columnas en el índice, pero aún sin includecláusula:

< table_index > ::=   
{  
    {  
      INDEX index_name [ CLUSTERED | NONCLUSTERED ]   
         (column_name [ ASC | DESC ] [ ,... n ] )   
    | INDEX index_name CLUSTERED COLUMNSTORE  
    | INDEX index_name [ NONCLUSTERED ] COLUMNSTORE (column_name [ ,... n ] )  
    }  
    [ WITH ( <index_option> [ ,... n ] ) ]   
    [ ON { partition_scheme_name (column_name )   
         | filegroup_name  
         | default   
         }  
    ]   
    [ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]  

}   

Por ejemplo, aquí agregamos un índice en ambas columnas c y d:

CREATE TABLE MyTable(
    a int NOT NULL
    ,b smallint NOT NULL index IX_MyTable_b nonclustered
    ,c smallint NOT NULL
    ,d smallint NOT NULL
    ,e smallint NOT NULL

    ,index IX_MyTable_c_d nonclustered (c,d)
)
AaronLS
fuente
1
Esto funciona muy bien y, según BOL, al menos se remonta a 2008. Al igual que el OP, encuentro que el código que veo principalmente (generalmente generado por SSMS) hace que me duelan los ojos, y simplemente me gusta que las definiciones de mi tabla tengan sentido para un humano racional. Gracias.
Wade Hatler
8

Es una declaración separada.

Tampoco es posible insertar en una tabla y seleccionar de ella y construir un índice en la misma declaración.

La entrada BOL contiene la información que necesita:

AGRUPADO | NO CLUSTERADO
Indica que se crea un índice agrupado o no agrupado para la restricción PRIMARY KEY o UNIQUE. Las restricciones PRIMARY KEY se establecen de forma predeterminada en CLÚSTER y las restricciones ÚNICAS en NO CLÚSTER.

En una instrucción CREATE TABLE, CLUSTER se puede especificar para una sola restricción. Si se especifica CLUSjected para una restricción UNIQUE y también se especifica una restricción PRIMARY KEY, PRIMARY KEY se establece de forma predeterminada en NONCLUSjected.

Puede crear un índice en un campo PK, pero no un índice no agrupado en un campo restringido no único que no sea pk.

Un índice NCL no es relevante para la estructura de la tabla y no es una restricción sobre los datos dentro de la tabla. Es una entidad separada que soporta la mesa pero no es parte integral de su funcionalidad o diseño.

Por eso es una declaración separada. El índice NCL es irrelevante para la tabla desde una perspectiva de diseño (a pesar de la optimización de consultas).

JNK
fuente
7

La respuesta aceptada de cómo crear un índice en línea, un script de creación de tablas no funcionó para mí. Esto hizo:

CREATE TABLE [dbo].[TableToBeCreated]
(
    [Id] BIGINT IDENTITY(1, 1) NOT NULL PRIMARY KEY
    ,[ForeignKeyId] BIGINT NOT NULL
    ,CONSTRAINT [FK_TableToBeCreated_ForeignKeyId_OtherTable_Id] FOREIGN KEY ([ForeignKeyId]) REFERENCES [dbo].[OtherTable]([Id])
    ,INDEX [IX_TableToBeCreated_ForeignKeyId] NONCLUSTERED ([ForeignKeyId])
)

Recuerde, las claves externas no crean índices, por lo que es una buena práctica indexarlas, ya que es muy probable que se una a ellas.

ScubaSteve
fuente
No estoy siguiendo esa última declaración. Estoy de acuerdo con esa declaración si es una práctica habitual consultar su tabla por la clave externa; pero no simplemente que te unas a él, por lo tanto, debería estar indexado. Ejemplo: busque todos los empleados y el nombre de su empresa o la identificación de la empresa X, luego asegúrese de que un índice en el FK ayude. Busque todos los empleados y el nombre de su empresa cuyo apellido comience con A; índice en el FK no ayuda. En otras palabras, no estoy seguro de que "porque te unes a él, deberías indexarlo" sea una buena práctica. ¿Me estoy perdiendo de algo?
Paul
2
Los índices hacen que las consultas de combinación sean más rápidas.
ScubaSteve