ユーザーが入力した特定の番号に一致するテーブル内のレコードを探しています。したがって、ユーザーは12345と入力できますが、データベースでは123zz4-5となる可能性があります。
PHP関数がMySQLで機能した場合、このようなものが機能すると思います。
SELECT * FROM foo WHERE preg_replace("/[^0-9]/","",bar) = '12345'
MySQLだけでこれを行うための同等の機能または方法は何ですか?
きれいではなく、一致しない結果が表示されますが、これは役立ちます。
SELECT * FROM foo WHERE bar LIKE = '%1%2%3%4%5%'
元の質問の項目と同様のより良い解決策を見つけたいと思っています。
これは古代のトピックであることはわかっていますが、この問題をグーグルで調べたとき、簡単な解決策を見つけることができませんでした(私は古くからのエージェントを見ましたが、これはより簡単な解決策だと思います)。
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
$$
最も賛成された 上記の回答 は最速ではありません。
跳ね返る実用的な提案をしたことに対する彼らへの完全な称賛!
これは改良版です:
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;;
テストサーバーで5000回のテスト:
-- 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
正規表現による置換はなく、プレーンな文字列REPLACE()のみです。
MySQLにはREGEXP演算子がありますが、これは単なる一致テスターであり、置換子ではないため、ロジックを裏返しにする必要があります。
SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*';
これはLIKEを使用したバージョンに似ていますが、より正確に一致します。どちらもパフォーマンスは同じでなく、インデックスなしで全表スキャンが必要です。
_REGEXP_REPLACE
_(MySQL 8+およびMariaDB 10.0.5+と互換性があります)を使用すると、簡単に好きなことができます
REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]])
パターンpatで指定された正規表現に一致する文字列expr内の出現箇所を置換文字列replで置き換え、結果の文字列を返します。 expr、pat、またはreplがNULLの場合、戻り値はNULLです。
試してみてください:
_SELECT REGEXP_REPLACE('123asd12333', '[a-zA-Z]+', '');
_
出力:
_12312333
_
このブログ投稿では、MySQL関数を介して文字列から数値以外の文字を削除する方法について詳しく説明しています。
SELECT NumericOnly("asdf11asf");
戻り値 11
http://venerableagents.wordpress.com/2011/01/29/mysql-numeric-functions/
私が考えることができる最も簡単な方法は、MySQL REGEXP演算子a laを使用することです。
WHERE foo LIKE '1\D*2\D*3\D*4\D*5'
特にきれいではありませんが、MySQLにはpreg_replace
関数なので、取得するのに最適です。
個人的には、この数値のみのデータが非常に重要である場合、除去されたデータを含めるためだけに別のフィールドを保持します。これにより、正規表現検索よりもはるかに高速に検索を行うことができます。
私も同様の状況で、製品をバーコードと照合し、バーコードが英数字をまったく保存しないことがあるので、1022234を検索するときにDB内の102.2234を見つける必要があります。
最後に、製品フィールドにreference_numberという新しいフィールドを追加しました。新しい製品が追加されるたびに、phpでproduct_numberの英数字を削除してreference_numberに入力します。
既存の製品のすべてのreference_numberフィールドを作成するには、テーブルを1回スキャンする必要があります。
その後、インデックスを設定できます。速度がこの操作の要素ではない場合でも、データベースを適切に実行しておくと、このクエリによってパフォーマンスが低下したり、他のクエリの速度が低下したりすることがなくなります。
私はこの解決策に出くわしました。 user1467716による上位の回答は、phpMyAdminで小さな変更を加えて機能します。コードの最後に2つ目の区切りタグを追加します。
phpMyAdminのバージョンは4.1.14です。 MySQLバージョン5.6.20
を使用して長さリミッターも追加しました
DECLARE count INT DEFAULT 0;
宣言内
AND count < 5
WHILE
ステートメント
SET COUNT=COUNT+1;
IF
ステートメント
最終フォーム:
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
私に関する限り、正規表現に代わるものはありませんが、この解決策を見つけました。
--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;
それは私のために働き、私はMySQL 5.0を使用します
また、私はこれを見つけることができます 場所 それは役立つかもしれません。
Fooを含むテーブルの大きさは?それが小さく、速度が本当に重要でない場合は、行IDとfooをプルし、PHP replace関数を使用してループしてループし、必要な情報をプルすることができます。行番号。
もちろん、テーブルが大きすぎる場合、これはうまく機能しません。
文字列内の特定の数値パターンに一致する数値を検索するには、まず以下のような方法ですべてのアルファベットと特殊文字を削除してから、値を整数に変換してから検索します
SELECT *
FROM foo
WHERE Convert(Regexp_replace(bar, '[a-zA-Z]+', ''), signed) = 12345
MySQL 8.0+では、REGEXP_REPLACEと呼ばれる新しいネイティブ関数があります。この質問に対する明確な解決策は次のとおりです。
SELECT * FROM foo WHERE REGEXP_REPLACE(bar,'[^0-9]+',"") = '12345'
ここから機能を作り上げたので、これを共有しようと思いました。読みやすくするために並べ替えました(サーバー側です)。
これを呼び出すには、テーブル名と列名を渡して、その列から既存のすべての非数値文字を削除します。大量のintフィールドを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 ;
この例を試してください。これは電話番号に使用されますが、必要に応じて変更できます。
-- 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
このユーザー定義関数(UDF)を使用します。電話番号の列があるとしましょう:
col1
(513)983-3983
1-838-338-9898
phone983-889-8383
select remove_alpha(col1) from mytable
結果は次のようになります。
5139833983
18383389898
9838898383