Tworzenie indeksu na zmiennej tabeli

190

Czy można utworzyć indeks dla zmiennej tabeli w SQL Server 2000?

to znaczy

DECLARE @TEMPTABLE TABLE (
     [ID] [int] NOT NULL PRIMARY KEY
    ,[Name] [nvarchar] (255) COLLATE DATABASE_DEFAULT NULL 
)

Czy mogę utworzyć indeks na Name?

GordyII
źródło
3
Tworzenie obu rodzajów tabel tymczasowych wiąże się z pewnym kosztem; a jeśli masz tak dużo danych, że potrzebujesz indeksu, być może nadszedł czas, aby spojrzeć na użycie prawdziwej tabeli; że skonfigurowałeś się jako bezpieczny dla transakcji; filtruj według identyfikatora spid lub użytkownika, a następnie wyczyść go na końcu. Rzeczywiste tabele v tabele temp mają swoje wzloty i upadki, ale jeśli wydajność stanowi problem; spróbuj też z prawdziwym stołem.
u07ch
Stół tymczasowy „IS” to prawdziwy stół, po prostu znika, kiedy skończysz. Prawdziwa różnica (inna niż zniknie automatycznie) polega na tym, że jest w TempDB. To jest naprawdę ogromne, jeśli chodzi o indeksy i ograniczenia, ponieważ możesz skończyć z konfliktami nazw, nie tylko z innymi wykonaniami twojego kodu, ale z kodem wykonywanym w innych bazach danych w twojej instancji.
bielawski
@bielawski to zmienna tabelowa, a nie tabela tymczasowa. Zmienne tabeli nie zezwalają na jawnie nazwane ograniczenia, nazwy generowane przez system są gwarancją unikalności. Pozwalają na indeksy nazwane z 2014 r., Ale nie stanowi to problemu, ponieważ indeksy muszą być jednoznacznie nazwane w obrębie obiektu, a nie między obiektami.
Martin Smith
Mój punkt był 2-krotny. 1) Poza użyciem zmiennej w celu uniknięcia splątania transakcji nie ma istotnej różnicy między tabelą tymczasową a zmienną tabelową. Jednak w V-2000 nie ma składni do dodawania ograniczeń, indeksów ... do zmiennej. 2) Biorąc pod uwagę, że zamiast tego można użyć tabeli tymczasowej, nazwane dodatki tabeli takie jak indeksy BĘDĄ kolidować z jednoczesnym wykonywaniem kopii tego samego SP, jeśli zostanie użyta nazwa statyczna! Poniższy mechanizm został opracowany wyraźnie, ponieważ prześledziłem awarie SP do zderzenia nazwanych indeksów w tych właśnie okolicznościach. MUSZĄ być wyjątkowe.
bielawski
1
@bielawski - Żadne nazwy indeksów nie muszą być unikalne między obiektami - tylko nazwy ograniczeń. testowanie jest trywialne. Po prostu wykonajCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
Martin Smith,

Odpowiedzi:

363

Pytanie jest oznaczone jako SQL Server 2000, ale najpierw rozwiążę tę kwestię z korzyścią dla osób rozwijających najnowszą wersję.

SQL Server 2014

Oprócz metod dodawania indeksów opartych na ograniczeniach omówionych poniżej SQL Server 2014 umożliwia także bezpośrednie określanie indeksów nie unikatowych za pomocą wbudowanej składni deklaracji zmiennych tabeli.

Przykładowa składnia tego jest poniżej.

/*SQL Server 2014+ compatible inline index syntax*/
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
       INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);

Filtrowanych indeksów i indeksów z dołączonymi kolumnami nie można obecnie zadeklarować za pomocą tej składni, jednak SQL Server 2016 nieco to rozluźnia. Od CTP 3.1 można teraz deklarować przefiltrowane indeksy dla zmiennych tabeli. Według RTM może się zdarzyć, że uwzględnione kolumny są również dozwolone, ale obecna pozycja jest taka, że „prawdopodobnie nie dostaną się do SQL16 z powodu ograniczeń zasobów”

/*SQL Server 2016 allows filtered indexes*/
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)

SQL Server 2000 - 2012

Czy mogę utworzyć indeks w nazwie?

Krótka odpowiedź: tak.

DECLARE @TEMPTABLE TABLE (
  [ID]   [INT] NOT NULL PRIMARY KEY,
  [Name] [NVARCHAR] (255) COLLATE DATABASE_DEFAULT NULL,
  UNIQUE NONCLUSTERED ([Name], [ID]) 
  ) 

Bardziej szczegółowa odpowiedź znajduje się poniżej.

Tradycyjne tabele w SQL Server mogą mieć indeks klastrowany lub mają strukturę stosów .

Indeksy klastrowe można zadeklarować jako unikalne, aby uniemożliwić powielanie wartości kluczy, lub domyślnie jako niepowtarzalne. Jeśli nie jest unikalny, to SQL Server cicho dodaje unikatowy do każdego zduplikowanego klucza, aby były unikalne.

Indeksy nieklastrowane można również jawnie zadeklarować jako unikalne. W przeciwnym razie w przypadku nie unikatowego przypadku SQL Server dodaje lokalizator wierszy (klastrowany klucz indeksu lub RID dla sterty) do wszystkich kluczy indeksu (nie tylko duplikatów), co ponownie zapewnia ich unikalność.

W SQL Server 2000 - 2012 indeksy zmiennych tabeli można tworzyć niejawnie tylko przez utworzenie ograniczenia UNIQUElub PRIMARY KEY. Różnica między tymi typami ograniczeń polega na tym, że klucz podstawowy musi znajdować się w kolumnach, które nie mają wartości NULL. Kolumny uczestniczące w unikalnym ograniczeniu mogą mieć wartość null. (chociaż implementacja unikalnych ograniczeń przez SQL Server w obecności NULLs nie jest zgodna ze specyfikacją SQL Standard). Również tabela może mieć tylko jeden klucz podstawowy, ale wiele unikalnych ograniczeń.

Oba te ograniczenia logiczne są fizycznie realizowane za pomocą unikalnego indeksu. Jeśli nie zostanie to wyraźnie określone inaczej, PRIMARY KEYstanie się indeksem klastrowanym i unikatowymi ograniczeniami nieklastrowanymi, ale to zachowanie można zastąpić poprzez określenie CLUSTEREDlub NONCLUSTEREDjawnie za pomocą deklaracji ograniczeń (przykładowa składnia)

DECLARE @T TABLE
(
A INT NULL UNIQUE CLUSTERED,
B INT NOT NULL PRIMARY KEY NONCLUSTERED
)

W wyniku powyższego można domyślnie utworzyć następujące indeksy na zmiennych tabeli w SQL Server 2000 - 2012.

+-------------------------------------+-------------------------------------+
|             Index Type              | Can be created on a table variable? |
+-------------------------------------+-------------------------------------+
| Unique Clustered Index              | Yes                                 |
| Nonunique Clustered Index           |                                     |
| Unique NCI on a heap                | Yes                                 |
| Non Unique NCI on a heap            |                                     |
| Unique NCI on a clustered index     | Yes                                 |
| Non Unique NCI on a clustered index | Yes                                 |
+-------------------------------------+-------------------------------------+

Ten ostatni wymaga trochę wyjaśnienia. W definicji zmiennej tabeli na początku tej odpowiedzi symulowany jest nie unikalny indeks nieklastrowany Nameprzez unikalny indeks włączony Name,Id(pamiętaj, że SQL Server i tak po cichu dodałby klastrowany klucz indeksu do nie unikatowego klucza NCI).

IDENTITYNiejednoznaczny indeks klastrowy można również uzyskać ręcznie dodając kolumnę, która będzie działać jako unikatator.

DECLARE @T TABLE
(
A INT NULL,
B INT NULL,
C INT NULL,
Uniqueifier INT NOT NULL IDENTITY(1,1),
UNIQUE CLUSTERED (A,Uniqueifier)
)

Ale nie jest to dokładna symulacja tego, jak normalnie klaster klastrowy normalnie zostałby faktycznie zaimplementowany w SQL Server, ponieważ dodaje to „Uniqueifier” do wszystkich wierszy. Nie tylko ci, którzy tego wymagają.

Martin Smith
źródło
1
Uwaga: Rozwiązanie 2000-2012 działa tylko wtedy, gdy kolumna tekstowa <= 900 bajtów. to znaczy. varchar (900), nvarchar (450)
Andre Figueiredo
1
@AndreFigueiredo tak, to maksymalny rozmiar klucza indeksu na stałych tabelach również w tych wersjach.
Martin Smith
1
Chciałem tylko zauważyć, że odpowiedź SQL 2014 działa dobrze na platformie Azure. Dzięki Martin!
Jaxidian
13

Należy rozumieć, że z punktu widzenia wydajności nie ma różnic między tabelami @temp a tabelami #temp, które faworyzują zmienne. Znajdują się w tym samym miejscu (tempdb) i są wdrażane w ten sam sposób. Wszystkie różnice pojawiają się w dodatkowych funkcjach. Zobacz ten niezwykle kompletny opis: /dba/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

Chociaż istnieją przypadki, w których nie można użyć tabeli tymczasowej, takie jak funkcje tabelowe lub skalarne, w większości innych przypadków przed wersją v2016 (gdzie nawet zmienne indeksy można dodać do zmiennej tabeli), można po prostu użyć tabeli #temp.

Wadą używania indeksów nazwanych (lub ograniczeń) w tempdb jest to, że nazwy mogą się następnie kolidować. Nie tylko teoretycznie z innymi procedurami, ale często dość łatwo z innymi instancjami samej procedury, które próbowałyby umieścić ten sam indeks na swojej kopii tabeli #temp.

Aby uniknąć konfliktów nazw, zwykle działa coś takiego:

declare @cmd varchar(500)='CREATE NONCLUSTERED INDEX [ix_temp'+cast(newid() as varchar(40))+'] ON #temp (NonUniqueIndexNeeded);';
exec (@cmd);

Zapewnia to, że nazwa jest zawsze unikalna, nawet między jednoczesnymi wykonywaniem tej samej procedury.

bielawski
źródło
Ma jeden brakujący nawias po varchar (40), dodaj, że
Tejasvi Hegde
1
Nie ma problemu z nazwanymi indeksami - indeksy muszą być jednoznacznie nazwane w obrębie tabeli. Problem polega na nazwanych ograniczeniach i najlepszym rozwiązaniem jest na ogół nie nazywanie ich w tabelach tymczasowych - nazwane ograniczenia uniemożliwiają buforowanie obiektów tabeli tymczasowej.
Martin Smith
1
To musi być prawdą tylko w przypadku niektórych wersji (jeśli dotyczy to dowolnej wersji). Musiałem wymyślić to obejście, szczególnie dlatego, że prześledziłem awarie sp do zderzenia nazwanych indeksów podczas jednoczesnego wykonywania.
bielawski
@bielawski Używasz 2016? Jestem bardzo ciekawy, czy indeksy nazwane w tabelach tymczasowych stanowią ryzyko dla współbieżnych środowisk.
Elaskanator
1
@Elaskanator tak, są. Stwierdziliśmy niezgodność w tabelach metadanych SQL, gdy są one wczytywane, usunięcie nazwy indeksu rozwiązało problem. To był SQL 2016.
Dan Def
0

Jeśli zmienna tabeli ma duże dane, zamiast zmiennej tabeli (@table) utwórz tabelę temp (#table). Zmienna table nie pozwala na utworzenie indeksu po wstawieniu.

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. Utwórz tabelę z unikalnym indeksem klastrowym

  2. Wstaw dane do tabeli Temp „#Table”

  3. Utwórz indeksy klastrowe.

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);
Boopathi.Indotnet
źródło
utwórz indeks po instrukcji insert, aby uniknąć niepotrzebnego sortowania
Boopathi.Indotnet
Zauważ, że nie jest to możliwe, jeśli zmienna tabeli jest w funkcji
Geoff,