Criando um índice em uma variável de tabela

189

Você pode criar um índice em uma variável de tabela no SQL Server 2000?

ie

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

Posso criar um índice Name?

GordyII
fonte
3
Existe um custo na criação dos dois tipos de tabelas temporárias; e se você tiver tantos dados e precisar de um índice, talvez seja hora de usar uma tabela real; que você configurou para ser seguro para transações; filtre por spid ou ID do usuário e limpe-a no final. Tabelas reais x tabelas temporárias têm seus altos e baixos, mas se o desempenho é um problema; experimente com uma mesa real também.
U07ch
Uma tabela temporária 'IS' é uma tabela real, ela desaparece quando você termina. A diferença real (além de desaparecer automaticamente) é que está no TempDB. Isso é realmente enorme quando se trata de índices e restrições, porque você pode acabar com conflitos de nome, não apenas com outras execuções do seu código, mas com o código em outros bancos de dados da sua instância.
Bielawski # 12/16
@bielawski esta é uma variável de tabela e não uma tabela temporária. As variáveis ​​da tabela não permitem restrições explicitamente nomeadas; os nomes gerados pelo sistema têm a garantia de serem exclusivos. Eles permitem índices nomeados a partir de 2014, mas isso não é um problema, pois os índices precisam apenas ser nomeados exclusivamente dentro de um objeto e não entre objetos.
Martin Smith
Meu ponto foi 2 vezes. 1) Além de usar uma variável para evitar o emaranhamento da transação, não há diferença material entre uma tabela temporária e uma variável da tabela. No V-2000, no entanto, não há sintaxe para adicionar restrições, índices ... a uma variável. 2) Dado pode-se usar uma tabela temporária em vez disso, apêndices tabela denominada como índices VAI colidir com executam simultaneamente cópias do mesmo SP se um nome estático é usado! O mecanismo abaixo foi desenvolvido explicitamente porque identifiquei falhas de SP em índices nomeados que se chocavam nessas circunstâncias exatas. Eles devem ser únicos.
Bielawski
1
@bielawski - Nenhum nome de índice não precisa ser exclusivo entre objetos - apenas nomes de restrições. isso é trivial para testar. Apenas executeCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
Martin Smith

Respostas:

362

A pergunta está marcada como SQL Server 2000, mas, para o benefício das pessoas que desenvolvem a versão mais recente, abordarei isso primeiro.

SQL Server 2014

Além dos métodos de adição de índices baseados em restrições discutidos abaixo, o SQL Server 2014 também permite que índices não exclusivos sejam especificados diretamente com sintaxe embutida nas declarações de variáveis ​​da tabela.

Sintaxe de exemplo para isso está abaixo.

/*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*/
);

No momento, os índices filtrados e os índices com colunas incluídas não podem ser declarados com esta sintaxe, mas o SQL Server 2016 relaxa um pouco mais. A partir do CTP 3.1, agora é possível declarar índices filtrados para variáveis ​​de tabela. Por RTM, pode ser que as colunas incluídas também sejam permitidas, mas a posição atual é que elas "provavelmente não entrarão no SQL16 devido a restrições de recursos"

/*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

Posso criar um índice em Nome?

Resposta curta: Sim.

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

Uma resposta mais detalhada está abaixo.

As tabelas tradicionais no SQL Server podem ter um índice em cluster ou estão estruturadas como heaps .

Os índices clusterizados podem ser declarados como exclusivos para desaprovar valores de chave duplicados ou padrão para não exclusivos. Se não for exclusivo, o SQL Server adiciona silenciosamente um exclusivo a qualquer chave duplicada para torná-las exclusivas.

Os índices não agrupados também podem ser declarados explicitamente como exclusivos. Caso contrário, no caso não exclusivo, o SQL Server adiciona o localizador de linhas (chave de índice em cluster ou RID para um heap) a todas as chaves de índice (não apenas duplicatas), novamente isso garante que elas sejam exclusivas.

Em SQL Server 2000 - 2012 índices em variáveis de tabela só pode ser criado implicitamente criando um UNIQUEou PRIMARY KEYrestrição. A diferença entre esses tipos de restrições é que a chave primária deve estar em colunas não anuláveis. As colunas que participam de uma restrição exclusiva podem ser anuláveis. (embora a implementação do SQL Server de restrições exclusivas na presença deNULL s não seja a especificada no SQL Standard). Além disso, uma tabela pode ter apenas uma chave primária, mas várias restrições exclusivas.

Essas duas restrições lógicas são fisicamente implementadas com um índice exclusivo. Se não especificado explicitamente, PRIMARY KEYele se tornará o índice clusterizado e as restrições exclusivas não clusterizadas, mas esse comportamento poderá ser substituído, especificando CLUSTEREDou NONCLUSTEREDexplicitamente com a declaração de restrição (sintaxe de exemplo)

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

Como resultado do acima, os seguintes índices podem ser criados implicitamente em variáveis ​​de tabela no 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                                 |
+-------------------------------------+-------------------------------------+

O último requer um pouco de explicação. Na definição da variável de tabela no início desta resposta, o índice não exclusivo não clusterizado Nameé simulado por um índice exclusivoName,Id (lembre-se de que o SQL Server adicionaria silenciosamente a chave de índice clusterizado à chave NCI não exclusiva).

Um índice clusterizado não exclusivo também pode ser alcançado adicionando manualmente uma IDENTITYcoluna para atuar como um identificador exclusivo.

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

Mas essa não é uma simulação precisa de como um índice clusterizado não exclusivo seria realmente implementado no SQL Server, pois isso adiciona o "Uniqueifier" a todas as linhas. Não apenas aqueles que precisam.

Martin Smith
fonte
1
Nota: a solução 2000-2012 funcionará apenas se a coluna de texto <= 900 bytes. ie varchar (900), nvarchar (450)
Andre Figueiredo
1
@AndreFigueiredo sim, esse é o tamanho máximo para uma chave de índice em tabelas permanentes também nessas versões.
Martin Smith
1
Eu só queria observar que a resposta do SQL 2014 funciona bem no Azure. Obrigado Martin!
Jaxidian
13

Deve-se entender que, do ponto de vista do desempenho, não há diferenças entre as tabelas @temp e #temp que favorecem variáveis. Eles residem no mesmo local (tempdb) e são implementados da mesma maneira. Todas as diferenças aparecem em recursos adicionais. Veja este artigo incrivelmente completo: /dba/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

Embora existam casos em que uma tabela temporária não possa ser usada, como nas funções de tabela ou escalar, na maioria dos outros casos anteriores à v2016 (onde até índices filtrados podem ser adicionados a uma variável de tabela), você pode simplesmente usar uma tabela #temp.

A desvantagem de usar índices nomeados (ou restrições) no tempdb é que os nomes podem entrar em conflito. Não apenas teoricamente com outros procedimentos, mas com bastante facilidade com outras instâncias do próprio procedimento, que tentariam colocar o mesmo índice em sua cópia da tabela #temp.

Para evitar conflitos de nome, algo como isso geralmente funciona:

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

Isso garante que o nome seja sempre único, mesmo entre execuções simultâneas do mesmo procedimento.

bielawski
fonte
Ele tem um colchete ausente após o varchar (40), e acrescente que:
Tejasvi Hegde 21/06
1
Não há problema com índices nomeados - os índices precisam ser nomeados exclusivamente em uma tabela. O problema está nas restrições nomeadas e a melhor solução geralmente não é nomeá-las nas tabelas temporárias - as restrições nomeadas impedem o cache do objeto da tabela temporária.
Martin Smith
1
Isso deve ser verdade apenas para determinadas versões (se for verdade para qualquer versão). Eu tive que criar essa solução alternativa especificamente porque rastreei falhas sp para o choque de índices nomeados durante execuções simultâneas.
Bielawski # 25/17
@bielawski Você está usando 2016? Estou muito curioso para saber se índices nomeados em tabelas temporárias são um risco para ambientes simultâneos.
Elaskanator
1
@ Elaskanator sim, eles são. Encontramos contenção nas tabelas de metadados SQL quando sob carga, removendo o nome do índice resolvido o problema. Este foi o SQL 2016.
Dan Def
0

Se a variável de tabela tiver dados grandes, em vez da variável de tabela (@table) crie a tabela temporária (#table). A variável table não permite criar índice após a inserção.

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. Criar tabela com índice clusterizado exclusivo

  2. Inserir dados na tabela Temp "#Table"

  3. Crie índices não clusterizados.

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);
Boopathi.Indotnet
fonte
criar índice após instrução de inserção para evitar desnecessários de triagem
Boopathi.Indotnet
Observe que isso não é possível se a variável da tabela estiver em uma função
Geoff