Crie um índice não exclusivo não clusterizado na instrução CREATE TABLE com o SQL Server

92

É possível criar uma chave primária ou índice exclusivo em uma instrução CREATE TABLE do SQL Server. É possível criar um índice não exclusivo em uma instrução 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

Novamente, o objetivo é criar o índice não exclusivo dentro da instrução CREATE TABLE, não depois dele.

Pelo que vale a pena, não achei a [entrada do SQL Server Books Online para CREATE TABLE] útil.

Além disso, [esta pergunta] é quase idêntica, mas a resposta aceita não se aplica.

Mike
fonte

Respostas:

123

Você não pode. CREATE / ALTER TABLE aceita apenas CONSTRAINTs a serem adicionados, não índices. O fato de a chave primária e as restrições exclusivas serem implementadas em termos de um índice é um efeito colateral. Para gerenciar índices, você tem CREATE / ALTER / DROP INDEX, como você bem sabe.

Por que você tem o requisito de adicionar índices não exclusivos e não agrupados na instrução CREATE TABLE?

Observe que o SQL Server 2014 introduziu a opção de criação de índice embutido :

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
fonte
17
Obrigado pela ótima explicação! Por quê? Puramente por razões estéticas. Achei que seria conveniente para qualquer pessoa que estivesse lendo o script se todas as restrições / índices estivessem contidos na mesma instrução. Pessoalmente, gosto de saber se as colunas pertencentes a uma chave estrangeira também têm um índice, e esse pode ter sido um bom método para agrupar logicamente essas informações na mesma instrução.
Mike
Recebi Error: (1146) Table 'tablename' doesn't exist, hahahah, irônico
Aminah Nuraini
13

De acordo com a documentação CREATE TABLE do T-SQL , em 2014 a definição da coluna suporta a definição de um índice:

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

e a gramática é definida 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" } ]  

Portanto, muito do que você pode fazer como uma instrução separada pode ser feito inline. Percebi que includenão é uma opção nesta gramática, então algumas coisas não são possíveis.

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
)

Você também pode ter índices inline definidos como outra linha após as colunas, mas dentro da instrução create table, e isso permite várias colunas no índice, mas ainda nenhuma 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 exemplo, aqui adicionamos um índice em ambas as colunas c e 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
fonte
1
Isso funciona muito bem e, de acordo com o BOL, remonta a 2008, pelo menos. Como o OP, acho que o código que vejo principalmente (geralmente gerado por SSMS) faz meus olhos doerem, e gosto que minhas definições de tabela façam sentido para um ser humano racional. Obrigado.
Wade Hatler
8

É uma declaração separada.

Também não é possível inserir em uma tabela e selecionar a partir dela e construir um índice na mesma instrução.

A entrada BOL contém as informações de que você precisa:

CLUSTERED | NONCLUSTERED
Indica que um índice clusterizado ou não clusterizado é criado para a restrição PRIMARY KEY ou UNIQUE. As restrições PRIMARY KEY são padronizadas como CLUSTERED e as restrições UNIQUE são padronizadas como NONCLUSTERED.

Em uma instrução CREATE TABLE, CLUSTERED pode ser especificado para apenas uma restrição. Se CLUSTERED for especificado para uma restrição UNIQUE e uma restrição PRIMARY KEY também for especificada, o padrão de PRIMARY KEY será NONCLUSTERED.

Você pode criar um índice em um campo PK, mas não um índice não clusterizado em um campo não restrito não exclusivo não PK.

Um índice NCL não é relevante para a estrutura da tabela e não é uma restrição aos dados dentro da tabela. É uma entidade separada que suporta a tabela, mas não é parte integrante de sua funcionalidade ou design.

É por isso que é uma declaração separada. O índice NCL é irrelevante para a tabela do ponto de vista do projeto (apesar da otimização da consulta).

JNK
fonte
7

A resposta aceita de como criar um índice embutido em um script de criação de tabela não funcionou para mim. Isso fez:

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

Lembre-se de que as chaves estrangeiras não criam índices, portanto, é uma boa prática indexá-los, pois é mais do que provável que você se junte a eles.

ScubaSteve
fonte
Não estou seguindo essa última declaração. Eu concordaria com essa declaração se for prática comum consultar sua tabela pela chave estrangeira; mas não simplesmente que você se junte a ele, portanto, ele deve ser indexado. Exemplo: encontre todos os funcionários e o nome de sua empresa com o ID da empresa X - e certifique-se de que um índice no FK o ajude. Encontre todos os funcionários e o nome da empresa com o sobrenome começando com A; índice no FK não ajuda. Em outras palavras, não tenho certeza se "porque você se inscreve nele, você deve indexá-lo" é uma boa prática. Estou esquecendo de algo?
Paul de
2
Os índices tornam as consultas de junção mais rápidas.
ScubaSteve