2008-11-13 7 views
22

Ich suche nach Datensätzen in einer Tabelle, die eine bestimmte Zahl, die der Benutzer eingibt übereinstimmen. Also kann der Benutzer 12345 eingeben, aber dies könnte 123zz4-5 in der Datenbank sein.MySQL nicht-numerische Zeichen zum Vergleich

Ich stelle mir vor, etwas wie das würde funktionieren, wenn PHP-Funktionen in MySQL funktioniert.

SELECT * FROM foo WHERE preg_replace("/[^0-9]/","",bar) = '12345' 

Was ist die äquivalente Funktion oder Möglichkeit, dies nur mit MySQL zu tun?

Antwort

6

Während es nicht schön ist und es zeigt Ergebnisse, die nicht übereinstimmen, das hilft:

SELECT * FROM foo WHERE bar LIKE = '%1%2%3%4%5%' 

Ich würde immer noch eine bessere Lösung ähnlich das Element in der ursprünglichen Frage finden mag.

3

Der einfachste Weg, die ich denken kann zu tun, ist es, den MySQL REGEXP Operator a la zu verwenden:

WHERE foo LIKE '1\D*2\D*3\D*4\D*5' 

Es ist nicht besonders schön, aber MySQL hat keine preg_replace Funktion, so halte ich es für das Beste Sie werde es bekommen.

Persönlich, wenn diese nur numerischen Daten so wichtig sind, würde ich ein separates Feld behalten, nur um die abgestreiften Daten zu enthalten. Es macht Ihre Lookups sehr viel schneller als mit der regulären Ausdruckssuche.

+0

Geschwindigkeit ist nicht wichtig.Dies ist für ein Back-End-Tool, das nur verwendet wird, wenn ein Element in der Datenbank nicht anders gefunden werden kann. –

+1

Das funktioniert nicht in MySQL. –

+0

+1 für den Vorschlag, ein Feld hinzuzufügen, um eine normalisierte (d. H. Nur-Ziffern-) Version des Werts zu speichern. –

0

Es gibt keine Regex ersetzen, soweit es mich betrifft, aber ich habe diese Lösung gefunden;

--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; 

Es funktioniert für mich und ich verwende MySQL 5.0

Auch fand ich dieses place, die helfen könnte.

0

Wie groß ist Tabelle mit foo? Wenn es klein ist und Geschwindigkeit wirklich keine Rolle spielt, können Sie die Zeilen-ID und foo ziehen, mit den PHP-Replace-Funktionen zum Vergleichen überlappen und dann die gewünschten Informationen nach Zeilennummer ziehen.

Natürlich, wenn der Tisch zu groß ist, wird dies nicht gut funktionieren.

+0

Dies ist wahrscheinlich ungefähr die gleiche Geschwindigkeit wie in meinem Beispiel mit LIKE mit Wildcards zwischen jeder Zahl. –

7

Es gibt keine regexp ersetzen, nur eine einfache Zeichenfolge REPLACE().

MySQL hat den REGEXP Operator, aber es ist nur ein Spiel Tester kein Ersatzstoff, so würden Sie die Logik von innen nach außen drehen müssen:

SELECT * FROM foo WHERE bar REGEXP '[^0-9]*1[^0-9]*2[^0-9]*3[^0-9]*4[^0-9]*5[^0-9]*'; 

Diese wie Ihre Version mit LIKE ist aber passt genauer . Beides funktioniert genauso schlecht und erfordert einen vollständigen Tabellenscan ohne Indizes.

1

Ich habe eine ähnliche Situation, Produkte zu Barcodes passend, wo der Barcode nicht Numerik keiner alpha manchmal nicht speichert, so 102,2234 im DB gefunden werden muss, wenn für 1022234.

Am Ende der Suche ich gerade hinzugefügt ein neues Feld, reference_number, zu den productables-Tabellen, und php strip out die nicht alphanumerischen Zahlen in product_number, um reference_number zu füllen, wenn ein neues Produkt hinzugefügt wird.

Sie müssen einen einmaligen Scan der Tabelle durchführen, um alle reference_number-Felder für vorhandene Produkte zu erstellen.

Sie können dann Ihren Index einrichten, auch wenn die Geschwindigkeit für diese Operation keine Rolle spielt, ist es trotzdem eine gute Idee, die Datenbank gut laufen zu lassen, damit diese Abfrage nicht verlangsamt und andere Abfragen verlangsamt.

30

Mir ist klar, dass dies eine alte Thema, aber beim googeln dieses Problem konnte ich keine einfache Lösung finden (ich sah die ehrwürdigen Agenten, aber denke, dass dies eine einfachere Lösung ist), also hier ist eine Funktion, die ich schrieb, scheint zu arbeiten k ziemlich gut.

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 
$$ 
+0

Würde SUBSTRING() einmal schneller machen? – Stoutie

+0

nm, läuft viel schnell. 100.000+ Aufzeichnungen in einer Sekunde oder zwei. – Stoutie

+0

einfach toll, danke – Novasol

1

Ich bin auf diese Lösung gestoßen. Die oberste Antwort von user1467716 funktioniert in phpMyAdmin mit einer kleinen Änderung: Fügen Sie am Ende des Codes ein zweites Trennzeichen hinzu.

phpMyAdmin Version ist 4.1.14; MySQL Version 5.6.20

Ich habe auch eine Längenbegrenzung mit

DECLARE count INT DEFAULT 0; in den Erklärungen

AND count < 5 in der WHILE Anweisung

SET COUNT=COUNT+1; in der IF Anweisung

Endform:

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 
3

Die meist aktualisierte Antwort (@ user1467716) ist nicht die schnellste. Ein großes Lob an sie dafür, dass sie einen Arbeitsvorschlag zum Abprallen gegeben haben!

Dies ist eine verbesserte Version:

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;; 

Testing 5000-mal auf einem Testserver:

-- 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 
0

dieses Beispiel versuchen. Dies wird für Telefonnummern verwendet, Sie können sie jedoch für Ihre Bedürfnisse ändern.

-- 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 

diesen Benutzer definierte Funktion (UDF) zu verwenden. sagen wir mal Sie eine Spalte von Telefonnummern haben:

col1 
(513)983-3983 
1-838-338-9898 
phone983-889-8383 

select remove_alpha(col1) from mytable 

Das Ergebnis wäre;

5139833983 
18383389898 
9838898383