Utwórz nieklastrowy nieunikatowy indeks w instrukcji CREATE TABLE z SQL Server

95

Możliwe jest utworzenie klucza podstawowego lub unikalnego indeksu w instrukcji CREATE TABLE programu SQL Server. Czy jest możliwe utworzenie nieunikalnego indeksu w instrukcji 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

Ponownie, celem jest utworzenie nieunikalnego indeksu w instrukcji CREATE TABLE, a nie po niej.

Z tego powodu nie znalazłem [wpisu SQL Server Books Online dla CREATE TABLE] jako pomocnego.

Również [To pytanie] jest prawie identyczne, ale zaakceptowana odpowiedź nie ma zastosowania.

Mikrofon
źródło

Odpowiedzi:

126

Nie możesz. CREATE / ALTER TABLE akceptuje tylko OGRANICZENIA do dodania, a nie indeksy. Efektem ubocznym jest fakt, że klucz główny i ograniczenia unikatowe są implementowane w postaci indeksu. Do zarządzania indeksami służy CREATE / ALTER / DROP INDEX, o czym dobrze wiesz.

Dlaczego masz taki wymóg, aby dodać nieunikalne indeksy nieklastrowe w instrukcji CREATE TABLE?

Zwróć uwagę, że SQL Server 2014 wprowadził opcję tworzenia indeksu inline :

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
źródło
17
Dzięki za świetne wyjaśnienie! Czemu? Czysto ze względów estetycznych. Pomyślałem, że dla każdego czytającego skrypt byłoby wygodne, gdyby wszystkie ograniczenia / indeksy były zawarte w tej samej instrukcji. Osobiście lubię wiedzieć, czy kolumny należące do klucza obcego również mają indeks i może to być dobry sposób na logiczne pogrupowanie tych informacji w tej samej instrukcji.
Mike
Mam Error: (1146) Table 'tablename' doesn't exist, hahahah, ironiczne
Aminah Nuraini
13

Zgodnie z dokumentacją T-SQL CREATE TABLE w 2014 roku definicja kolumny umożliwia definiowanie indeksu:

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

a gramatyka jest definiowana jako:

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

Tak więc wiele z tego, co możesz zrobić jako oddzielne oświadczenie, można zrobić w tekście. Zauważyłem, że includenie ma takiej opcji w tej gramatyce, więc niektóre rzeczy nie są możliwe.

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
)

Możesz również zdefiniować indeksy wbudowane jako kolejny wiersz po kolumnach, ale w instrukcji create table, co pozwala na wiele kolumn w indeksie, ale nadal nie ma includeklauzuli:

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

}   

Na przykład tutaj dodajemy indeks do obu kolumn c i 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
źródło
1
Działa to świetnie i według BOL sięga przynajmniej 2008 roku. Podobnie jak w przypadku OP, uważam, że kod, który najczęściej widzę (zwykle generowany przez SSMS), sprawia, że ​​bolą mnie gałki oczne, i po prostu lubię, aby moje definicje tabel miały sens dla racjonalnego człowieka. Dzięki.
Wade Hatler
8

To osobne stwierdzenie.

Nie jest również możliwe wstawienie do tabeli, wybranie z niej i zbudowanie indeksu w tej samej instrukcji.

Wpis BOL zawiera potrzebne informacje:

KLASTEROWANE | NONCLUSTERED
Wskazuje, że dla ograniczenia PRIMARY KEY lub UNIQUE tworzony jest indeks klastrowy lub nieklastrowy. Ograniczenia PRIMARY KEY są domyślnie ustawione na CLUSTERED, a UNIQUE - na NONCLUSTERED.

W instrukcji CREATE TABLE parametr CLUSTERED można określić tylko dla jednego ograniczenia. Jeśli dla ograniczenia UNIQUE określono CLUSTERED i ograniczenie PRIMARY KEY, klucz podstawowy ma wartość domyślną NONCLUSTERED.

Możesz utworzyć indeks w polu PK, ale nie indeks nieklastrowy w polu innym niż PK.

Indeks NCL nie jest istotny dla struktury tabeli i nie jest ograniczeniem dla danych wewnątrz tabeli. Jest to oddzielna jednostka, która obsługuje tabelę, ale nie jest integralna z jej funkcjonalnością ani projektem.

Dlatego jest to osobne stwierdzenie. Indeks NCL nie ma znaczenia dla tabeli z punktu widzenia projektu (niezależnie od optymalizacji zapytań).

JNK
źródło
7

Zaakceptowana odpowiedź, jak utworzyć indeks w skrypcie tworzenia tabeli, nie działa dla mnie. To zrobiło:

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

Pamiętaj, klucze obce nie tworzą indeksów, więc warto je indeksować, ponieważ najprawdopodobniej będziesz do nich dołączać.

ScubaSteve
źródło
Nie podążam za tym ostatnim stwierdzeniem. Zgodziłbym się z tym stwierdzeniem, jeśli zwykłą praktyką jest wysyłanie zapytań do tabeli za pomocą klucza obcego; ale nie tylko dlatego, że się do niego przyłączasz, dlatego powinien być indeksowany. Przykład: Znajdź wszystkich pracowników i nazwę ich firmy o identyfikatorze firmy X - wtedy upewnij się, że indeks na FK pomoże. Znajdź wszystkich pracowników i ich nazwę firmy z nazwiskiem zaczynającym się na literę A; indeks na FK nie pomaga. Innymi słowy, nie jestem pewien, czy „ponieważ przyłączasz się do tego, powinieneś go indeksować” jest dobrą praktyką. Czy coś mi brakuje?
Paul
2
Indeksy przyspieszają zapytania łączące.
ScubaSteve