¿Hay alguna diferencia entre NUMERIC y DECIMAL?

47

Sé que los tipos de datos NUMÉRICOS y DECIMALES en SQL Server funcionan de la misma manera: la sintaxis para crearlos es la misma, los rangos de valores que puede almacenar en ellos son los mismos, etc.

Sin embargo, la documentación de MSDN describe la relación entre los dos de la siguiente manera:

numérico es funcionalmente equivalente a decimal.

Normalmente, cuando veo el calificador " funcionalmente equivalente", significa que las dos cosas no son exactamente lo mismo, sino que son dos tipos diferentes que no se pueden distinguir desde el exterior .

¿Es esta implicación verdadera? ¿Existen diferencias entre NUMÉRICO y DECIMAL que simplemente se comportan de la misma manera con un observador externo? ¿O son realmente equivalentes, por ejemplo, NUMERIC es solo un sinónimo heredado de DECIMAL?

KutuluMike
fuente
1
Puede encontrar diferencias en el soporte fuera de SQL Server, por ejemplo, me acaban de dar cuenta de esta extraña diferencia en SSIS .
Aaron Bertrand

Respuestas:

56

En realidad son equivalentes, pero son tipos independientes, y no técnicamente sinónimos, como ROWVERSIONy TIMESTAMP, aunque pueden haber sido referidos como sinónimos en la documentación alguna vez . Ese es un significado ligeramente diferente del sinónimo (por ejemplo, no se pueden distinguir excepto en el nombre, ninguno es un alias para el otro). Irónico, ¿verdad?

Lo que interpreto de la redacción en MSDN es en realidad:

Estos tipos son idénticos, solo tienen nombres diferentes.

Aparte de los type_idvalores, todo aquí es idéntico:

SELECT * FROM sys.types WHERE name IN (N'numeric', N'decimal');

No tengo absolutamente ningún conocimiento de las diferencias de comportamiento entre los dos, y volviendo a SQL Server 6.5, siempre los he tratado como 100% intercambiables.

para DECIMAL (18,2) y NUMERIC (18,2)? ¿Asignar uno al otro es técnicamente una "conversión"?

Solo si lo haces explícitamente. Puede probar esto fácilmente creando una tabla y luego inspeccionando el plan de consultas para consultas que realizan conversiones explícitas o, como es de esperar, implícitas. Aquí hay una tabla simple:

CREATE TABLE [dbo].[NumDec]
(
    [num] [numeric](18, 0) NULL,
    [dec] [decimal](18, 0) NULL
);

Ahora ejecute estas consultas y capture el plan:

DECLARE @num NUMERIC(18,0);
DECLARE @dec DECIMAL(18,0);

SELECT 
  CONVERT(DECIMAL(18,0), [num]), -- conversion
  CONVERT(NUMERIC(18,0), [dec])  -- conversion
FROM dbo.NumDec
UNION ALL SELECT [num],[dec] 
  FROM dbo.NumDec WHERE [num] = @dec  -- no conversion
UNION ALL SELECT [num],[dec] 
  FROM dbo.NumDec WHERE [dec] = @num; -- no conversion

Como se muestra en SQL Sentry Plan Explorer *, el plan no es realmente interesante:

ingrese la descripción de la imagen aquí

Pero la pestaña Expresiones seguro es:

ingrese la descripción de la imagen aquí

Como comenté anteriormente, tenemos conversiones explícitas donde las solicitamos, pero no conversiones explícitas donde podríamos haberlas esperado. Parece que el optimizador también los trata como intercambiables.

Siga adelante y pruebe esta prueba también (datos e índices).

CREATE TABLE [dbo].[NumDec2]
(
    [num] [numeric](18, 2) NULL,
    [dec] [decimal](18, 2) NULL
);

INSERT dbo.NumDec2([num],[dec])
SELECT [object_id] + 0.12, [object_id] + 0.12
  FROM sys.all_columns;

CREATE INDEX [ix_num] ON dbo.NumDec2([num]);
CREATE INDEX [ix_dec] ON dbo.NumDec2([dec]);

Ahora ejecute esta consulta:

DECLARE @num NUMERIC(18,2) = -1291334356.88,
        @dec NUMERIC(18,2) = -1291334356.88;

SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = @num
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = @dec;

El plan no tiene conversiones (de hecho, la pestaña Expresiones está vacía):

ingrese la descripción de la imagen aquí

Incluso estos no conducen a ninguna conversión inesperada. Por supuesto, lo ve en el RHS en el predicado, pero en ningún caso tuvo que ocurrir ninguna conversión contra los datos de la columna para facilitar la búsqueda (mucho menos forzar un escaneo).

SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(DECIMAL(18,2), @num)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(DECIMAL(18,2), @dec)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(NUMERIC(18,2), @num)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(NUMERIC(18,2), @dec)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(DECIMAL(18,2), @num)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(DECIMAL(18,2), @dec)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(NUMERIC(18,2), @num)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(NUMERIC(18,2), @dec);

Personalmente, prefiero usar el término DECIMALsolo porque es mucho más preciso y descriptivo. BITes "numérico" también.

* Disclaimer: I work for SQL Sentry.
Aaron Bertrand
fuente
Sé que SQL trata, por ejemplo, DECIMAL (18,2) y DECIMAL (18,0) como "tipos diferentes"; ¿Esto significa lo mismo para DECIMAL (18,2) y NUMERIC (18,2)? ¿Asignar uno al otro es técnicamente una "conversión"?
KutuluMike
@MichaelEdenfield Actualizó mi respuesta para su nueva pregunta.
Aaron Bertrand
1
Sé que esta es una respuesta anterior, pero acabo de encontrar un caso en el que pensar que numérico y decimal son sinónimos hizo que SQL Server generara un error. Solo pensé que debería dejar un comentario aquí en caso de que alguien más pudiera beneficiarse de ello.
Zohar Peled
@AaronBertrand He votado a favor de su respuesta porque es útil. Ambos tipos parecen ser idénticos, así que si ese es el caso, ¿por qué se nombran de manera diferente? ¿Hay alguna razón histórica ?
Ivanzinho
@Ivanzinho No estoy seguro de si su pregunta es retórica, hay un montón de teorías (tanto en esta página como en la respuesta que vinculó), pero si desea una respuesta definitiva, probablemente no tendrá suerte. Tendría que localizar a los ingenieros originales que implementaron primero ambos tipos en SQL Server, y confiar en su memoria tanto de esa implementación como de su interpretación del estándar en ese momento.
Aaron Bertrand
15

Sin embargo, son idénticos en la práctica (del estándar SQL 2003):

21) NUMERIC especifica el tipo de datos numérico exacto, con la precisión decimal y la escala especificada por precisiony scale.

22) DECIMAL especifica el tipo de datos numérico exacto, con la escala decimal especificada por el scaley la precisión decimal definida por la implementación igual o mayor que el valor del especificado precision.

Neil McGuigan
fuente
55
La pregunta fundamental entonces (y creo que la respuesta es no), fue que SQL Server implementó la precisión de DECIMAL de manera diferente a la de NUMERIC. Lo que el estándar dice que se podría hacer es mucho menos relevante que lo que se hizo realmente.
Aaron Bertrand
55
Gracias. Esta es una excelente respuesta a la pregunta de seguimiento de por qué hay dos tipos con nombres diferentes pero comportamiento idéntico.
Gabe