Créer un index non unique non clusterisé dans l'instruction CREATE TABLE avec SQL Server

94

Il est possible de créer une clé primaire ou un index unique dans une instruction SQL Server CREATE TABLE. Est-il possible de créer un index non unique dans une instruction 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

Là encore, le but est de créer l'index non unique dans l'instruction CREATE TABLE, et non après.

Pour ce que ça vaut, je n'ai pas trouvé [l'entrée de la documentation en ligne de SQL Server pour CREATE TABLE] utile.

De plus, [Cette question] est presque identique, mais la réponse acceptée ne s'applique pas.

Mike
la source

Réponses:

124

Vous ne pouvez pas. CREATE / ALTER TABLE n'accepte que les CONTRAINTES à ajouter, pas les index. Le fait que la clé primaire et les contraintes uniques soient implémentées en termes d'index est un effet secondaire. Pour gérer les index, vous avez CREATE / ALTER / DROP INDEX, comme vous le savez bien.

Pourquoi avez-vous une telle exigence pour ajouter des index non uniques-non clusterisés dans l'instruction CREATE TABLE?

Notez que SQL Server 2014 a introduit l' option de création d'index en ligne :

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
la source
17
Merci pour l'excellente explication! Pourquoi? Pour des raisons purement esthétiques. J'ai pensé qu'il pourrait être pratique pour quiconque lisant le script si toutes les contraintes / index sont contenus dans la même instruction. Personnellement, j'aime savoir si les colonnes appartenant à une clé étrangère ont également un index, et cela peut avoir été une bonne méthode pour regrouper logiquement ces informations dans la même instruction.
Mike
J'ai eu Error: (1146) Table 'tablename' doesn't exist, hahahah, ironique
Aminah Nuraini
13

Selon la documentation de T-SQL CREATE TABLE , en 2014, la définition de colonne prend en charge la définition d'un index:

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

et la grammaire est définie comme:

<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" } ]  

Ainsi, une grande partie de ce que vous pouvez faire en tant qu'instruction distincte peut être effectuée en ligne. J'ai remarqué que ce includen'est pas une option dans cette grammaire, donc certaines choses ne sont pas possibles.

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
)

Vous pouvez également avoir des index en ligne définis comme une autre ligne après les colonnes, mais dans l'instruction create table, et cela autorise plusieurs colonnes dans l'index, mais toujours pas de includeclause:

< 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" } ]  

}   

Par exemple ici, nous ajoutons un index sur les deux colonnes c et 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
la source
1
Cela fonctionne très bien et, selon BOL, cela remonte au moins à 2008. Comme l'OP, je trouve que le code que je vois le plus souvent (généralement généré par SSMS) me fait mal aux yeux, et j'aime juste que mes définitions de table aient un sens pour un humain rationnel. Merci.
Wade Hatler
8

C'est une déclaration distincte.

Il n'est pas non plus possible d'insérer dans une table, de faire une sélection et de créer un index dans la même instruction.

L'entrée BOL contient les informations dont vous avez besoin:

CLUSTERED | NONCLUSTERED
Indique qu'un index clusterisé ou non clusterisé est créé pour la contrainte PRIMARY KEY ou UNIQUE. Les contraintes PRIMARY KEY par défaut sur CLUSTERED et les contraintes UNIQUE par défaut sur NONCLUSTERED.

Dans une instruction CREATE TABLE, CLUSTERED ne peut être spécifié que pour une seule contrainte. Si CLUSTERED est spécifié pour une contrainte UNIQUE et qu'une contrainte PRIMARY KEY est également spécifiée, PRIMARY KEY prend la valeur par défaut NONCLUSTERED.

Vous pouvez créer un index sur un champ PK, mais pas un index non clusterisé sur un champ non contraint non-pk non unique.

Un index NCL n'est pas pertinent pour la structure de la table et n'est pas une contrainte sur les données à l'intérieur de la table. C'est une entité distincte qui prend en charge la table mais qui ne fait pas partie intégrante de sa fonctionnalité ou de sa conception.

C'est pourquoi c'est une déclaration distincte. L'index NCL n'est pas pertinent pour la table du point de vue de la conception (nonobstant l'optimisation des requêtes).

JNK
la source
7

La réponse acceptée sur la façon de créer un index en ligne un script de création de table n'a pas fonctionné pour moi. Cela a fait:

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])
)

N'oubliez pas que les clés étrangères ne créent pas d'index, il est donc recommandé de les indexer, car vous les rejoindrez probablement.

ScubaSteve
la source
Je ne suis pas cette dernière déclaration. Je serais d'accord avec cette déclaration si c'est la pratique habituelle d'interroger votre table par la clé étrangère; mais pas simplement que vous vous y joigniez donc il devrait être indexé. Exemple: Trouvez tous les employés et le nom de leur entreprise de l'ID d'entreprise X - alors assurez-vous qu'un index sur le FK aide. Trouvez tous les employés et le nom de leur entreprise avec le nom commençant par A; l'index sur le FK n'aide pas. En d'autres termes, je ne suis pas sûr que "parce que vous y adhérez, vous devez l'indexer" soit une bonne pratique. Est-ce que je manque quelque chose?
Paul
2
Les index accélèrent les requêtes de jointure.
ScubaSteve