Jak mogę znaleźć znaki spoza ASCII w MySQL?

124

Pracuję z bazą danych MySQL, która zawiera dane zaimportowane z programu Excel . Dane zawierają znaki spoza zestawu ASCII (kreski em, itp.), A także ukryte znaki końca karetki lub nowe wiersze. Czy istnieje sposób na znalezienie tych rekordów za pomocą MySQL?

Ed Mays
źródło
8
Ollie Jones ma znacznie lepszą odpowiedź (sprawdź na dole).
Jonathan Arkell
1
@JonathanArkell Już nie na dole :)
Brilliand
Korekta… sprawdź środek! ;)
Jonathan Arkell
Oto odpowiedź @Jonathan mówi o stackoverflow.com/a/11741314/792066
Braiam

Odpowiedzi:

64

Zależy to dokładnie od tego, co definiujesz jako „ASCII”, ale sugerowałbym wypróbowanie wariantu zapytania takiego:

SELECT * FROM tableName WHERE columnToCheck NOT REGEXP '[A-Za-z0-9]';

To zapytanie zwróci wszystkie wiersze, w których columnToCheck zawiera znaki inne niż alfanumeryczne. Jeśli masz inne dopuszczalne znaki, dodaj je do klasy znaków w wyrażeniu regularnym. Na przykład, jeśli kropki, przecinki i łączniki są w porządku, zmień zapytanie na:

SELECT * FROM tableName WHERE columnToCheck NOT REGEXP '[A-Za-z0-9.,-]';

Najbardziej odpowiednią stroną dokumentacji MySQL jest prawdopodobnie 12.5.2 Wyrażenia regularne .

Chad Birch
źródło
3
Nie powinieneś uciec od łącznika i kropki? (Ponieważ mają one specjalne znaczenie w wyrażeniach regularnych.) SELECT * FROM tableName WHERE NOT columnToCheck REGEXP '[A-Za-z0-9 \., \ -]';
Tooony
3
@Tooony Nie, w zestawie kropka oznacza samą siebie, a myślnik ma specjalne znaczenie tylko między innymi znakami. Na końcu zestawu oznacza tylko siebie.
Michael Speer
10
To zapytanie znajduje tylko wszystkie wiersze w tableName, które nie zawierają znaku alfanumerycznego. To nie odpowiada na pytanie.
Rob Bailey
8
Dotyczy to kolumn, które w ogóle nie mają żadnych znaków ascii, więc pominie te z mieszanką znaków ascii i innych niż ascii. Poniższa odpowiedź z zende sprawdza, czy nie ma jednego lub więcej znaków spoza zestawu ASCII. Pomogło mi to w większościSELECT * FROM tbl WHERE colname NOT REGEXP '^[A-Za-z0-9\.,@&\(\) \-]*$';
Frank Forte
1
Działa to (w każdym razie dla mnie) w celu znalezienia ciągów, które nie zawierają ŻADNEGO z tych znaków. Nie znajduje ciągów zawierających mieszankę znaków ASCII i innych niż ASCII.
Ian,
236

MySQL zapewnia kompleksowe zarządzanie zestawem znaków, które może pomóc w tego rodzaju problemach.

SELECT whatever
  FROM tableName 
 WHERE columnToCheck <> CONVERT(columnToCheck USING ASCII)

CONVERT(col USING charset)Funkcja zamienia unconvertable znaki do znaków zastępczych. Wtedy przekonwertowany i nieprzekonwertowany tekst będzie nierówny.

Zobacz to, aby uzyskać więcej dyskusji. https://dev.mysql.com/doc/refman/8.0/en/charset-repertoire.html

Możesz użyć dowolnej nazwy zestawu znaków zamiast ASCII. Na przykład, jeśli chcesz dowiedzieć się, które znaki nie będą renderowane poprawnie na stronie kodowej 1257 (litewski, łotewski, estoński), użyjCONVERT(columnToCheck USING cp1257)

O. Jones
źródło
20
To doskonałe rozwiązanie tego problemu i dużo solidniejsze.
CraigDouglas
5
jest to również przydatne do znajdowania znaków z akcentami (á ä itp.) lub znaków nienależących do kodowania
Glasnhost
3
znacznie lepsze niż używanie REGEXP (który wydaje się nie działać dla mnie do znajdowania akcentów), a także zapewnia prosty mechanizm do ponownego tworzenia ascii ...
Dirk Conrad Coetsee
1
Ta odpowiedź działa cudownie i wyświetli ciągi zawierające znaki spoza zestawu ASCII, a nie tylko łańcuchy zawierające tylko znaki spoza zestawu ASCII. Dziękuję Ci!
Ian,
2
Rewelacyjne rozwiązanie!
Mad Dog Tannen
93

Możesz zdefiniować ASCII jako wszystkie znaki, które mają wartość dziesiętną od 0 do 127 (0x00 - 0x7F) i znaleźć kolumny ze znakami spoza zestawu ASCII za pomocą następującego zapytania

SELECT * FROM TABLE WHERE NOT HEX(COLUMN) REGEXP '^([0-7][0-9A-F])*$';

To było najbardziej wszechstronne zapytanie, jakie mogłem wymyślić.

zende
źródło
3
Jak dotąd najlepsza odpowiedź, ale jest jeszcze łatwiej:SELECT * FROM table WHERE LENGTH( column ) != CHAR_LENGTH( column )
SuN
15
-1 Może to spowodować błędne wyniki. Załóżmy na przykład, że mamy kolumnę UTF-16 zawierającą 'ā'(zakodowaną przez sekwencję bajtów 0x0101) - przy użyciu tego testu zostanie to uznane za „ASCII”: fałszywie ujemny ; Rzeczywiście, niektóre zestawy znaków nie kodują ciągu znaków ASCII 0x00, aby 0x7fczym to rozwiązanie przyniesie fałszywy alarm. NIE POLEGAJ NA TEJ ODPOWIEDZI!
eggyal
2
@sun: To wcale nie pomaga - wiele zestawów znaków ma stałą długość, a więc LENGTH(column)będzie to stała wielokrotność CHAR_LENGTH(column)niezależnie od wartości.
eggyal
49

To jest prawdopodobnie to, czego szukasz:

select * from TABLE where COLUMN regexp '[^ -~]';

Powinien zwrócić wszystkie wiersze, w których KOLUMNA zawiera znaki inne niż ASCII (lub niedrukowalne znaki ASCII, takie jak nowa linia).

Peter Mortensen
źródło
7
U mnie działa świetnie. „regexp '[^ - ~]'” oznacza, że ​​ma znak znajdujący się przed spacją „” lub po „~” lub ASCII 32–126. Wszystkie litery, cyfry i symbole, ale żadnych niedrukowalnych elementów.
Josh
Możesz nawet dostać jako koszulkę;) catonmat.net/blog/my-favorite-regex
SamGoody
1
Należy zwrócić uwagę na ostrzeżenia w dokumentacji : " i . Operatorzy pracują w bajtów mądry sposób, więc nie są one multi-byte bezpieczne i może spowodować nieoczekiwane rezultaty z zestawów znaków wielobajtowych Ponadto te podmioty gildie przez ich wartości bajtowych i znaki akcentowane mogą nie być porównywane jako równe, nawet jeśli dane zestawienie traktuje je jako równe.REGEXPRLIKE
eggyal
1
dzięki za to. zastanawiam się, jak zamienić znak zastępczy - np. â
mars-o
1
@ mars-o - czarny romb wskazuje nieprawidłowy znak utf8. Więcej dyskusji tutaj
Rick James
14

Jednym brakującym znakiem z wszystkich powyższych przykładów jest znak zakończenia (\ 0). Jest to niewidoczne dla danych wyjściowych konsoli MySQL i nie jest wykrywalne przez żadne z wyżej wymienionych zapytań. Zapytanie, aby go znaleźć, jest proste:

select * from TABLE where COLUMN like '%\0%';
Rob Bailey
źródło
4

Opierając się na poprawnej odpowiedzi, ale biorąc pod uwagę również znaki sterujące ASCII, rozwiązanie, które zadziałało dla mnie to:

SELECT * FROM `table` WHERE NOT `field` REGEXP  "[\\x00-\\xFF]|^$";

Robi to samo: wyszukuje naruszenia zakresu ASCII w kolumnie, ale umożliwia również wyszukiwanie znaków sterujących, ponieważ używa notacji szesnastkowej dla punktów kodowych. Ponieważ nie ma porównania ani konwersji (w przeciwieństwie do odpowiedzi @ Ollie), to również powinno być znacznie szybsze. (Zwłaszcza jeśli MySQL wykonuje wczesne zakończenie zapytania regex, co zdecydowanie powinno.)

Unika również zwracania pól o zerowej długości. Jeśli potrzebujesz nieco dłuższej wersji, która może działać lepiej, możesz użyć tego:

SELECT * FROM `table` WHERE `field` <> "" AND NOT `field` REGEXP  "[\\x00-\\xFF]";

Wykonuje oddzielne sprawdzenie długości, aby uniknąć wyników o zerowej długości, bez uwzględniania ich jako przebiegu wyrażenia regularnego. W zależności od liczby posiadanych wpisów o zerowej długości może to być znacznie szybsze.

Zauważ, że jeśli twój domyślny zestaw znaków jest czymś dziwnym, gdzie 0x00-0xFF nie jest mapowane na te same wartości co ASCII (czy istnieje taki zestaw znaków gdziekolwiek istnieje?), Zwróci to fałszywie dodatni wynik. W przeciwnym razie baw się dobrze!

Mahmoud Al-Qudsi
źródło
1
00-FF zawiera wszystkie możliwe wartości 8-bitowe, co REGEXPjest sprawdzane. W związku z tym gwarantuje, że zawsze będzie pasować. Również ^$nie jest chyba to, czego chciał.
Rick James,
Zdecydowanie najlepsze rozwiązanie REGEXP do znajdowania wszystkich 8-bitowych znaków, ale nie tak dobre, jak rozwiązanie CONVERT (col USING charset), które również umożliwia sterowanie znakami, jednocześnie ograniczając wyświetlane znaki do określonego zestawu znaków.
Ian
1

Spróbuj użyć tego zapytania do wyszukiwania rekordów znaków specjalnych

SELECT *
FROM tableName
WHERE fieldName REGEXP '[^a-zA-Z0-9@:. \'\-`,\&]'
Sachin
źródło
0

Odpowiedź @ zende była jedyną, która obejmowała kolumny mieszaniną znaków ascii i non-ascii, ale zawierała również problematyczne szesnastkowe. Użyłem tego:

SELECT * FROM `table` WHERE NOT `column` REGEXP '^[ -~]+$' AND `column` !=''
chiliNUT
źródło
0

W Oracle możemy użyć poniżej.

SELECT * FROM TABLE_A WHERE ASCIISTR(COLUMN_A) <> COLUMN_A;
Malaka Gunawardhana
źródło
-2

do tego pytania możemy również użyć tej metody:

Pytanie od sql zoo:
Znajdź wszystkie szczegóły nagrody zdobytej przez PETERA GRÜNBERGA

Znaki spoza ASCII

ans: wybierz * z nobla, gdzie zwycięzca jak „P% GR% _% berg”;

hemu123
źródło
1
Gdzie jest związek z pytaniem?
Nico Haase,