smallmoney - ¿Debería elegir los tipos de datos MONEY o DECIMAL(x, y) en SQL Server?
tipo de dato para precio en sql server (12)
Tengo curiosidad por saber si existe o no una diferencia real entre el tipo de datos del money
y algo como el decimal(19,4)
(que es lo que el dinero usa internamente, creo).
Soy consciente de que el money
es específico de SQL Server. Quiero saber si hay una razón convincente para elegir una sobre la otra; la mayoría de las muestras de SQL Server (por ejemplo, la base de datos AdventureWorks) usan money
y no decimal
para cosas como información de precios.
¿Debo seguir usando el tipo de datos de dinero, o hay un beneficio al usar decimal en su lugar? El dinero es menos caracteres para escribir, pero eso no es una razón válida :)
Acabamos de encontrarnos con un problema muy similar y ahora tengo un +1 por no usar Money, excepto en la presentación de nivel superior. Tenemos varias tablas (de hecho, un comprobante de venta y una factura de venta), cada una de las cuales contiene uno o más campos de Dinero por razones históricas, y debemos realizar un cálculo proporcional para calcular la cantidad del Impuesto de la factura total que es relevante para cada una. Línea en el comprobante de venta. Nuestro calculo es
vat proportion = total invoice vat x (voucher line value / total invoice value)
Esto resulta en un cálculo de dinero / dinero del mundo real que causa errores de escala en la parte de la división, que luego se multiplica en una proporción de cuba incorrecta. Cuando estos valores se agregan posteriormente, terminamos con una suma de las proporciones de la tina que no suman el valor total de la factura. Si alguno de los valores entre paréntesis hubiera sido un decimal (estoy a punto de lanzar uno de ellos como tal), la proporción de la cuba sería correcta.
Cuando los paréntesis no estaban originalmente, esto solía funcionar, supongo que debido a los valores más grandes involucrados, simulaba una escala más alta. Añadimos los corchetes porque primero hacía la multiplicación, que en algunos casos extraños hacía saltar la precisión disponible para el cálculo, pero ahora esto ha causado un error mucho más común.
Acabo de ver esta entrada de blog: Money vs. Decimal en SQL Server .
Lo que básicamente dice que el dinero tiene un problema de precisión ...
declare @m money
declare @d decimal(9,2)
set @m = 19.34
set @d = 19.34
select (@m/1000)*1000
select (@d/1000)*1000
Para el tipo de money
, obtendrá 19.30 en lugar de 19.34. No estoy seguro de si hay un escenario de aplicación que divida el dinero en 1000 partes para el cálculo, pero este ejemplo expone algunas limitaciones.
Bueno, me gusta el MONEY
! Es un byte más barato que DECIMAL
, y los cálculos se realizan más rápido porque (debajo de las coberturas) las operaciones de suma y resta son esencialmente operaciones de números enteros. El ejemplo de @ SQLMenace, que es una gran advertencia para los que no lo saben, podría aplicarse igualmente a los indicadores de INT
, donde el resultado sería cero. Pero esa no es razón para no usar números enteros, cuando sea apropiado .
Por lo tanto, es perfectamente "seguro" y apropiado usar MONEY
cuando lo que estás tratando es MONEY
y usarlo de acuerdo con las reglas matemáticas que siguen (igual que INT
eger).
¿Hubiera sido mejor si SQL Server promoviera la división y multiplicación de MONEY
en DECIMAL
s (o FLOAT
s?), Posiblemente, pero no eligieron hacer esto; Tampoco decidieron promocionar los integrales a los FLOAT
al dividirlos.
MONEY
no tiene ningún problema de precisión; el hecho de que DECIMAL
tenga un tipo intermedio más grande utilizado durante los cálculos es solo una ''característica'' de usar ese tipo (y no estoy realmente seguro de hasta qué punto se extiende esa ''característica'').
Para responder a la pregunta específica, una "razón convincente"? Bueno, si desea un rendimiento máximo absoluto en una SUM(x)
donde x
podría ser DECIMAL
o MONEY
, entonces MONEY
tendrá una ventaja.
Además, no olvide que es el primo más pequeño, SMALLMONEY
, SMALLMONEY
4 bytes, pero alcanza un máximo de 214,748.3647
, que es bastante pequeño para el dinero, y por lo tanto no suele ser una buena opción.
Para probar el punto en torno al uso de tipos intermedios más grandes, si asigna el intermedio explícitamente a una variable, DECIMAL
sufre el mismo problema:
declare @a decimal(19,4)
declare @b decimal(19,4)
declare @c decimal(19,4)
declare @d decimal(19,4)
select @a = 100, @b = 339, @c = 10000
set @d = @a/@b
set @d = @d*@c
select @d
Produce 2950.0000
(está bien, entonces al menos DECIMAL
redondeado en lugar de MONEY
truncado, igual que lo haría un entero).
Como un punto contrario a la orientación general de las otras respuestas. Ver los muchos beneficios del dinero ... Tipo de datos! en la Guía de SQLCAT para Motor Relacional
Específicamente señalaría lo siguiente
Al trabajar en las implementaciones de los clientes, encontramos algunos números de rendimiento interesantes con respecto al tipo de datos de dinero. Por ejemplo, cuando Analysis Services se configuró en el tipo de datos de moneda (de doble) para coincidir con el tipo de datos de dinero de SQL Server, hubo una mejora del 13% en la velocidad de procesamiento (filas / s). Para obtener un rendimiento más rápido dentro de SQL Server Integration Services (SSIS) para cargar 1.18 TB en menos de treinta minutos, como se señaló en SSIS 2008, el rendimiento mundial de ETL, se observó que el cambio de las cuatro columnas decimales (9,2) con un tamaño de 5 bytes en la tabla TPC-H LINEITEM a dinero (8 bytes) mejoraron la velocidad de inserción masiva en un 20% ... El motivo de la mejora del rendimiento se debe al protocolo de flujo de datos tabulares (TDS) de SQL Server, que tiene el principio de diseño clave para transferir datos en formato binario compacto y lo más cerca posible del formato de almacenamiento interno de SQL Server. Empíricamente, esto se observó durante el SSIS 2008: prueba de rendimiento de ETL récord mundial utilizando Kernrate; el protocolo se redujo significativamente cuando el tipo de datos se cambió a dinero de decimal. Esto hace que la transferencia de datos sea lo más eficiente posible. Un tipo de datos complejo necesita un análisis adicional y ciclos de CPU para manejar que un tipo de ancho fijo.
Así que la respuesta a la pregunta es "depende". Debe tener más cuidado con ciertas operaciones aritméticas para preservar la precisión, pero puede encontrar que las consideraciones de rendimiento valen la pena.
Encontré una razón para usar el decimal sobre el dinero en el tema de precisión.
DECLARE @dOne DECIMAL(19,4),
@dThree DECIMAL(19,4),
@mOne MONEY,
@mThree MONEY,
@fOne FLOAT,
@fThree FLOAT
SELECT @dOne = 1,
@dThree = 3,
@mOne = 1,
@mThree = 3,
@fOne = 1,
@fThree = 3
SELECT (@dOne/@dThree)*@dThree AS DecimalResult,
(@mOne/@mThree)*@mThree AS MoneyResult,
(@fOne/@fThree)*@fThree AS FloatResult
Solo pruébalo y toma tu decisión.
Me doy cuenta de que WayneM ha declarado que sabe que el dinero es específico de SQL Server. Sin embargo, él está preguntando si hay razones para usar el dinero en lugar de decimal o viceversa y creo que todavía debe declararse una razón obvia y es que usar decimal significa que es una cosa menos de qué preocuparse si alguna vez tiene que cambiar su DBMS. - Lo que puede pasar.
¡Haga que sus sistemas sean lo más flexibles posible!
No debe usar dinero cuando necesita hacer multiplicaciones / divisiones en el valor. El dinero se almacena de la misma manera que se almacena un entero, mientras que el decimal se almacena como un punto decimal y dígitos decimales. Esto significa que el dinero perderá precisión en la mayoría de los casos, mientras que el decimal solo lo hará cuando se vuelva a convertir a su escala original. El dinero es un punto fijo, por lo que su escala no cambia durante los cálculos. Sin embargo, como es un punto fijo cuando se imprime como una cadena decimal (en lugar de como una posición fija en una cadena base 2), los valores hasta la escala de 4 se representan exactamente. Así que para sumar y restar, el dinero está bien.
Un decimal se representa internamente en la base 10 y, por lo tanto, la posición del punto decimal también se basa en el número de la base 10. Lo que hace que su parte fraccional represente su valor exactamente, al igual que con el dinero. La diferencia es que los valores intermedios de decimal pueden mantener una precisión de hasta 38 dígitos.
Con un número de punto flotante, el valor se almacena en binario como si fuera un número entero, y la posición del punto decimal (o binario, ej.) Es relativa a los bits que representan el número. Debido a que es un punto decimal binario, los números de base 10 pierden precisión justo después del punto decimal. 1/5, o 0.2, no se puede representar precisamente de esta manera. Ni el dinero ni el decimal sufren de esta limitación.
Es bastante fácil convertir dinero a decimal, realizar los cálculos y luego almacenar el valor resultante en un campo o variable de dinero.
Desde mi punto de vista, quiero que las cosas que pasan con los números solo sucedan sin tener que pensar demasiado en ellos. Si todos los cálculos se van a convertir a decimales, entonces, para mí, solo querría usar decimales. Me gustaría guardar el campo de dinero para fines de visualización.
En cuanto al tamaño, no veo suficiente diferencia para cambiar de opinión. El dinero toma de 4 a 8 bytes, mientras que el decimal puede ser 5, 9, 13 y 17. Los 9 bytes pueden cubrir todo el rango que los 8 bytes de dinero pueden. Índice (la comparación y la búsqueda deben ser comparables).
Nunca debes usar dinero. No es preciso, y es pura basura; Siempre use decimal / numérico.
Ejecuta esto para ver lo que quiero decir:
DECLARE
@mon1 MONEY,
@mon2 MONEY,
@mon3 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)
SELECT
@mon1 = 100, @mon2 = 339, @mon3 = 10000,
@num1 = 100, @num2 = 339, @num3 = 10000
SET @mon4 = @mon1/@mon2*@mon3
SET @num4 = @num1/@num2*@num3
SELECT @mon4 AS moneyresult,
@num4 AS numericresult
Salida: 2949.0000 2949.8525
A algunas de las personas que dijeron que no divides dinero por dinero:
Aquí está una de mis consultas para calcular las correlaciones, y cambiar eso a dinero da resultados incorrectos.
select t1.index_id,t2.index_id,(avg(t1.monret*t2.monret)
-(avg(t1.monret) * avg(t2.monret)))
/((sqrt(avg(square(t1.monret)) - square(avg(t1.monret))))
*(sqrt(avg(square(t2.monret)) - square(avg(t2.monret))))),
current_timestamp,@MaxDate
from Table1 t1 join Table1 t2 on t1.Date = traDate
group by t1.index_id,t2.index_id
Quiero dar una visión diferente de MONEY vs. NUMERICAL, en gran parte mi propia experiencia y experiencia ... Mi punto de vista aquí es MONEY, porque he trabajado con él durante un tiempo considerable y nunca he usado mucho NUMERICAL. .
DINERO Pro:
Tipo de datos nativos . Utiliza un tipo de datos nativo ( entero ) igual que un registro de CPU (32 o 64 bits), por lo que el cálculo no requiere una sobrecarga innecesaria, por lo que es más pequeño y más rápido ... DINERO necesita 8 bytes y NUMÉRICO (19, 4) ) necesita 9 bytes (12.5% más grande) ...
El DINERO es más rápido siempre y cuando se use para lo que fue destinado a ser (como dinero). ¿Qué rápido? Mi simple prueba
SUM
en 1 millón de datos muestra que el DINERO es 275 ms y NUMÉRICO 517 ms ... Eso es casi el doble de rápido ... ¿Por qué la prueba SUM? Ver el siguiente punto Pro- Mejor por dinero . El DINERO es el mejor para almacenar dinero y realizar operaciones, por ejemplo, en contabilidad. Un solo informe puede ejecutar millones de adiciones (SUM) y algunas multiplicaciones después de que se realiza la operación SUM. Para aplicaciones de contabilidad muy grandes, es casi el doble de rápido y es extremadamente significativo ...
- Baja precisión del dinero . El dinero en la vida real no necesita ser muy preciso. Quiero decir, a muchas personas les puede importar aproximadamente 1 centavo de USD, pero ¿qué hay de 0.01 centavo de USD? De hecho, en mi país, a los bancos ya no les importan los centavos (dígito tras coma decimal); No sé sobre el banco de Estados Unidos u otro país ...
DINERO Con:
- Precisión limitada . El DINERO solo tiene una precisión de cuatro dígitos (después de la coma), por lo que se debe convertir antes de realizar operaciones como la división ... Pero, de nuevo, el
money
no necesita ser tan preciso y debe utilizarse como dinero, no solo un número...
Pero ... grande, pero aquí está incluso su aplicación involucrada con dinero real, pero no la use en muchas operaciones SUM, como en contabilidad. Si usa muchas divisiones y multiplicaciones, entonces no debería usar DINERO ...
SQLMenace dijo que el dinero es inexacto. ¡Pero usted no multiplica / divide dinero por dinero! ¿Cuánto es 3 dólares por 50 centavos? 150 dolarescentes? Usted multiplica / divide el dinero por los escalares, que deberían ser decimales.
DECLARE
@mon1 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)
SELECT
@mon1 = 100,
@num1 = 100, @num2 = 339, @num3 = 10000
SET @mon4 = @mon1/@num2*@num3
SET @num4 = @num1/@num2*@num3
SELECT @mon4 AS moneyresult,
@num4 AS numericresult
Resultados en el resultado correcto:
moneyresult numericresult --------------------- --------------------------------------- 2949.8525 2949.8525
money
es bueno siempre y cuando no necesite más de 4 dígitos decimales, y se asegura de que los escalares, que no representan dinero, sean decimal
.
Todos los mensajes anteriores aportan puntos válidos, pero algunos no responden a la pregunta con precisión.
La pregunta es: ¿por qué alguien preferiría dinero cuando ya sabemos que es un tipo de datos menos preciso y puede causar errores si se usa en cálculos complejos?
Usted usa el dinero cuando no hace cálculos complejos y puede cambiar esta precisión por otras necesidades.
Por ejemplo, cuando no tiene que hacer los cálculos anteriores y necesita guardar 1 byte (el dinero toma 8 bytes y el decimal (19,4) toma 9 bytes).
Otro ejemplo, cuando no tiene que realizar los cálculos anteriores y necesita velocidad para procesar una gran cantidad de datos.
Otro ejemplo, cuando no tiene que realizar los cálculos anteriores, y necesita importar desde cadenas de texto de moneda válida. Intente hacer esto con otros tipos numéricos: SELECCIONE CONVERTIR (DINERO, ''$ 1,000.68'')
Todo es peligroso si no sabes lo que estás haciendo.
Incluso los tipos decimales de alta precisión no pueden salvar el día:
declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000
1.000000 <- Debería ser 0.6000000
Los tipos de money
son enteros.
Las representaciones de texto de smallmoney
y decimal(10,4)
pueden parecerse, pero eso no las hace intercambiables. ¿Te estremeces cuando ves las fechas almacenadas como varchar(10)
? Esto es lo mismo.
Detrás de escena, money
/ smallmoney
son solo un bigint
/ int
El punto decimal en la representación de texto del money
es una pelusa visual, al igual que los guiones en una fecha aaaa-mm-dd. SQL en realidad no los almacena internamente.
Con respecto a decimal
vs money
, elija lo que sea apropiado para sus necesidades. Los tipos de money
existen porque el almacenamiento de valores contables como múltiplos enteros de 1/10000 de unidad es muy común. Además, si está tratando con dinero real y cálculos más allá de la simple suma y resta, ¡no debería hacerlo en el nivel de la base de datos! Hágalo a nivel de la aplicación con una biblioteca que admita el redondeo de la banca (IEEE 754)