web-dev-qa-db-fra.com

Données tronquées pour la colonne

DECLARE float_one, float_two, my_result NUMERIC(7,2)    
my_result = CONVERT(float_one/float_two, DECIMAL(7,2));

Dans cette requête mysql, je fais ce type d'opération dans une procédure stockée, mais l'environnement Linux phpmyadmin lance l'avertissement suivant:

Remarque: # 1265 Données tronquées pour la colonne 'float_one' à la ligne 5

Quelqu'un at-il une idée comment puis-je résoudre ce problème?

CREATE TABLE IF NOT EXISTS `float_sample` (
      `id` int(11) NOT NULL auto_increment,
      `first_number` float(10,3) default NULL,
      `second_number` float(10,3) default NULL,
      PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

INSERT INTO `float_sample` (`id`, `first_number`, `second_number`) 
VALUES    (1, 2.900, 1.900),    (2, 3.100, 22.100);

et la procédure

DELIMITER $$
DROP PROCEDURE IF EXISTS float_test$$
CREATE PROCEDURE float_test(IN my_ID INT)  

BEGIN
DECLARE first_float, second_float DECIMAL(10,3);

DECLARE done INT DEFAULT 0;

DECLARE myCursor CURSOR FOR 
        SELECT `first_number`, `second_number`
        FROM `float_sample`
        WHERE `id` = my_ID;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

OPEN myCursor;
my_loop:LOOP
    FETCH myCursor INTO first_float, second_float;

    IF done = 1 THEN
        LEAVE my_loop;
    END IF;

END LOOP my_loop;
CLOSE myCursor;

-- SELECT first_float, second_float;
END;$$
DELIMITER ;

et le résultat

1 70 10:41:36    CALL mytests.float_test(1) 0 row(s) affected, 2 warning(s):
1265 Data truncated for column 'first_float' at row 2
1265 Data truncated for column 'second_float' at row 2  0.032 sec
6
dole doug

Quelle est la valeur de float_one au moment de la conversion ???

Notez cet exemple de MySQL 5.5.12 sous Windows

mysql> select convert(20000,decimal(7,2));
+-----------------------------+
| convert(20000,decimal(7,2)) |
+-----------------------------+
|                    20000.00 |
+-----------------------------+
1 row in set (0.00 sec)

mysql> select convert(200000,decimal(7,2));
+------------------------------+
| convert(200000,decimal(7,2)) |
+------------------------------+
|                     99999.99 |
+------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------------------------------+
| Level   | Code | Message                                                               |
+---------+------+-----------------------------------------------------------------------+
| Warning | 1264 | Out of range value for column 'convert(200000,decimal(7,2))' at row 1 |
+---------+------+-----------------------------------------------------------------------+

1 row in set (0.00 sec)

Il est possible que des données aient été tronquées si un nombre supérieur à 99999,99 était dans float_one. Peut-être que mysql convertissait float_one et float_two en DECIMAL (7,2) individuellement avant d'effectuer la division. Essayez d'utiliser DECIMAL (10,2) ou plus pour prendre en charge de grandes valeurs.

MISE À JOUR 2011-07-25 15:05 EDT

Il y a un problème de troncature certain en cours ici

Selon Manuel de référence MySQL, page 442, paragraphes 2,

Les valeurs DECIMAL et NUMERIC sont stockées sous forme de chaînes, plutôt que sous forme de nombres binaires à virgule flottante, afin de préserver la précision décimale de ces nombres. Un caractère est utilisé pour chaque chiffre de la valeur, le point déciaml (si échelle> 0) et le signe - (pour les nombres négatifs). Si l'échelle est 0, les valeurs DÉCIMALES et NUMÉRIQUES ne contiennent ni point décimal ni partie fractionnaire.

La plage maximale de DECIMAL et NUMERIC est la même que pour DOUBLE, mais la plage réelle pour la colonne DECIMAL ou NUMERIC donnée peut être limitée par la précision et l'échelle d'une colonne donnée. Lorsqu'une telle colonne se voit attribuer une valeur avec plus de chiffres après la virgule décimale autorisée par l'échelle spécifiée, la valeur est arrondie à cette échelle. Lorsqu'une colonne DECIMAL ou NUMERIC se voit attribuer une valeur dont l'amplitude dépasse la plage impliquée par la précision et l'échelle spécifiées (ou par défaut), MySQL stocke la valeur représentant le point de terminaison correspondant de cette plage.

Vous devez adapter à une plus grande précision et/ou échelle.

Voici un exemple expliquant pourquoi

J'ai écrit cette procédure stockée en utilisant vos spécifications pour DECMIAL et NUMERIC.

DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`NumTest` $$
CREATE PROCEDURE `test`.`NumTest` (num1 NUMERIC(7,2), num2 NUMERIC(7,2))
BEGIN

  DECLARE float_one,float_two,my_result NUMERIC(7,2);
  DECLARE f1,f2 DOUBLE(7,2);

  SET f1 = num1;
  SET f2 = num2;

  SET float_one = num1;
  SET float_two = num2;

  SELECT f1 / f2;
  SELECT float_one / float_two;
  SELECT CONVERT(float_one / float_two,DECIMAL(7,2));
  SET my_result = CONVERT(float_one / float_two,DECIMAL(7,2));
  SELECT my_result;

END $$

DELIMITER ;

J'ai utilisé deux valeurs: 290,0 et 14,5 pour ce test.

Avant d'appeler la procédure stockée NumTest, j'ai calculé manuellement 290,0/14,5

mysql> select 290.0 / 14.5;
+--------------+
| 290.0 / 14.5 |
+--------------+
|     20.00000 |
+--------------+
1 row in set (0.00 sec)

J'ai divisé chaque nombre par 100, 10000 et 1000000 et j'ai réessayé

mysql> select 2.9 / .145;
+------------+
| 2.9 / .145 |
+------------+
|   20.00000 |
+------------+
1 row in set (0.00 sec)

mysql> select .029 / .00145;
+---------------+
| .029 / .00145 |
+---------------+
|    20.0000000 |
+---------------+
1 row in set (0.00 sec)

mysql> select .00029 / .0000145;
+-------------------+
| .00029 / .0000145 |
+-------------------+
|      20.000000000 |
+-------------------+
1 row in set (0.00 sec)

Jusqu'ici tout va bien !!! Maintenant, pour la procédure stockée.

mysql> call numtest(290.0,14.5);
+-----------+
| f1 / f2   |
+-----------+
| 20.000000 |
+-----------+
1 row in set (0.00 sec)

+-----------------------+
| float_one / float_two |
+-----------------------+
|             20.000000 |
+-----------------------+
1 row in set (0.00 sec)

+---------------------------------------------+
| CONVERT(float_one / float_two,DECIMAL(7,2)) |
+---------------------------------------------+
|                                       20.00 |
+---------------------------------------------+
1 row in set (0.00 sec)

+-----------+
| my_result |
+-----------+
|     20.00 |
+-----------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Donc, mes deux chiffres d'origine fonctionnent bien. Divisons-les par 100 et réessayons.

mysql> call numtest(2.9,0.145);
+-----------+
| f1 / f2   |
+-----------+
| 19.333333 |
+-----------+
1 row in set (0.00 sec)

+-----------------------+
| float_one / float_two |
+-----------------------+
|             19.333333 |
+-----------------------+
1 row in set (0.00 sec)

+---------------------------------------------+
| CONVERT(float_one / float_two,DECIMAL(7,2)) |
+---------------------------------------------+
|                                       19.33 |
+---------------------------------------------+
1 row in set (0.00 sec)

+-----------+
| my_result |
+-----------+
|     19.33 |
+-----------+
1 row in set (0.00 sec)

Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+-------------------------------------------+
| Level | Code | Message                                   |
+-------+------+-------------------------------------------+
| Note  | 1265 | Data truncated for column 'num2' at row 2 |
+-------+------+-------------------------------------------+
1 row in set (0.00 sec)

ATTENDEZ, nous avons perdu un peu de précision. Qu'est-il arrivé ??? Comment est-ce arrivé ??? Vous devez accepter plus de chiffres décimaux (> 2)

MISE À JOUR 2011-07-25 17:37 EDT

J'ai substitué (7,2) par (10,7) dans la procédure stockée et j'ai récupéré la bonne précision

mysql> call numtest(2.9,0.145);
+----------------+
| f1 / f2        |
+----------------+
| 20.00000000000 |
+----------------+
1 row in set (0.00 sec)

+-----------------------+
| float_one / float_two |
+-----------------------+
|        20.00000000000 |
+-----------------------+
1 row in set (0.01 sec)

+----------------------------------------------+
| CONVERT(float_one / float_two,DECIMAL(10,7)) |
+----------------------------------------------+
|                                   20.0000000 |
+----------------------------------------------+
1 row in set (0.03 sec)

+------------+
| my_result  |
+------------+
| 20.0000000 |
+------------+
1 row in set (0.05 sec)

Query OK, 0 rows affected (0.06 sec)

mysql>
3
RolandoMySQLDBA

D'après ce que je peux voir, votre procédure stockée se contente de parcourir les lignes - elle ne "sauvegarde les données" nulle part et vous avez commenté le select:

- SELECT first_float, second_float;

Oui, vous recevez des avertissements lorsque les champs float(10,3) sont convertis en decimal(10,3). Qu'ils posent problème ou non dépend de ce que vous voulez faire avec les champs.

Est-ce juste un échantillon du vrai code? Vous devrez peut-être en publier d'autres ...