Crear un índice en una variable de tabla


191

¿Se puede crear un índice en una variable de tabla en SQL Server 2000?

es decir

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

¿Puedo crear un índice en Name?


3
Hay un costo en crear ambos tipos de tablas temporales; y si tiene tantos datos que necesita un índice, tal vez sea hora de ver el uso de una tabla real; que configuró para transacciones seguras; filtre por spid o ID de usuario y luego bórrelo al final. Las tablas reales v las tablas temporales tienen sus altibajos, pero si el rendimiento es un problema; Pruébalo con una mesa real también.
u07ch

Una tabla temporal "ES" una tabla real, simplemente desaparece cuando haya terminado. La verdadera diferencia (aparte de que desaparecerá automáticamente) es que está en TempDB. En realidad, esto es enorme cuando se trata de índices y restricciones porque podría terminar con conflictos de nombres, no solo con otras ejecuciones de su código sino también con la ejecución de código en otras bases de datos en su instancia.
bielawski

@bielawski esta es una variable de tabla, no una tabla temporal. Las variables de tabla no permiten restricciones explícitamente nombradas, se garantiza que los nombres generados por el sistema serán únicos. Permiten índices con nombre a partir de 2014, pero eso no es un problema, ya que los índices solo necesitan un nombre exclusivo dentro de un objeto, no a través de los objetos.
Martin Smith

Mi punto fue 2 veces. 1) Aparte de usar una variable para evitar el enredo de transacciones, no hay diferencia material entre una tabla temporal y una variable de tabla. Sin embargo, en V-2000 no hay sintaxis para agregar restricciones, índices ... a una variable. 2) Teniendo en cuenta que uno puede utilizar una tabla temporal en cambio, apéndices tabla denominada como índices SE chocar con ejecución simultánea copias del mismo SP si se utiliza un nombre estático! El mecanismo a continuación se desarrolló explícitamente porque rastreé las fallas de SP en los índices nombrados que chocan durante estas circunstancias exactas. DEBEN ser únicos.
bielawski

1
@bielawski: no es necesario que los nombres de índice sean únicos entre los objetos, solo los nombres de restricción lo hacen. Esto es trivial para probar. Solo ejecuteCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
Martin Smith el

Respuestas:


363

La pregunta está etiquetada como SQL Server 2000, pero para beneficio de las personas que desarrollan en la última versión, abordaré eso primero.

SQL Server 2014

Además de los métodos para agregar índices basados ​​en restricciones que se analizan a continuación, SQL Server 2014 también permite especificar índices no únicos directamente con sintaxis en línea en las declaraciones de variables de tabla.

Ejemplo de sintaxis para eso está abajo.

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

Los índices filtrados y los índices con columnas incluidas no se pueden declarar actualmente con esta sintaxis, sin embargo, SQL Server 2016 lo relaja un poco más. Desde CTP 3.1 ahora es posible declarar índices filtrados para variables de tabla. Según RTM, es posible que las columnas incluidas también estén permitidas, pero la posición actual es que "probablemente no llegarán a SQL16 debido a limitaciones 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

¿Puedo crear un índice en Nombre?

Respuesta corta: sí.

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

Una respuesta más detallada está abajo.

Las tablas tradicionales en SQL Server pueden tener un índice agrupado o están estructuradas como montones .

Los índices agrupados pueden declararse como únicos para no permitir valores de clave duplicados o predeterminados como no únicos. Si no es único, SQL Server agrega silenciosamente un unificador a las claves duplicadas para que sean únicas.

Los índices no agrupados también se pueden declarar explícitamente como únicos. De lo contrario, para el caso no único, SQL Server agrega el localizador de filas (clave de índice agrupada o RID para un montón) a todas las claves de índice (no solo duplicados), esto nuevamente asegura que sean únicos.

En SQL Server 2000 - 2012, los índices de las variables de tabla solo se pueden crear implícitamente creando una restricción UNIQUEo PRIMARY KEY. La diferencia entre estos tipos de restricciones es que la clave primaria debe estar en columnas no anulables. Las columnas que participan en una restricción única pueden ser anulables. (aunque la implementación de restricciones únicas de SQL Server en presencia de NULLs no es la especificada en el Estándar SQL). Además, una tabla solo puede tener una clave primaria pero múltiples restricciones únicas.

Ambas restricciones lógicas se implementan físicamente con un índice único. Si no se especifica explícitamente de lo contrario, PRIMARY KEYse convertirá en el índice agrupado y las restricciones únicas no agrupadas, pero este comportamiento puede anularse especificando CLUSTEREDo NONCLUSTEREDexplícitamente con la declaración de restricción (sintaxis de ejemplo)

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

Como resultado de lo anterior, los siguientes índices se pueden crear implícitamente en variables de tabla en 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                                 |
+-------------------------------------+-------------------------------------+

El último requiere un poco de explicación. En la definición de la variable de tabla al principio de esta respuesta, el índice no agrupado no único activado Namese simula mediante un índice único activado Name,Id(recuerde que SQL Server agregaría silenciosamente la clave de índice agrupado a la clave NCI no exclusiva de todos modos).

También se puede lograr un índice agrupado no único agregando manualmente una IDENTITYcolumna para que actúe como unificador.

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

Pero esta no es una simulación precisa de cómo un índice agrupado no único normalmente se implementaría en SQL Server, ya que esto agrega el "Uniqueifier" a todas las filas. No solo aquellos que lo requieren.


1
Nota: la solución 2000-2012 solo funciona si la columna de texto <= 900 bytes. es decir. varchar (900), nvarchar (450)
Andre Figueiredo

1
@AndreFigueiredo sí, ese es el tamaño máximo para una clave de índice en tablas permanentes también en esas versiones.
Martin Smith

1
Solo quería señalar que la respuesta de SQL 2014 funciona bien en Azure. Gracias martin
Jaxidian

13

Debe entenderse que desde el punto de vista del rendimiento no hay diferencias entre las tablas @temp y las tablas #temp que favorecen las variables. Residen en el mismo lugar (tempdb) y se implementan de la misma manera. Todas las diferencias aparecen en características adicionales. Vea este artículo increíblemente completo: /dba/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

Aunque hay casos en los que no se puede usar una tabla temporal, como en una tabla o funciones escalares, para la mayoría de los casos anteriores a v2016 (donde incluso se pueden agregar índices filtrados a una variable de tabla), simplemente puede usar una tabla #temp.

El inconveniente de usar índices con nombre (o restricciones) en tempdb es que los nombres pueden entrar en conflicto. No solo teóricamente con otros procedimientos, sino a menudo con bastante facilidad con otras instancias del procedimiento en sí mismo que tratarían de poner el mismo índice en su copia de la tabla #temp.

Para evitar conflictos de nombres, algo como esto generalmente funciona:

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

Esto asegura que el nombre siempre sea único, incluso entre ejecuciones simultáneas del mismo procedimiento.


Le falta un soporte después de varchar (40), agregue eso
Tejasvi Hegde

1
No hay ningún problema con los índices con nombre: los índices solo deben tener un nombre exclusivo dentro de una tabla. El problema es con restricciones con nombre y la mejor solución generalmente no es nombrarlas en tablas temporales: las restricciones con nombre evitan el almacenamiento en caché de objetos de la tabla temporal.
Martin Smith

1
Eso debe ser cierto solo para ciertas versiones (si es cierto para cualquier versión). Tuve que encontrar esta solución específicamente porque rastreé las fallas de sp hasta el choque de los índices nombrados durante las ejecuciones simultáneas.
bielawski

@bielawski ¿Estás usando 2016? Tengo mucha curiosidad acerca de si los índices nombrados en las tablas temporales son un riesgo para entornos concurrentes.
Elaskanator

1
@Elaskanator sí, lo son. Encontramos contención en las tablas de metadatos SQL cuando estaba bajo carga, al eliminar el nombre del índice se resolvió el problema. Esto fue SQL 2016.
Dan Def

0

Si la variable de tabla tiene datos grandes, en lugar de la variable de tabla (@table) cree la tabla temporal (#table). La variable de tabla no permite crear índice después de insertar.

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. Crear tabla con índice agrupado único

  2. Insertar datos en la tabla Temp "#Table"

  3. Crear índices no agrupados.

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);

crear índice después de insertar una declaración para evitar una clasificación innecesaria
Boopathi

Tenga en cuenta que esto no es posible si la variable de tabla está en una función
Geoff
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.