2009-06-10 7 views
13

J'ai une table avec deux colonnes: price (int) et price_display (varchar).Comment extraire une valeur numérique d'une chaîne dans une requête MySQL?

le prix est le prix numérique réel, par ex. "9990"

price_display est la représentation visuelle, par ex. "$ 9,99" ou "9.99Fr"

Je suis en mesure de confirmer les deux colonnes correspondent via regexp:

price_display pas regexp Format (prix/1000, 2)

Mais dans le cas d'une discordance, je veux extraire la valeur de la colonne price_display et la placer dans la colonne des prix, le tout dans le contexte d'une instruction de mise à jour. Je n'ai pas été capable de comprendre comment.

Merci.

Répondre

30

Cette fonction fait le travail de seulement retourner les chiffres 0-9 de la chaîne, qui fait le travail bien pour résoudre votre problème, peu importe ce que préfixes ou postfixes que vous avez.

http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#815

Copié ici pour référence:

SET GLOBAL log_bin_trust_function_creators=1; 
DROP FUNCTION IF EXISTS digits; 
DELIMITER | 
CREATE FUNCTION digits(str CHAR(32)) RETURNS CHAR(32) 
BEGIN 
    DECLARE i, len SMALLINT DEFAULT 1; 
    DECLARE ret CHAR(32) DEFAULT ''; 
    DECLARE c CHAR(1); 

    IF str IS NULL 
    THEN 
    RETURN ""; 
    END IF; 

    SET len = CHAR_LENGTH(str); 
    REPEAT 
    BEGIN 
     SET c = MID(str, i, 1); 
     IF c BETWEEN '0' AND '9' THEN 
     SET ret=CONCAT(ret,c); 
     END IF; 
     SET i = i + 1; 
    END; 
    UNTIL i > len END REPEAT; 
    RETURN ret; 
END | 
DELIMITER ; 

SELECT digits('$10.00Fr'); 
#returns 1000 
+0

En plus de la solution ci-dessus, dans le cas où le numéro commence juste ou se termine par une lettre ou un caractère (par exemple $ 10.00 ou 10.00Fr), il existe une solution plus simple et plus efficace pour extraire les nombres. – Angel

+0

Génial, merci. Un petit conseil aux autres lecteurs: si vous tournez 'IF c BETWEEN' dans' IF c NOT BETWEEN', vous pouvez utiliser cette fonction pour faire correspondre tous les caractères qui ne sont pas **. – user2428118

0

Ceci est un "horreur de codage", les schémas de base de données relationnelles devraient pas être écrit comme ça!

Vous devez écrire un code complexe et inutile pour valider les données.

Essayez quelque chose comme ceci:

SELECT CONCAT('$',(price/1000)) AS Price FROM ... 

En outre, vous pouvez utiliser un float, double ou real au lieu d'un nombre entier.

Si vous devez stocker des données de devise, vous pouvez envisager d'ajouter un champ de devise ou utiliser les fonctions de paramètres régionaux du système pour les afficher au format correct.

+2

Merci pour la réponse. Malheureusement, je n'ai aucun contrôle sur le schéma; Je l'ai simplement hérité et je dois maintenant corriger les données contenues. –

4

Une approche serait d'utiliser la fonction REPLACE():

UPDATE my_table 
SET price = replace(replace(replace(price_display,'Fr',''),'$',''),'.','') 
WHERE price_display not regexp format(price/1000, 2); 

Cela fonctionne pour les données des exemples que vous avez donné:

'$9.99' 
'9.99Fr' 

Les deux résultat dans 999 dans mon test. Avec une mise à jour comme celle-ci, il est important d'être sûr de sauvegarder la base de données en premier et d'être conscient des formats des éléments. Vous pouvez voir tous les « méchants » en faisant cette requête:

SELECT DISTINCT price_display 
FROM  my_table 
WHERE price_display not regexp format(price/1000, 2) 
ORDER BY price_display; 
+0

Merci, c'est une bonne chose, mais j'ai bien sûr beaucoup plus de symboles monétaires que "$" et "Fr", donc la chaîne de remplacement serait hors de contrôle. –

+0

Je serais intéressé de voir quelques résultats représentatifs de la dernière requête que j'ai posté. Combien y a-t-il d'éléments distincts? Peut-être qu'avec quelques autres exemples, moi ou quelqu'un d'autre peut faire un morceau de code plus éclairé. Une chose que je pensais est que si vous avez des articles formatés comme "10 $" représentant 10,00 $ alors ma méthode échoue. Mais alors vous n'avez pas affiché cela comme une possibilité. La dernière requête que j'ai posté serait utile pour clouer la requête qui fera la magie. – artlung

1

Je crée une procédure qui détectent le premier numéro dans une chaîne et retourner ce, sinon retourner 0.

DROP FUNCTION IF EXISTS extractNumber; 
    DELIMITER // 
    CREATE FUNCTION extractNumber (string1 VARCHAR(255)) RETURNS INT(11) 
     BEGIN 
     DECLARE position, result, longitude INT(11) DEFAULT 0; 
     DECLARE string2 VARCHAR(255); 
     SET longitude = LENGTH(string1); 
     SET result = CONVERT(string1, SIGNED); 
     IF result = 0 THEN 
      IF string1 REGEXP('[0-9]') THEN 
       SET position = 2; 
       checkString:WHILE position <= longitude DO 
        SET string2 = SUBSTR(string1 FROM position); 
        IF CONVERT(string2, SIGNED) != 0 THEN 
         SET result = CONVERT(string2, SIGNED); 
         LEAVE checkString; 
        END IF; 
        SET position = position + 1; 
       END WHILE; 
      END IF; 
     END IF; 
     RETURN result; 
    END // 
    DELIMITER ; 
4

Pour moi CASTING le terrain a fait l'affaire:

CAST(price AS UNSIGNED) // Pour nombre entier positif

CAST(price AS SIGNED) // Pour entier négatif et positif

IF(CAST(price AS UNSIGNED)=0,REVERSE(CAST(REVERSE(price) AS UNSIGNED)),CAST(price AS UNSIGNED)) // Fix lorsque le prix commence par quelque chose d'autre puis un chiffre

Pour plus de détails voir:

https://dev.mysql.com/doc/refman/5.0/en/cast-functions.html

+0

adore ça. Juste a couru un inverse sur la chaîne, l'a casted, et l'a inversé encore pour obtenir le nombre à la fin de la chaîne. Simple .. Efficace .. Fiable. Je vous remercie. – Barry

+0

Génial. C'est génial pour diviser les numéros de maison et les caractères, par exemple. '' '40A''' dans' '' 40''' et '' 'A''' comme ceci: ' '' SELECT CAST (t.col UNSIGNED) AS "Numéro", REPLACE (t.col , CAST (t.col AS UNSIGNED), "") AS "Caractère" '' ' –

+0

Je ne sais pas de quoi vous parlez. Cette approche est erronée: exécuter 'REVERSE (CAST (REVERSE (numstring) comme UNSIGNED))' sur une valeur comme 'string-10' retournera' 1' et non '10'. – WoodrowShigeru

1

Retour dernier numéro de la chaîne:

CREATE FUNCTION getLastNumber(str VARCHAR(255)) RETURNS INT(11) 
DELIMETER // 
BEGIN 
    DECLARE last_number, str_length, position INT(11) DEFAULT 0; 
    DECLARE temp_char VARCHAR(1); 
    DECLARE temp_char_before VARCHAR(1); 


IF str IS NULL THEN 
    RETURN -1; 
END IF; 

SET str_length = LENGTH(str); 

WHILE position <= str_length DO 
    SET temp_char = MID(str, position, 1); 

    IF position > 0 THEN 
     SET temp_char_before = MID(str, position - 1, 1); 
    END IF; 

    IF temp_char BETWEEN '0' AND '9' THEN 
     SET last_number = last_number * 10 + temp_char; 
    END IF; 
    IF (temp_char_before NOT BETWEEN '0' AND '9') AND 
      (temp_char BETWEEN '0' AND '9') THEN     
     SET last_number = temp_char; 
    END IF; 

    SET position = position + 1; 
END WHILE; 

RETURN last_number; 
END// 
DELIMETER; 

appellent ensuite cette fonction:

sélectionnez getLastNumber ("ssss111www222w"); impression 222

select getLastNumber ("ssss111www222www3332"); imprimer 3332

Questions connexes