Je cherche à trouver des enregistrements dans une table qui correspondent à un nombre spécifique que l'utilisateur entre. Ainsi, l'utilisateur peut entrer 12345, mais cela pourrait être 123zz4-5 dans la base de données.
J'imagine que quelque chose comme cela fonctionnerait si les fonctions PHP fonctionnaient dans MySQL.
SELECT * FROM foo WHERE preg_replace("/[^0-9]/","",bar) = '12345'
Quelle est la fonction ou le moyen équivalent de le faire avec MySQL uniquement?
Bien que ce ne soit pas joli et que les résultats ne correspondent pas, cela aide:
SELECT * FROM foo WHERE bar LIKE = '%1%2%3%4%5%'
J'aimerais quand même trouver une meilleure solution similaire à celle de la question initiale.
Je réalise que c'est un sujet ancien, mais après avoir recherché ce problème sur Google, je n'ai pas trouvé de solution simple (j'ai vu les vénérables agents mais je pense que c'est une solution plus simple). Voici donc une fonction que j'ai écrite et qui semble fonctionner assez bien.
DROP FUNCTION IF EXISTS STRIP_NON_DIGIT;
DELIMITER $$
CREATE FUNCTION STRIP_NON_DIGIT(input VARCHAR(255))
RETURNS VARCHAR(255)
BEGIN
DECLARE output VARCHAR(255) DEFAULT '';
DECLARE iterator INT DEFAULT 1;
WHILE iterator < (LENGTH(input) + 1) DO
IF SUBSTRING(input, iterator, 1) IN ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) THEN
SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
END IF;
SET iterator = iterator + 1;
END WHILE;
RETURN output;
END
$$
Il n'y a pas d'expression rationnelle à remplacer, seulement une chaîne simple REPLACE ().
MySQL a l'opérateur REGEXP, mais il ne s'agit que d'un testeur de correspondance et non d'un substitut;
SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*';
Cela ressemble à votre version avec LIKE mais correspond plus précisément. Les deux fonctionneront également mal, nécessitant une analyse complète de la table sans index.
La réponse la plus votée (@ user1467716) n'est pas la plus rapide. Bravo à eux pour avoir donné une proposition de travail à rebondir!
Ceci est une version améliorée:
DELIMITER ;;
DROP FUNCTION IF EXISTS `STRIP_NON_DIGIT`;;
CREATE DEFINER=`root`@`localhost` FUNCTION `STRIP_NON_DIGIT`(input VARCHAR(255)) RETURNS VARCHAR(255) CHARSET utf8
READS SQL DATA
BEGIN
DECLARE output VARCHAR(255) DEFAULT '';
DECLARE iterator INT DEFAULT 1;
DECLARE lastDigit INT DEFAULT 1;
DECLARE len INT;
SET len = LENGTH(input) + 1;
WHILE iterator < len DO
-- skip past all digits
SET lastDigit = iterator;
WHILE ORD(SUBSTRING(input, iterator, 1)) BETWEEN 48 AND 57 AND iterator < len DO
SET iterator = iterator + 1;
END WHILE;
IF iterator != lastDigit THEN
SET output = CONCAT(output, SUBSTRING(input, lastDigit, iterator - lastDigit));
END IF;
WHILE ORD(SUBSTRING(input, iterator, 1)) NOT BETWEEN 48 AND 57 AND iterator < len DO
SET iterator = iterator + 1;
END WHILE;
END WHILE;
RETURN output;
END;;
Tester 5000 fois sur un serveur de test:
-- original
Execution Time : 7.389 sec
Execution Time : 7.257 sec
Execution Time : 7.506 sec
-- ORD between not string IN
Execution Time : 4.031 sec
-- With less substrings
Execution Time : 3.243 sec
Execution Time : 3.415 sec
Execution Time : 2.848 sec
La façon la plus simple de le faire consiste à utiliser l'opérateur MySQL REGEXP à la fois:
WHERE foo LIKE '1\D*2\D*3\D*4\D*5'
Ce n’est pas particulièrement joli, mais MySQL n’a pas de fonction preg_replace
donc je pense que c’est le meilleur choix que vous puissiez obtenir.
Personnellement, si ces données uniquement numériques sont si importantes, je conserverais un champ séparé uniquement pour contenir les données supprimées. Vos recherches seront beaucoup plus rapides qu'avec la recherche par expression régulière.
J'ai une situation similaire, en faisant correspondre les produits à des codes à barres où le code à barres ne stocke parfois pas de chiffres alpha, donc il faut trouver 102.2234 dans la base de données lors de la recherche de 1022234.
En fin de compte, je viens d'ajouter un nouveau champ, reference_number aux tables products, et php supprime les nombres sans alpha du product_number pour renseigner reference_number chaque fois qu'un nouveau produit est ajouté.
Vous devez effectuer une analyse ponctuelle de la table pour créer tous les champs numéro de référence des produits existants.
Vous pouvez ensuite configurer votre index, même si la rapidité n’est pas un facteur déterminant pour cette opération. Il est néanmoins judicieux de maintenir le bon fonctionnement de la base de données afin que cette requête ne l’empêche pas de ralentir les autres requêtes.
Je suis tombé sur cette solution. La première réponse de l'utilisateur 1467716 fonctionnera dans phpMyAdmin avec une petite modification: ajoutez une deuxième balise de délimiteur à la fin du code.
la version de phpMyAdmin est 4.1.14; MySQL version 5.6.20
J'ai aussi ajouté un limiteur de longueur en utilisant
DECLARE count INT DEFAULT 0;
dans les déclarations
AND count < 5
dans l'instruction WHILE
SET COUNT=COUNT+1;
dans l'instruction IF
Forme définitive:
DROP FUNCTION IF EXISTS STRIP_NON_DIGIT;
DELIMITER $$
CREATE FUNCTION STRIP_NON_DIGIT(input VARCHAR(255))
RETURNS VARCHAR(255)
BEGIN
DECLARE output VARCHAR(255) DEFAULT '';
DECLARE iterator INT DEFAULT 1;
DECLARE count INT DEFAULT 0;
WHILE iterator < (LENGTH(input) + 1) AND count < 5 DO --limits to 5 chars
IF SUBSTRING(input, iterator, 1) IN ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ) THEN
SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
SET COUNT=COUNT+1;
END IF;
SET iterator = iterator + 1;
END WHILE;
RETURN output;
END
$$
DELIMITER $$ --added this
Pour moi, il n'y a pas de regex à remplacer, mais j'ai trouvé cette solution;
--Create a table with numbers
DROP TABLE IF EXISTS ints;
CREATE TABLE ints (i INT UNSIGNED NOT NULL PRIMARY KEY);
INSERT INTO ints (i) VALUES
( 1), ( 2), ( 3), ( 4), ( 5), ( 6), ( 7), ( 8), ( 9), (10),
(11), (12), (13), (14), (15), (16), (17), (18), (19), (20);
--Then extract the numbers from the specified column
SELECT
bar,
GROUP_CONCAT(SUBSTRING(bar, i, 1) ORDER BY i SEPARATOR '')
FROM foo
JOIN ints ON i BETWEEN 1 AND LENGTH(bar)
WHERE
SUBSTRING(bar, i, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
GROUP BY bar;
Cela fonctionne pour moi et j'utilise MySQL 5.0
J'ai aussi trouvé ce lieu qui pourrait aider.
Quelle est la taille de la table avec foo? Si elle est petite et que la vitesse importe peu, vous pouvez extraire l'ID de la ligne et foo, la parcourir à l'aide des fonctions de remplacement PHP à comparer, puis extraire les informations souhaitées par numéro de ligne.
Bien sûr, si la table est trop grande, cela ne fonctionnera pas bien.
je pensais partager cela puisque je l'ai construit à partir de la fonction. Je me suis réorganisé pour que je puisse le lire plus facilement (je ne suis que côté serveur).
Vous l'appelez en passant un nom de table et un nom de colonne pour qu'il supprime tous les caractères non numériques existants de cette colonne. J'ai hérité d'un grand nombre de structures de table incorrectes qui placent une tonne de champs int sous la forme varchar;.
drop procedure if exists strip_non_numeric_characters;
DELIMITER ;;
CREATE PROCEDURE `strip_non_numeric_characters`(
tablename varchar(100)
,columnname varchar(100)
)
BEGIN
-- =============================================
-- Author: <Author,,David Melton>
-- Create date: <Create Date,,2/26/2019>
-- Description: <Description,,loops through data and strips out the bad characters in whatever table and column you pass it>
-- =============================================
#this idea was generated from the idea STRIP_NON_DIGIT function
#https://stackoverflow.com/questions/287105/mysql-strip-non-numeric-characters-to-compare
declare input,output varchar(255);
declare iterator,lastDigit,len,counter int;
declare date_updated varchar(100);
select column_name
into date_updated
from information_schema.columns
where table_schema = database()
and extra rlike 'on update CURRENT_TIMESTAMP'
and table_name = tablename
limit 1;
#only goes up to 255 so people don't run this for a longtext field
#just to be careful, i've excluded columns that are part of keys, that could potentially mess something else up
set @find_column_length =
concat("select character_maximum_length
into @len
from information_schema.columns
where table_schema = '",database(),"'
and column_name = '",columnname,"'
and table_name = '",tablename,"'
and length(ifnull(character_maximum_length,100)) < 255
and data_type in ('char','varchar')
and column_key = '';");
prepare stmt from @find_column_length;
execute stmt;
deallocate prepare stmt;
set counter = 1;
set len = @len;
while counter <= ifnull(len,1) DO
#this just removes it by putting all the characters before and after the character i'm looking at
#you have to start at the end of the field otherwise the lengths don't stay in order and you have to run it multiple times
set @update_query =
concat("update `",tablename,"`
set `",columnname,"` = concat(substring(`",columnname,"`,1,",len - counter,"),SUBSTRING(`",columnname,"`,",len - counter,",",counter - 1,"))
",if(date_updated is not null,concat(",`",date_updated,"` = `",date_updated,"`
"),''),
"where SUBSTRING(`",columnname,"`,",len - counter,", 1) not REGEXP '^[0-9]+$';");
prepare stmt from @update_query;
execute stmt;
deallocate prepare stmt;
set counter = counter + 1;
end while;
END ;;
DELIMITER ;
essayez cet exemple. Ceci est utilisé pour les numéros de téléphone, mais vous pouvez le modifier selon vos besoins.
-- function removes non numberic characters from input
-- returne only the numbers in the string
CREATE DEFINER =`root`@`localhost` FUNCTION `remove_alpha`(inputPhoneNumber VARCHAR(50))
RETURNS VARCHAR(50)
CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE inputLenght INT DEFAULT 0;
-- var for our iteration
DECLARE counter INT DEFAULT 1;
-- if null is passed, we still return an tempty string
DECLARE sanitizedText VARCHAR(50) DEFAULT '';
-- holder of each character during the iteration
DECLARE oneChar VARCHAR(1) DEFAULT '';
-- we'll process only if it is not null.
IF NOT ISNULL(inputPhoneNumber)
THEN
SET inputLenght = LENGTH(inputPhoneNumber);
WHILE counter <= inputLenght DO
SET oneChar = SUBSTRING(inputPhoneNumber, counter, 1);
IF (oneChar REGEXP ('^[0-9]+$'))
THEN
SET sanitizedText = Concat(sanitizedText, oneChar);
END IF;
SET counter = counter + 1;
END WHILE;
END IF;
RETURN sanitizedText;
END
pour utiliser cette fonction définie par l'utilisateur (UDF) ..__, disons que vous avez une colonne de numéros de téléphone:
col1
(513)983-3983
1-838-338-9898
phone983-889-8383
select remove_alpha(col1) from mytable
Le résultat serait;
5139833983
18383389898
9838898383