¿Cuál es la diferencia entre una tabla temporal y una variable de tabla en SQL Server?

390

En SQL Server 2005, podemos crear tablas temporales de dos maneras:

declare @tmp table (Col1 int, Col2 int);

o

create table #tmp (Col1 int, Col2 int);

¿Cuáles son las diferencias entre estos dos? He leído opiniones contradictorias sobre si @tmp todavía usa tempdb o si todo sucede en la memoria.

¿En qué escenarios uno supera al otro?

Eric Z Beard
fuente
2
Hay un muy buen artículo escrito por Pinal Dave aquí ... blog.sqlauthority.com/2009/12/15/…
sam yi

Respuestas:

392

Existen algunas diferencias entre las Tablas temporales (#tmp) y las Variables de tabla (@tmp), aunque usar tempdb no es una de ellas, como se detalla en el siguiente enlace de MSDN.

Como regla general, para volúmenes de datos pequeños a medianos y escenarios de uso simples, debe usar variables de tabla. (Esta es una directriz demasiado amplia con, por supuesto, muchas excepciones; consulte los artículos siguientes y siguientes).

Algunos puntos a considerar al elegir entre ellos:

  • Las tablas temporales son tablas reales, por lo que puede hacer cosas como CREAR ÍNDICES, etc. Si tiene grandes cantidades de datos para los cuales el acceso por índice será más rápido, las tablas temporales son una buena opción.

  • Las variables de tabla pueden tener índices utilizando PRIMARY KEY o restricciones ÚNICAS. (Si desea un índice no único, simplemente incluya la columna de clave principal como la última columna en la restricción única. Si no tiene una columna única, puede usar una columna de identidad). SQL 2014 también tiene índices no únicos .

  • Las variables de tabla no participan en las transacciones y SELECTs están implícitamente con NOLOCK. El comportamiento de la transacción puede ser muy útil, por ejemplo, si desea ROLLBACK a mitad de camino a través de un procedimiento, las variables de la tabla pobladas durante esa transacción aún se completarán.

  • Las tablas temporales pueden provocar que los procedimientos almacenados se vuelvan a compilar, quizás a menudo. Las variables de la tabla no lo harán.

  • Puede crear una tabla temporal usando SELECT INTO, que puede ser más rápido de escribir (bueno para consultas ad-hoc) y puede permitirle lidiar con los tipos de datos cambiantes con el tiempo, ya que no necesita definir la estructura de su tabla temporal por adelantado.

  • Puede pasar las variables de tabla de las funciones, lo que le permite encapsular y reutilizar la lógica mucho más fácilmente (por ejemplo, hacer que una función divida una cadena en una tabla de valores en algún delimitador arbitrario).

  • El uso de variables de tabla dentro de las funciones definidas por el usuario permite que esas funciones se usen más ampliamente (consulte la documentación de CREAR FUNCIÓN para obtener más detalles). Si está escribiendo una función, debe usar variables de tabla sobre tablas temporales a menos que exista una necesidad imperiosa de lo contrario.

  • Ambas variables de tabla y tablas temporales se almacenan en tempdb. Sin embargo, las variables de la tabla (desde 2005) utilizan de manera predeterminada la clasificación de la base de datos actual frente a las tablas temporales que toman la clasificación predeterminada de tempdb ( ref ). Esto significa que debe tener en cuenta los problemas de intercalación si usa tablas temporales y su intercalación db es diferente a la de tempdb, causando problemas si desea comparar los datos en la tabla temporal con los datos de su base de datos.

  • Las tablas temporales globales (## tmp) son otro tipo de tabla temporal disponible para todas las sesiones y usuarios.

Algunas lecturas adicionales:

Rory
fuente
26
Las variables de tabla pueden tener índices. Simplemente cree una restricción única y obtendrá automáticamente un índice. Hace una gran diferencia de rendimiento. (Si no desea un índice único, simplemente agregue la clave primaria real al final de los campos que desea. Si no tiene uno, cree una columna de identidad).
Ben
77
@Ben y SQL Server 2014 permite que se especifiquen
Martin Smith
44
Las variables de tabla que no se ven afectadas por las transacciones a veces son útiles. Si tiene algo que desea conservar después de una reversión, puede colocarlo en una variable de tabla.
Quillbreaker
3
Las estadísticas se crean para tablas temporales, que pueden mejorar los planes de consulta, pero no para las variables de tabla. Estas estadísticas se almacenan en caché durante un tiempo, junto con las páginas de la tabla temporal, después de que la tabla temporal se descarta y pueden ser inexactas si la tabla almacenada en caché se reactiva.
Michael Green
Las variables de tabla utilizarán de forma predeterminada la clasificación del tipo de datos definido por el usuario (si la columna es de un tipo de datos definido por el usuario) o la clasificación de la base de datos actual y no la clasificación predeterminada de tempdb. Las tablas temporales usarán la intercalación predeterminada tempdb. Ver: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad
25

Simplemente mirando el reclamo en la respuesta aceptada de que las variables de la tabla no participan en el registro.

En general, parece falso que haya alguna diferencia en la cantidad de registros (al menos para insert/ update/ deleteoperaciones en la tabla en sí, aunque desde entonces he encontrado que hay una pequeña diferencia a este respecto para los objetos temporales en caché en los procedimientos almacenados debido a la tabla del sistema adicional actualizaciones).

Observé el comportamiento de registro en contra de @table_variableuna #temptabla para las siguientes operaciones.

  1. Inserto exitoso
  2. Insertar varias filas donde la declaración se revierte debido a una violación de restricción.
  3. Actualizar
  4. Eliminar
  5. Desasignar

Los registros de transacciones fueron casi idénticos para todas las operaciones.

La versión de la variable de tabla en realidad tiene algunas entradas de registro adicionales porque obtiene una entrada agregada (y luego eliminada de) la sys.syssingleobjrefstabla base, pero en general tenía unos pocos bytes menos registrados simplemente ya que el nombre interno para las variables de tabla consume 236 bytes menos que para las #temptablas (118 menosnvarchar caracteres ).

Script completo para reproducir (mejor ejecución en una instancia iniciada en modo de usuario único y usando el sqlcmdmodo)

:setvar tablename "@T" 
:setvar tablescript "DECLARE @T TABLE"

/*
 --Uncomment this section to test a #temp table
:setvar tablename "#T" 
:setvar tablescript "CREATE TABLE #T"
*/

USE tempdb 
GO    
CHECKPOINT

DECLARE @LSN NVARCHAR(25)

SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null) 


EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT

$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)


BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT

INSERT INTO $(tablename)
DEFAULT VALUES

BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT


INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns

BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT


/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH

BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT

UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
    OffRowFiller  =LOWER(OffRowFiller),
    LOBFiller  =LOWER(LOBFiller)


BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT

DELETE FROM  $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT

BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')


DECLARE @LSN_HEX NVARCHAR(25) = 
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)        

SELECT 
    [Operation],
    [Context],
    [AllocUnitName],
    [Transaction Name],
    [Description]
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  

SELECT CASE
         WHEN GROUPING(Operation) = 1 THEN 'Total'
         ELSE Operation
       END AS Operation,
       Context,
       AllocUnitName,
       COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
       COUNT(*)                              AS Cnt
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())

Resultados

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @TV      |             #TV      |                  |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation             | Context            | AllocUnitName             | Size in Bytes | Cnt  | Size in Bytes | Cnt  | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT        | LCX_NULL           |                           | 52            | 1    | 52            | 1    |                  |
| LOP_BEGIN_XACT        | LCX_NULL           |                           | 6056          | 50   | 6056          | 50   |                  |
| LOP_COMMIT_XACT       | LCX_NULL           |                           | 2548          | 49   | 2548          | 49   |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 624           | 3    | 624           | 3    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 208           | 1    | 208           | 1    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrscols.clst        | 832           | 4    | 832           | 4    |                  |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           |                           | 120           | 3    | 120           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 720           | 9    | 720           | 9    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.clust   | 444           | 3    | 444           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.nc      | 276           | 3    | 276           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.clst       | 628           | 4    | 628           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.nc         | 484           | 4    | 484           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.clst      | 176           | 1    | 176           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.nc        | 144           | 1    | 144           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.clst        | 100           | 1    | 100           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.nc1         | 88            | 1    | 88            | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysobjvalues.clst     | 596           | 5    | 596           | 5    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrowsets.clust      | 132           | 1    | 132           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrscols.clst        | 528           | 4    | 528           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.clst       | 1040          | 6    | 1276          | 6    | 236              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc1        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc2        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc3        | 480           | 6    | 480           | 6    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.clst | 96            | 1    |               |      | -96              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.nc1  | 88            | 1    |               |      | -88              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | Unknown Alloc Unit        | 72092         | 19   | 72092         | 19   |                  |
| LOP_DELETE_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 16348         | 37   | 16348         | 37   |                  |
| LOP_FORMAT_PAGE       | LCX_HEAP           | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit        | 252           | 3    | 252           | 3    |                  |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 84            | 1    | 84            | 1    |                  |
| LOP_FORMAT_PAGE       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 4788          | 57   | 4788          | 57   |                  |
| LOP_HOBT_DDL          | LCX_NULL           |                           | 108           | 3    | 108           | 3    |                  |
| LOP_HOBT_DELTA        | LCX_NULL           |                           | 9600          | 150  | 9600          | 150  |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 456           | 3    | 456           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syscolpars.clst       | 644           | 4    | 644           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysidxstats.clst      | 180           | 1    | 180           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysiscols.clst        | 104           | 1    | 104           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysobjvalues.clst     | 616           | 5    | 616           | 5    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 136           | 1    | 136           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrscols.clst        | 544           | 4    | 544           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1064          | 6    | 1300          | 6    | 236              |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syssingleobjrefs.clst | 100           | 1    |               |      | -100             |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | Unknown Alloc Unit        | 135888        | 19   | 135888        | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysallocunits.nc      | 288           | 3    | 288           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syscolpars.nc         | 500           | 4    | 500           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysidxstats.nc        | 148           | 1    | 148           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysiscols.nc1         | 92            | 1    | 92            | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc1        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc2        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc3        | 504           | 6    | 504           | 6    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syssingleobjrefs.nc1  | 92            | 1    |               |      | -92              |
| LOP_INSERT_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 5112          | 71   | 5112          | 71   |                  |
| LOP_MARK_SAVEPOINT    | LCX_NULL           |                           | 508           | 8    | 508           | 8    |                  |
| LOP_MODIFY_COLUMNS    | LCX_CLUSTERED      | Unknown Alloc Unit        | 1560          | 10   | 1560          | 10   |                  |
| LOP_MODIFY_HEADER     | LCX_HEAP           | Unknown Alloc Unit        | 3780          | 45   | 3780          | 45   |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.syscolpars.clst       | 384           | 4    | 384           | 4    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysidxstats.clst      | 100           | 1    | 100           | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysrowsets.clust      | 92            | 1    | 92            | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1144          | 13   | 1144          | 13   |                  |
| LOP_MODIFY_ROW        | LCX_IAM            | Unknown Alloc Unit        | 4224          | 48   | 4224          | 48   |                  |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit        | 13632         | 169  | 13632         | 169  |                  |
| LOP_MODIFY_ROW        | LCX_TEXT_MIX       | Unknown Alloc Unit        | 108640        | 120  | 108640        | 120  |                  |
| LOP_ROOT_CHANGE       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 960           | 10   | 960           | 10   |                  |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit        | 1200          | 20   | 1200          | 20   |                  |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit        | 1080          | 18   | 1080          | 18   |                  |
| LOP_SET_BITS          | LCX_SGAM           | Unknown Alloc Unit        | 120           | 2    | 120           | 2    |                  |
| LOP_SHRINK_NOOP       | LCX_NULL           |                           |               |      | 32            | 1    | 32               |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total                 |                    |                           | 410144        | 1095 | 411232        | 1092 | 1088             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
Martin Smith
fuente
1
+1 Solo por curiosidad (y para ser un poco pedante). La pregunta es / era bastante antigua (agosto de 2008), por lo que se trataba de SQL 2005. Ahora estamos en 2011 (fin de) y el último SQL es 2008 R2 más la versión beta de Denali. ¿Qué versión has usado?
xanatos
2
@xanatos - 2008. En 2005, las variables de tabla estarían en desventaja, ya que INSERT ... SELECTno se registraron mínimamente y no se puede establecer SELECT INTO ... una variable de tabla.
Martin Smith
1
Gracias @MartinSmith, actualicé mi respuesta para eliminar el reclamo sobre el registro.
Rory
18

¿En qué escenarios uno supera al otro?

Para tablas más pequeñas (menos de 1000 filas) use una variable temporal, de lo contrario use una tabla temporal.

SQLMenace
fuente
17
¿Algún dato de apoyo? Esto no es muy útil solo como una afirmación por sí sola.
Michael Myers
8
Microsoft recomienda un límite de 100 filas: msdn.microsoft.com/en-us/library/ms175010.aspx (consulte la sección Prácticas recomendadas ).
Artemix
Vea mi respuesta a continuación para obtener una explicación.
Weihui Guo
17

@wcm: en realidad, para elegir la variable de tabla no es solo Ram: se puede almacenar parcialmente en el disco.

Una tabla temporal puede tener índices, mientras que una variable de tabla solo puede tener un índice primario. Si la velocidad es un problema, las variables de la tabla pueden ser más rápidas, pero obviamente si hay muchos registros o la necesidad de buscar en la tabla temporal de un índice agrupado, entonces una tabla temporal sería mejor.

Buen artículo de antecedentes

JamesSugrue
fuente
2
Buen artículo de antecedentes +1. Eliminaré mi respuesta ya que modificarla no dejaría mucho y ya hay tantas respuestas buenas
wcm
12
  1. Tabla temporal: una tabla temporal es fácil de crear y hacer una copia de seguridad de los datos.

    Variable de tabla: Pero la variable de tabla implica el esfuerzo cuando generalmente creamos las tablas normales.

  2. Tabla temporal: el resultado de la tabla temporal puede ser utilizado por varios usuarios.

    Variable de tabla: Pero la variable de tabla solo puede ser utilizada por el usuario actual. 

  3. Tabla temporal: la tabla temporal se almacenará en tempdb. Hará tráfico de red. Cuando tenemos datos grandes en la tabla temporal, tiene que funcionar en la base de datos. Existirá un problema de rendimiento.

    Variable de tabla: Pero una variable de tabla se almacenará en la memoria física para algunos de los datos, luego, cuando el tamaño aumente, se moverá a tempdb.

  4. Tabla temporal: la tabla temporal puede hacer todas las operaciones DDL. Permite crear los índices, soltar, alterar, etc.,

    Variable de tabla: mientras que la variable de tabla no permitirá realizar las operaciones DDL. Pero la variable de tabla nos permite crear solo el índice agrupado.

  5. Tabla temporal: la tabla temporal se puede usar para la sesión actual o global. Para que una sesión de múltiples usuarios pueda utilizar los resultados en la tabla.

    Variable de tabla: pero la variable de tabla se puede utilizar hasta ese programa. (Procedimiento almacenado)

  6. Tabla temporal: la variable temporal no puede usar las transacciones. Cuando hacemos las operaciones DML con la tabla temporal, entonces puede revertir o confirmar las transacciones.

    Variable de tabla: pero no podemos hacerlo para la variable de tabla.

  7. Tabla temporal: las funciones no pueden usar la variable temporal. Además, no podemos hacer la operación DML en las funciones.

    Variable de tabla: Pero la función nos permite usar la variable de tabla. Pero usando la variable de tabla podemos hacer eso.

  8. Tabla temporal: El procedimiento almacenado realizará la recompilación (no puede usar el mismo plan de ejecución) cuando usemos la variable temporal para cada llamada subsiguiente.

    Variable de tabla: mientras que la variable de tabla no será así.

Kumar Manish
fuente
8

Para todos los que creen en el mito de que las variables temporales están solo en la memoria

Primero, la variable de tabla NO es necesariamente residente en memoria. Bajo presión de memoria, las páginas que pertenecen a una variable de tabla se pueden enviar a tempdb.

Lea el artículo aquí: TempDB :: Tabla variable vs tabla temporal local

SQLMenace
fuente
3
¿Puedes editar tus respuestas en una sola respuesta que aborde los dos puntos?
Joshua Drake
7

La otra diferencia principal es que las variables de tabla no tienen estadísticas de columna, como lo hacen las tablas temporales. Esto significa que el optimizador de consultas no sabe cuántas filas hay en la variable de la tabla (adivina 1), lo que puede conducir a que se generen planes altamente no óptimos si la variable de la tabla realmente tiene un gran número de filas.

Monstruo Gila
fuente
2
La rowscolumna en sys.partitionsse mantiene para las variables de la tabla, por lo que realmente sabe cuántas filas hay en la tabla. Esto se puede ver usando OPTION (RECOMPILE). Pero la falta de estadísticas de columna significa que no puede estimar predicados de columna específicos.
Martin Smith
7

Cita tomada de; Solución de problemas interna y profesional de SQL Server 2012

Estadísticas La principal diferencia entre las tablas temporales y las variables de tabla es que las estadísticas no se crean en las variables de tabla. Esto tiene dos consecuencias principales, la primera de las cuales es que el Optimizador de consultas utiliza una estimación fija para el número de filas en una variable de tabla, independientemente de los datos que contiene. Además, agregar o eliminar datos no cambia la estimación.

Índices No puede crear índices en las variables de la tabla, aunque puede crear restricciones. Esto significa que al crear claves primarias o restricciones únicas, puede tener índices (ya que estos se crean para admitir restricciones) en las variables de la tabla. Incluso si tiene restricciones y, por lo tanto, índices que tendrán estadísticas, los índices no se utilizarán cuando se compile la consulta porque no existirán en el momento de la compilación ni causarán recompilaciones.

Modificaciones de esquema Las modificaciones de esquema son posibles en tablas temporales pero no en variables de tabla. Aunque las modificaciones de esquemas son posibles en las tablas temporales, evite usarlas porque causan recompilaciones de declaraciones que usan las tablas.

Tablas temporales versus variables de tabla

LAS VARIABLES DE LA MESA NO SE CREAN EN LA MEMORIA

Existe una idea errónea común de que las variables de tabla son estructuras en memoria y, como tales, funcionarán más rápido que las tablas temporales . Gracias a un DMV llamado sys. dm _ db _ session _ space _ use, que muestra el uso de tempdb por sesión, puede probar que ese no es el caso . Después de reiniciar SQL Server para borrar el DMV, ejecute el siguiente script para confirmar que su sesión _ id devuelve 0 para el usuario _ objetos _ alloc _ page _ count:

SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Ahora puede verificar cuánto espacio utiliza una tabla temporal ejecutando el siguiente script para crear una tabla temporal con una columna y llenarla con una fila:

CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Los resultados en mi servidor indican que a la tabla se le asignó una página en tempdb. Ahora ejecute el mismo script pero use una variable de tabla esta vez:

DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

¿Cuál usar?

Si usa o no tablas temporales o variables de tabla debe decidirse mediante pruebas exhaustivas, pero es mejor inclinarse hacia las tablas temporales como predeterminadas porque hay muchas menos cosas que pueden salir mal .

He visto a los clientes desarrollar código usando variables de tabla porque estaban lidiando con una pequeña cantidad de filas, y era más rápido que una tabla temporal, pero unos años más tarde había cientos de miles de filas en la variable de tabla y el rendimiento era terrible. , ¡así que intente planificar algo de capacidad cuando tome su decisión!

Teoman shipahi
fuente
De hecho, las estadísticas se crean en las variables de la tabla, consulte stackoverflow.com/questions/42824366/…
YuFeng Shen
4

Otra diferencia:

Solo se puede acceder a una tabla var desde las declaraciones dentro del procedimiento que la crea, no desde otros procedimientos llamados por ese procedimiento o SQL dinámico anidado (a través de exec o sp_executesql).

El alcance de una tabla temporal, por otro lado, incluye código en procedimientos llamados y SQL dinámico anidado.

Si la tabla creada por su procedimiento debe ser accesible desde otros procedimientos llamados o SQL dinámico, debe usar una tabla temporal. Esto puede ser muy útil en situaciones complejas.

BrianFinkel
fuente
2

Las diferencias entre Temporary Tables (##temp/#temp)y Table Variables (@table)son como:

  1. Table variable (@table)se crea en el memory. Mientras que a Temporary table (##temp/#temp)se crea en tempdb database. Sin embargo, si hay una presión de memoria, las páginas que pertenecen a una variable de tabla pueden ser empujadas a tempdb.

  2. Table variablesNo pueden participar en transactions, logging or locking. Esto hace @table faster then #temp. Entonces la variable de tabla es más rápida que la tabla temporal.

  3. Temporary tablepermite modificaciones de esquema a diferencia Table variables.

  4. Temporary tablesson visibles en la rutina creada y también en las rutinas secundarias. Mientras que las variables de tabla solo son visibles en la rutina creada.

  5. Temporary tablesestán permitidos CREATE INDEXes, mientras que Table variablesno están permitidos, CREATE INDEXsino que pueden tener índice mediante el uso Primary Key or Unique Constraint.

Litisqe Kumar
fuente
1

Considere también que a menudo puede reemplazar ambas con tablas derivadas que también pueden ser más rápidas. Sin embargo, al igual que con todos los ajustes de rendimiento, solo las pruebas reales contra sus datos reales pueden indicarle el mejor enfoque para su consulta en particular.

HLGEM
fuente
1

Me sorprende que nadie mencione que la diferencia clave entre estos dos es que la tabla temporal admite la inserción paralela, mientras que la variable de la tabla no. Debería poder ver la diferencia con el plan de ejecución. Y aquí está el video de los Talleres SQL en el Canal 9 .

Esto también explica por qué debería usar una variable temporal para tablas más pequeñas, de lo contrario, use una tabla temporal, como SQLMenace respondió antes.

Weihui Guo
fuente