Firebird e valores monetários
Já li vários textos à respeito do assunto e ainda tenho dúvidas. Até agora sempre usei DOUBLE PRECISION no IB/FB para simular campos monetários, mas já ouvi falar que é melhor NUMERIC/DECIMAL para evitar certos problemas de precisão mas tb dizem que nem esse tipo de campo está totalmente livre de problemas de arredondamento e que em alguns casos o tipo de dupla precisão ainda é melhor. Por isso, gostaria que os colegas do fórum opinassem a respeito, dizendo que tipo de dados usam para simular valores monetários (no caso do NUMERIC/DECIMAL indiquem a precisão tb), se tem algum problema de compatibilidade com o tipo Currency do Delphi, problemas de arredondamento, esse tipo de coisa.
Gandalf.nho
Curtidas 0
Respostas
Vinicius2k
17/10/2005
Eu nunca tive problemas com DOUBLE PRECISION porque sempre usei NUMERIC e, com este, nunca tive problemas.
Quando possível, eu utilizo no máximo NUMERIC(9,e) para que o IB/FB utilize o tipo INTEGER para armazenar o dado, economizando (eu acho) tamanho do registro, já que a partir de NUMERIC(10,e) ele irá utilizar INT64 internamente.
Nunca tive problemas de arredondamento e de incompatibilidade, apesar de TFmtBCD ter me dado ´algum trabalho´ no início (com dbExpress).
Creio que muitas vezes os problemas com DOUBLE e NUMERIC podem ser evitados. Por exemplo, na aplicação, eu nunca faço CAMPO_1 := CAMPO_2 * CAMPO_3. Eu procuro já tratar o arredondamento correto dentro da aplicação. Algo como: CAMPO_1 := ROUND(CAMPO_2 * CAMPO_3, 2), fazendo com que o campo já receba o valor da forma correta.
Se vc também utiliza esta prática, pode ser esta a razão de nunca ter tido problemas com DOUBLE, já que todos reclamam, deve mesmo ser ruim sem o tratamento adequado.
T+
Quando possível, eu utilizo no máximo NUMERIC(9,e) para que o IB/FB utilize o tipo INTEGER para armazenar o dado, economizando (eu acho) tamanho do registro, já que a partir de NUMERIC(10,e) ele irá utilizar INT64 internamente.
Nunca tive problemas de arredondamento e de incompatibilidade, apesar de TFmtBCD ter me dado ´algum trabalho´ no início (com dbExpress).
Creio que muitas vezes os problemas com DOUBLE e NUMERIC podem ser evitados. Por exemplo, na aplicação, eu nunca faço CAMPO_1 := CAMPO_2 * CAMPO_3. Eu procuro já tratar o arredondamento correto dentro da aplicação. Algo como: CAMPO_1 := ROUND(CAMPO_2 * CAMPO_3, 2), fazendo com que o campo já receba o valor da forma correta.
Se vc também utiliza esta prática, pode ser esta a razão de nunca ter tido problemas com DOUBLE, já que todos reclamam, deve mesmo ser ruim sem o tratamento adequado.
T+
GOSTEI 0
Afarias
17/10/2005
como pode ser visto no post abaixo:
http://forum.clubedelphi.net/viewtopic.php?t=69062&highlight=float
Tipos de ponto-flutuante são um ´problema´ em geral para a computação, não importa o banco de dados, sistema operacional ou o q for.
Tipos como FLOAT ([url=http://www.math.grin.edu/~stone/courses/fundamentals/IEEE-reals.html]IEEE[/url] single precision: 7 dígitos) ou DOUBLE PRECISION (IEEE double precision: 15 dígitos) são por natureza ´imprecisos´.
Para valores monetários e outros situações q exigem precisão (e geralmente poucas casas decimais) diversas soluções são abordadas como a adoção de tipos inteiros ou [url=http://en.wikipedia.org/wiki/Binary-coded_decimal]BCDs[/url] para representar valores de ponto-flutuante
No Interbase/Firebird, os ´tipos´ NUMERIC/DECIMAL não representam TIPOS reais mas apenas uma dessas soluções mencionadas acima.
O IB/FB determina a [melhor] forma de armazenamento do NUMERIC dependendo da precisão (´tamanho máximo´) do valor e do dialeto da base.
Para bancos no dialeto1 uma precisão até 9 é suportada como INTEGER internamente, provendo valores representados perfeitamente. Já no Dialeto 3, com o ´advento´ do INT64, é suportado até uma precisão de 18
É uma boa prática *sempre* usar NUMERIC, a não ser q realmente seja desejado/necessário um campo de ponto-flutuante (FLOAT ou DOUBLE)
T+
http://forum.clubedelphi.net/viewtopic.php?t=69062&highlight=float
Tipos de ponto-flutuante são um ´problema´ em geral para a computação, não importa o banco de dados, sistema operacional ou o q for.
Tipos como FLOAT ([url=http://www.math.grin.edu/~stone/courses/fundamentals/IEEE-reals.html]IEEE[/url] single precision: 7 dígitos) ou DOUBLE PRECISION (IEEE double precision: 15 dígitos) são por natureza ´imprecisos´.
Para valores monetários e outros situações q exigem precisão (e geralmente poucas casas decimais) diversas soluções são abordadas como a adoção de tipos inteiros ou [url=http://en.wikipedia.org/wiki/Binary-coded_decimal]BCDs[/url] para representar valores de ponto-flutuante
No Interbase/Firebird, os ´tipos´ NUMERIC/DECIMAL não representam TIPOS reais mas apenas uma dessas soluções mencionadas acima.
O IB/FB determina a [melhor] forma de armazenamento do NUMERIC dependendo da precisão (´tamanho máximo´) do valor e do dialeto da base.
Para bancos no dialeto1 uma precisão até 9 é suportada como INTEGER internamente, provendo valores representados perfeitamente. Já no Dialeto 3, com o ´advento´ do INT64, é suportado até uma precisão de 18
É uma boa prática *sempre* usar NUMERIC, a não ser q realmente seja desejado/necessário um campo de ponto-flutuante (FLOAT ou DOUBLE)
T+
GOSTEI 0
Afarias
17/10/2005
Ah, e quanto ao Currency do Delphi, é mais uma solução dessas ... o Currency serve para representar valores de campos flutuante (até 4 casas decimais) mas é um INTEIRO
(Obs: o Delphi não implementa um tipo BCD, até onde sei)
T+
(Obs: o Delphi não implementa um tipo BCD, até onde sei)
T+
GOSTEI 0
Gandalf.nho
17/10/2005
E qual precisão devo usar? Alguns falam em (18,2), outros em (14,2) ou (9,2). A maioria dos meus cálculos envolvendo valores monetários geralmente são simples somas ou subtrações, raramente preciso trabalhar com divisão ou multiplicação. Dá para converter sem problema um campo DOUBLE PRECISION (já populado) para NUMERIC/DECIMAL?
GOSTEI 0
Afarias
17/10/2005
|E qual precisão devo usar? Alguns falam em (18,2), outros em (14,2) ou
|(9,2).
Não existe uma precisão melhor q outra. No dialeto 1 o melhor é usar no máximo 9, já no dialeto 3 pode usar até 18.
Não tem fórmula aqui, use a maior precisão q acha q vai precisar. Exemplo: se para um determinado campo o valor máximo extrapolando tudo é 9 milhões, e ele é monetário (2 casas decimais) então vc usaria um campo NUMERIC(9,2) -- sendo assim: 9.999.999,99
(veja q esse número não é o máximo real, apenas o máximo admitindo a precisão e escala)
|Dá para converter sem problema um campo DOUBLE PRECISION (já
|populado) para NUMERIC/DECIMAL?
Se vc não tem problemas no uso de DOUBLE não acho q deve ficar preocupada em migrar o q já existe. Mas se for migrar, esteja atenta para ver se a conversão foi ok
T+
|(9,2).
Não existe uma precisão melhor q outra. No dialeto 1 o melhor é usar no máximo 9, já no dialeto 3 pode usar até 18.
Não tem fórmula aqui, use a maior precisão q acha q vai precisar. Exemplo: se para um determinado campo o valor máximo extrapolando tudo é 9 milhões, e ele é monetário (2 casas decimais) então vc usaria um campo NUMERIC(9,2) -- sendo assim: 9.999.999,99
(veja q esse número não é o máximo real, apenas o máximo admitindo a precisão e escala)
|Dá para converter sem problema um campo DOUBLE PRECISION (já
|populado) para NUMERIC/DECIMAL?
Se vc não tem problemas no uso de DOUBLE não acho q deve ficar preocupada em migrar o q já existe. Mas se for migrar, esteja atenta para ver se a conversão foi ok
T+
GOSTEI 0
Vinicius2k
17/10/2005
(Obs: o Delphi não implementa um tipo BCD, até onde sei)
Anderson,
Não implementa mesmo ou vc acha que a implementação é ruim?
Pergunto pois, até onde eu sei, a partir do Delphi 6 a implementação já existe, inclusive utilizada no dbExpress. A unit da implementação é a ´FMTBcd´.
Não tem fórmula aqui, use a maior precisão q acha q vai precisar. Exemplo: se para um determinado campo o valor máximo extrapolando tudo é 9 milhões, e ele é monetário (2 casas decimais) então vc usaria um campo NUMERIC(9,2) -- sendo assim: 9.999.999,99
Concordo. Creio que a precisão escolhida não tem influência no resultado final de operações matemáticas porque o dado é armazenado como INTEGER. Acho que apenas seguir a regra ´básica´ de não criar campos maiores do que se precisa é suficiente. Como disse anteriomente, quando possível eu uso até NUMERIC(9,e) para que o IB/FB não precise utilizar INT64 para armazenar o dado. Mas se não for possível, de NUMERIC(10,e) em diante eu uso muitas variações, dependendo do caso.
T+
GOSTEI 0
Afarias
17/10/2005
|Não implementa mesmo ou vc acha que a implementação é ruim?
Não implementa. Bom, mas pra ser mais exato ...
Não implementra um tipo BCD (Decimal) nativo!
O TBCD é um registro usado para representar um número BCD (vindo de um banco de dados por exemplo). O TBCDField por exemplo pega esse numero BCD e então transforma internamente em um Currency (Binário) para manipular (cálculos, etc) e converte novamente para a representação (TBCD) quando tem q enviar de volta para o banco.
É verdade q no D6 em diante (meu Delphi é o 5 ainda) há mais funções para trabalhar com TBCDs, mas ainda para isso (a melhor forma) é usar Variants.
Então, o fato é: O Delphi não possui um tipo BCD *nativo* :)
Para manipular números mesmo no código (cálculos e tals) vc tem/deve mesmo usar Currency
Valeu pela observação! Abraço
T+
Não implementa. Bom, mas pra ser mais exato ...
Não implementra um tipo BCD (Decimal) nativo!
O TBCD é um registro usado para representar um número BCD (vindo de um banco de dados por exemplo). O TBCDField por exemplo pega esse numero BCD e então transforma internamente em um Currency (Binário) para manipular (cálculos, etc) e converte novamente para a representação (TBCD) quando tem q enviar de volta para o banco.
É verdade q no D6 em diante (meu Delphi é o 5 ainda) há mais funções para trabalhar com TBCDs, mas ainda para isso (a melhor forma) é usar Variants.
Então, o fato é: O Delphi não possui um tipo BCD *nativo* :)
Para manipular números mesmo no código (cálculos e tals) vc tem/deve mesmo usar Currency
Valeu pela observação! Abraço
T+
GOSTEI 0
Gandalf.nho
17/10/2005
Bem que o Firebird podia implementar um tipo monetário nativo, como outros bancos tem (até o Paradox e o Access tem...). Não sei como é em relação aos outros bancos cliente/servidor
GOSTEI 0
Afarias
17/10/2005
|Bem que o Firebird podia implementar um tipo monetário nativo
Mas essa é a função do NUMERIC.
Não necessariamente tem q se usar um BCD, o q importa é o resultado não? Acho a solução de usar ponto-fixo (fixed-point decimals) muito boa.
T+
Mas essa é a função do NUMERIC.
Não necessariamente tem q se usar um BCD, o q importa é o resultado não? Acho a solução de usar ponto-fixo (fixed-point decimals) muito boa.
T+
GOSTEI 0