Ir al contenido


Foto

decimales en Lazarus-sqlite?


  • Por favor identifícate para responder
36 respuestas en este tema

#1 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 19 abril 2016 - 11:10

Llevo más de 4 días intentando guardar números en una variable con decimales en una bb.dd sqlite3. Pero no hay forma humana, he buscado y rebuscado y no hay nada que pueda conseguir, aunque si he avanzado algo.

 

La variable declarada en CREATE TABLE es precio FLOAT(3,2) (dos decimales y tres en parte entera).

 

Al guardar el nº desde un Tedit a la base de datos sentencio con:


php
  1. ZQuery2.ParamByName('precio').AsFloat:= StrToFloat(form3.edit7.text);

pues si inserto 12,90 al imprimir el precio en dbgrid este me muestra 12,9 (con un numero decimal solamente)

         si inserto 12,95 este me muestra con redondeo hacia arriba a 13 sin parte decimal (13,00)

         si inserto 12,94 este me muestra con redondeo hacia abajo 12,9 (con un numero decimal solamente)

 

mi intención es guardar un nº entero máx de 3 digitos parte entera y dos decimales exactos (12,95 - 30,10 - 25,88, 125,16 etc...) y con el símbolo € que se muestre tambien en la grilla. Si la parte entera es de dos digitos que no salta 018,20 por ejemplo.

 

He probado con DECIMAL(3,2) pero ('precio').AsDecimal no lo reconoce, he probado con DOUBLE y tampoco....

 

Un saludo


  • 0

#2 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 19 abril 2016 - 11:40

Es normal, lo que tienes que hacer es formatear la salida, debes hacer uso de la propiedad DisplayFormat del campo en el dataset.
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 19 abril 2016 - 11:44

Llevo más de 4 días intentando guardar números en una variable con decimales en una bb.dd sqlite3. Pero no hay forma humana, he buscado y rebuscado y no hay nada que pueda conseguir, aunque si he avanzado algo.

 

La variable declarada en CREATE TABLE es precio FLOAT(3,2) (dos decimales y tres en parte entera).

 

Al guardar el nº desde un Tedit a la base de datos sentencio con:


php
  1. ZQuery2.ParamByName('precio').AsFloat:= StrToFloat(form3.edit7.text);

pues si inserto 12,90 al imprimir el precio en dbgrid este me muestra 12,9 (con un numero decimal solamente)

         si inserto 12,95 este me muestra con redondeo hacia arriba a 13 sin parte decimal (13,00)

         si inserto 12,94 este me muestra con redondeo hacia abajo 12,9 (con un numero decimal solamente)

 

mi intención es guardar un nº entero máx de 3 digitos parte entera y dos decimales exactos (12,95 - 30,10 - 25,88, 125,16 etc...) y con el símbolo € que se muestre tambien en la grilla. Si la parte entera es de dos digitos que no salta 018,20 por ejemplo.

 

He probado con DECIMAL(3,2) pero ('precio').AsDecimal no lo reconoce, he probado con DOUBLE y tampoco....

 

Un saludo

 

Pues yo creo que debe ser el motor (ZEOS) porque con Delphi (FireDAC) sin formatos ni nada no tiene problema

 

Saludos

Archivos adjuntos


  • 0

#4 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 19 abril 2016 - 12:39

He visto un FormatFloat, pero no veo porque me redondea. Veré enecumene que alternativas me quedan, pero viendo lo que dice egostar, pienso que nada funciona a la primera!

 

un saludo


  • 0

#5 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 19 abril 2016 - 07:34

Decimal 3,2

Eso significa que tu número tiene en total 3 dígitos, dos son decimales

Eso en sql estándar

En sqlite el sistema de tipos es distinto, no existe el tipificado fuerte, más bien las columnas tienen una afinidad, o un tipo sugestivo

Si necesitas precisión exacta, no uses valores en coma flotante. Usa un tipo punto fijo, como currency.

Quizá esto sea de ayuda


http://pages.cs.wisc...edy/exact-float

http://floating-point-gui.de
  • 0

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 19 abril 2016 - 08:22

Yo estuve leyendo sobre los tipos de datos en SQLite, y veo que SQLite es bastante especial en este sentido. Como dice Agustín, los tipos se dan por afinidad... una especie de "compatibilidad" y que SQLite trata de imitar y de convertir.

 

Técnicamente el tipo FLOAT(3,2) como lo tienes no existe. SQLite, si es que estoy entendiendo bien, lo que trata de hacer es tomar lo que le pases y asumir a ese FLOAT() como quizá un REAL o un INTEGER.

Luego, desde Lazarus estás asumiendo que es equivalente a un tipo flotante (para ser concreto Double) y cuando haces el paso desde Lazarus hacia la base de datos, es posible que ahí es cuando se rompen las afinidades...

 

Lo mejor sería respetar los propios tipos de datos que ofrece SQLite.

Cuando se trabaja con valores que serán usados con fines monetarios, desde el lado del sistema, lo más adecuado es valerse del tipo Currency. Que es un tipo bastante especial. Técnicamente es un entero, pero que se comporta como un tipo real con punto fijo (4 decimales para ser exactos, aunque visualmente como sabemos sólo son usados 2). Esto permite realizar operaciones con exactitud. ¿Porqué 4 decimales? Para que cuando uno haga divisiones entre dos currency tener el valor exacto (un número de 2 decimales dividido por otro de 2 da como resultado uno de 4). Internamente el compilador va a multiplicar o dividir por 1000 cuando sea necesario para "correr la coma".

Por ello, si se emplea campos persistentes, al campo que representa este valor monetario (Zeos genera un TFloatField) lo mejor es establecerle la propiedad Currency en true para forzar a que internamente use Currency y no el Double.

 

Ahora bien, hay que encontrar el tipo más conveniente equivalente al Currency para SQLite. Al menos en Firebird tradicionalmente se emplea NUMERIC(12, 2), pero en SQLite no existe el NUMERIC(), aunque tiene afinidad hacia éste. De lo que leo, primero intentará convertirlo al INTEGER y luego al REAL. En Firebird internamente un NUMERIC(d,p) es un tipo entero y aplica aritmética fija como lo que hace el Currency de Delphi y Lazarus. Ahora bien, dependiendo de la cantidad de dígitos (d) reservará un entero de 16 o 32 bits. En SQLite por lo que leo, el integer es mucho más abierto y puede ser de 1, 2, 4, 6 y 8 bytes a nivel de disco... e internamente en memoria ram todo parece traducirse en un Int64 (o lo que es lo mismo, en 8 bytes)

¡Lo peor es que esto de la afinidad hace que sea tan ambiguo porque incluso hasta pareciera permitirle pasarle texto! Y dejar que el "motor" haga el trabajo de conversión. Eso es al menos lo que yo entiendo.

Esto a mi me confunde, y por ello no me animé a comentar antes. :(

 

Me siento perdido, ¿Al final como es conveniente hacer la equivalencia entre el Currency de Delphi y Lazarus y SQLite? ¿usar INTEGER? ¿TEXT? Arrojen luz porque estoy ciego.

 

Saludos,


  • 1

#7 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 20 abril 2016 - 11:22

Chicos parece ser que he dado con la "solución" siempre con vuestra gran ayuda. El caso es que la tipo currency lo usé días atras pero de ninguna forma me funcionaba. Ahora probando de nuevo con este tipo que en realidad no se muy bien que hace y no lo defino como un tipo de variable "estandar", he hecho lo siguiente:

 

La variable declarada en CREATE TABLE es precio CURRENCY(3,2). Enecumene indica que (3,2) son 3 digitos del numero, pero 2 son decimales, luego quedaría así Y,ZZ?

Yo he puesto CURRENCY(4,3) pero esto en SQLite que quiere decir?, mi objetivo es que pueda introducir máx YYY,ZZ (3 digitos enteros y 2 decimales), pero claro al introducir datos me deja introducirle 12345,22 por ejemplo es decir (5,2) incluso más decimales.

 

Y en 


php
  1. ZQuery2.ParamByName('precio').AsCurrency:= StrToFloat(form3.edit7.text);

lo cual ya aparecen los datos en el dbgrid con dos decimales pero claro con un separación de "." ejemplo: 25.88 - 128.35 ......

 

Ahora para que el dbgrid aparezca el simbolo "€" ejemplo 25.88€ - 128.35€ ? porque al cargar el dbgrid simplemente hay un SELECT * FROM personas y Open

 

Un saludo


  • 0

#8 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 20 abril 2016 - 12:12

No conozco bien los interines de SQLite, y a mi me confunde su sistema de afinidad. Pareciera ser que SQLite puede aceptar lo que le pongas y el solito trata de encontrar algún formato de los afines y de convertirlo.

No veo en la lista que puede consultarse en el link de mi anterior post que CURRENCY sea afin a los tipos de SQLite, por lo que quizá te esté aceptando el contenido tal cual lo ves, en forma textual.

 

En todo caso sugeriría que emplearas NUMERIC(d,p) o DECIMAL(d,p) siendo d la cantidad TOTAL de dígitos y p la precisión (o cantidad de decimales). Tu estás entendiendo mal el concepto. Si tu buscas que tus valores sean ddd,pp (es decir 3 dígitos enteros y 2 decimales) el tipo debe declararse entonces como NUMERIC(5,2) ¡Los decimales también son dígitos! Tienes 3 + 2 dígitos en total.

 

Luego en tu aplicación, cuando pases un valor al campo por medio de .AsCurrency:


delphi
  1. TuDataSet.FieldByName('NombreDelCampo').AsCurrency := ...

No hagas un StrToFloat(). Es erróneo, el .AsCurrency espera un valor del tipo Currency y no un Double como lo que devuelve StrToFloat().

Para convertir un string a Currency tienes que usar StrToCurr()

 

Ahora para que te muestre el símbolo de moneda tienes que indicarle en la propiedad DisplayFormat del campo del DBedit el formato en cuestión. Aunque debería de hacerlo de forma "automática" al indicarle al campo/columna del DBGrid en cuestión que se trata de un Currency al establecerle la propiedad homónima en true.

Estas dos opciones funcionan si empleas los campos persistentes.

 

Se puede hacerlo en tiempo en ejecución, sin los campos persistentes también con algo como esto:


delphi
  1. ElDBGrid.Columns[NumeroDeColumna].DisplayFormat := '$ ###.00';
  2.  

Por dar un ejemplo.


  • 1

#9 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 20 abril 2016 - 02:14

Que tal maestro Delphius, pues he cambiado lo que me indicas DECIMAL(5,2)  y en este caso con respecto a mi respuesta anterior, ahora me imprime en el dbgrid con separador de "," en vez de ".".

 

Decir tambien que aunque muy bien Agustin_Ortu me indicaba, el concepto ya voy entiendo. Con DECIMAL(5,2),cuando inserto por ejemplo 1234,22 en el TEdit al imprimir en pantalla me saca 1234,2 (entiendo que es porque ya hay 5 dígitos).

 

No utilizdo DBEdit sino TEdit y en este último no veo propiedades como el DBEdit. pero como mencionas que puedo hacerlo en tiempo de ejecución, supongo que este intrucción última que mencionas, irá despues del SELECT * FROM personas y del OPEN cierto? para cuando imprima en pantalla el DBGRid la columna precio saque la máscara €.

 

Insertando la máscara Delphius, ahora lo que veo es que  si inserto 1256,27 (6 digitos) cuando visualizo el DbGrid me imprime eso mismo 1256,27 € porque? si es DECIMAL(5,2) debería visualizar 1256,2€

 

Un saludo 


  • 0

#10 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 20 abril 2016 - 05:28

Hay varias formas de cambiar el formato, algunas giran en torno a la arquitectura TDataSet, otras son de caracter mas general

 

  • Variables globlales (en FreePascal no estoy seguro si es DecimalsSeparator,  ThousandsSeparators... etc o FormatSettings.DecimalSeparator), de la unidad SysUtils
  • Funciones de conversion: ejemplo, CurrToStrF, CurrToStr (la version sobrecargada que acepta un registro TFormatSettings para parametrizar el formato)
  • Usando mascaras para componentes visuales, no todos las tienen
  • TField creo que tiene una propiedad DisplayFormat por ahi para modificar el formato
  • TField tiene un evento OnGetText en el cual podes modificar justamente el texto que se muestra en cualquier control data-aware, si las mascaras no son suficientes

Editado por Agustin Ortu, 20 abril 2016 - 05:30 .

  • 0

#11 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 21 abril 2016 - 11:18

Gracias Agustin, copiado tu gran aporte de conocimiento en mi cuaderno de trabajo.


  • 0

#12 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 21 abril 2016 - 11:25

Lo que no entiendo es porque si el tipo es DECIMAL(5,2), introduzco precio = 1234567,123 en el dbgrid me visualiza 1234567,12 por ejemplo.

NO consigo acotar que al usuario no se le permita introducir más enteros de la cuenta, solo quiero (eee,dd).


  • 0

#13 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 21 abril 2016 - 09:18

Lo que no entiendo es porque si el tipo es DECIMAL(5,2), introduzco precio = 1234567,123 en el dbgrid me visualiza 1234567,12 por ejemplo.

NO consigo acotar que al usuario no se le permita introducir más enteros de la cuenta, solo quiero (eee,dd).

 

Hola

 

Pero para eso lo que necesitas no es un tipo de dato, lo que tu necesitas es una máscara que limite la cantidad de dígitos. Un EditMask por ejemplo....

 

Saludos

Archivos adjuntos


  • 0

#14 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 22 abril 2016 - 06:21

Lo que no entiendo es porque si el tipo es DECIMAL(5,2), introduzco precio = 1234567,123 en el dbgrid me visualiza 1234567,12 por ejemplo.
NO consigo acotar que al usuario no se le permita introducir más enteros de la cuenta, solo quiero (eee,dd).

Has probado insertar el valor en la bd...porque una cosa es que se vea en la grilla...pero logicamente no deberia permitirte insertarlo..
Puedes usar una mascara.
Colocas un maskedit y el cero indica que va un numero
00000,00..



Enviado desde mi SM-G530M mediante Tapatalk
  • 0

#15 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 23 abril 2016 - 05:07

Probaré como indicáis Egostar y giulichajari. Entonces que sentido tiene indicarle DECIMAL (5,2) (ddd,pp) y la máscara DBGrid1.Columns[8].DisplayFormat := '###.00€'; ?

 

Quizás lo que yo entiendo, es que DECIMAL(5,2) con esto se consigue que la base de datos, no pueda guardar más de 5 decimales, pero en mi caso si lo hace y esa máscara '###.00€' nos permite visualizar 3 enteros y 2 decimales, pero en mi caso saca más más enteros si los introduzco previamente.


  • 0

#16 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 23 abril 2016 - 07:41

Un decimal(5,2) tiene 5digitos de los cuales 2 son decimales. Osea qie 3 son enteros..
De -999,99 a 999,99.
Has visto en el motor de sqlite si em numero se guarda cn la coma? Prueba hacer un select..

Enviado desde mi SM-G530M mediante Tapatalk
  • 0

#17 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 23 abril 2016 - 03:17

Un decimal(5,2) tiene 5digitos de los cuales 2 son decimales. Osea qie 3 son enteros..
De -999,99 a 999,99.
Has visto en el motor de sqlite si em numero se guarda cn la coma? Prueba hacer un select..

Enviado desde mi SM-G530M mediante Tapatalk

Si se guarda, ya que al imprimir en pantalla, la "," aparece. Pero el caso está en que si le introduzco más decimales de 3 enteros, tambien los acepta y al imprimir aparecen igualmente.


  • 0

#18 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 23 abril 2016 - 07:05

Hola Dooper, yo estoy medio ocupado con mi proyecto. Tendría que ponerme a estudiar como es esto de los tipos y afinidades de SQLite para entender que es lo que hace, por ello no te sabría decir mucho.

El porqué te permite guardar números más grandes aún habiendo definido un rango acotado no tengo una respuesta certera. Sólo puedo tirar alguna teoría... y puede que sea una combinación de 2 cosas:

1. El tipo DECIMAL(d,p) no existe en SQLite. Es uno de los considerados afines (o con afinidad). De lo poco que había leído, y que me resulta un tanto confuso de entender. SQLite trata de convertir lo que le pongas a algún tipo nativo. En último caso, toma el dato textualmente y acepta el contenido tal cual es. Quizá por esto último es que observas que puedes meter números mayores y también sea motivo de porqué te coloca cierto símbolo de separador de decimal.

Desconozco porqué se maneja así. SQLite es un motor monousuario de base de datos muy difundido, y me parece raro que no se apegue al estándar SQL en los tipos y la forma de tratarlos.

2. Internamente tanto el NUMERIC(d,p) como el DECIMAL(d,p) se adapta a alguno de los tipos de enteros que si están estandarizados: enteros de 16bits (o smallint), de 32bits (o integer), o bien de 64bits (int64 o BigInt). Dependiendo del valor de p, todo motor de base de datos empleará de forma interna para realizar las operaciones y almacenar temporalmente alguno de estos tres tipos de enteros. Debido a esto tanto un NUMERIC(9,2) como un NUMERIC(5,2) internamente se representan con el mismo entero: 16bits. Ahora, si fuera un NUMERIC(12,2) por ejemplo el tipo empleado sería un integer o entero de 32bits. Y de nuevo, así será también un NUMERIC(17,4) por poner otro ejemplo. Si ahora elevamos a 18 dígitos el tipo se va al entero de 64bits.

Esto también puede explicar porqué es que te acepta números mayores, ya que el tamaño se lo permite.

En Firebird las cosas son ligeramente diferentes, si intentas meter un número mayor este te reporta un error. Respeta el tipo definido y esto es así ya que SI existen el NUMERIC y el DECIMAL. Aún cuando internamente la memoria tenga la capacidad.

 

Tendría que hacer algunas pruebas, y no estoy con tiempo. Para controlar el rango de valores lo que puedes hacer es emplear el evento OnSetText del campo y allí validar si el valor ingresado está en el rango [-999,99, 999,99]

 

Saludos,


  • 0

#19 dooper

dooper

    Advanced Member

  • Miembros
  • PipPipPip
  • 298 mensajes

Escrito 24 abril 2016 - 04:16

Gracias amigo Delphius.Usé la mascara que aconsejo Egostar, pero claro, esa másca al indicarle 000,00, me obliga a introducir 3 enteros para que salte a los decimales, sino lo hago, me da error. El usuario puede teclear 12,33 y no que de esta manera, hay que teclear 012,33 para que lo acepte.

 

En cuanto a lo que expones, es un lio total, aunque si no fuera posible, aceptaría que el usuario introduzca los enteros que desee y no limitarlo a 3 enteros como deseo, con tal que los decimales si los respete, perfecto! Ahora funciona con la máscara indicada, pero como dije anteriormente, hay ese leve problema, seguiré investigando por si hay algun configuración en la máscara, que no sepa, y permita corregir ese problema.

 

Si averiguas algo más sobre decimales en SQLITE3, como los demás amigos de este foro, estaría bien salir de dudas, para no ir dando golpe de ciego!

 

Un saludo


  • 0

#20 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 24 abril 2016 - 08:13

Prueba 000,##
  • 0




IP.Board spam blocked by CleanTalk.