2011-06-23 14 views
5

Mam tabelę z milionami wierszy i pojedynczą kolumną tekstu, która ma dokładnie 11 159 znaków. Wygląda to tak:Jak wybrać nieciągłe znaki z ciągu tekstowego w MySQL?

1202012101...(to 11,159 characters) 
1202020120... 
0121210212... 
... 
(to millions of rows) 

Zdaję sobie sprawę, że mogę używać

SELECT SUBSTR(column,2,4) FROM table; 

... gdybym chciał wyciągnąć znaków 2, 3, 4 i 5:

1202012101... 
1202020120... 
0121210212... 
^^^^ 

Ale muszę wyodrębnić nieciągłe znaki, np znaków 1,5,7:

1202012101... 
1202020120... 
0121210212... 
^ ^^ 

Zdaję sobie sprawę, można to zrobić z kwerendy jak:

SELECT CONCAT(SUBSTR(colm,1,1),SUBSTR(colm,5,1),SUBSTR(colm,7,1)) FROM table; 

Ale ta kwerenda pobiera bardzo nieporęczny zbudować tysięcy znaków, które muszę wybrać. Więc do pierwszej części pytania - w jaki sposób zbudować kwerendę, która robi coś takiego:

SELECT CHARACTERS(string,1,5,7) FROM table; 

Ponadto indeksy znaków Chcę wybrać są z innej tabeli, która wygląda mniej tak:

char_index keep_or_discard 
1   keep 
2   discard 
3   discard 
4   discard 
5   keep 
7   discard 
8   keep 
9   discard 
10   discard 

więc do drugiej części pytania, w jaki sposób zbudować kwerendę, aby wybrać konkretne postacie z pierwszej tabeli na podstawie czy keep_or_discard = „keep” dla indeksu bohatera w drugiej tabeli?

+0

Można rozważyć wykonanie tego w procedurze przechowywanej. Nigdy nie napisałem SP dla MySQL, ale w Postgresu użyłbym do tego PL/Perl, ponieważ Perl jest świetny w tego rodzaju manipulacji tekstem. – Flimzy

+0

Wyciągnąłbym też cały ciąg i sparsowałbym go czymś podobnym do Perla, ale stół ma rozmiar około 100 GB (miliony rzędów), a potrzebuję tylko małej części tego 11.159-znakowego długiego łańcucha. –

+0

Aby wyjaśnić, ta tabela ma 11159 * wierszy * lub tabela ma kolumnę zawierającą 11159 * znaków * w każdym wierszu? – JYelton

Odpowiedz

0

Można napisać skrypt php, aby to zrobić dla Ciebie:

<?php 
    //mysql connect 
    $conn = mysql_connect('localhost', 'mysql_user', 'mysql_password'); 

    if (!$conn) { 
     echo 'Unable to connect to DB: ' . mysql_error(); 
     exit; 
    } 

    //database connect 
    $db = mysql_select_db('mydb'); 

    if (!$db) { 
     echo 'Unable to select mydb: ' . mysql_error(); 
     exit; 
    } 

    //get the keep numbers you’re going to use. 
    //and change the number into string so, for example, instead of 5 you get 'SUBSTR(colm,5,1)' 
    $result = mysql_query("SELECT number FROM number_table WHERE keep_or_discard='keep'"); 
    $numbers = array(); 
    while ($row = mysql_fetch_assoc($result)) { 
     $row = 'SUBSTR(colm,' . $row . ',1)'; 
     $numbers = $row; 
    } 

    //implode the array so you get one long string with all the substrings 
    //eg. 'SUBSTR(colm,1,1),SUBSTR(colm,5,1),SUBSTR(colm,12,1)' 
    $numbers = implode(",", $numbers); 

    //pull the numbers you need and save them to an array. 
    $result = mysql_query("SELECT " . $numbers . " FROM table"); 
    $concat = array(); 
    while ($row = mysql_fetch_assoc($result)) { 
     $concat= $row; 
    } 

I nie masz tablicę z prawidłowymi numerami.

Przykro mi, jeśli nie możesz/nie chcesz używać PHP do tego, po prostu nie wiem jak to zrobić bez PHP, Perl, Python lub innych podobnych języków. Mam nadzieję, że to rozwiązanie pomoże jakoś ...

0

A co z dynamicznym sql? (Trzeba będzie zbudować wybierz część zapytania)

CREATE PROCEDURE example_procedure() 
BEGIN 
    -- 
    --build the concat values here  
    --  
    SET @ids := ''; 
    SET @S = 'SELECT @ids := built_concat_of_values FROM table'; 
    PREPARE n_StrSQL FROM @S; 
    EXECUTE n_StrSQL; 
    DEALLOCATE PREPARE n_StrSQL;  
END 
0

Źródłem swojej trudności jest to, że schemat nie odzwierciedla prawdziwych relacji między elementami danych. Jeśli chciał osiągnąć z „czystej” SQL, musisz schematu więcej tak:

table 
ID Index Char 
1  0  1 
1  1  2 
1  2  0 

charsToKeep 
ID Index Keep 
1  0  false 
1  1  true 
1  2  true 

Następnie można wykonać kwerendę tak:

SELECT Char FROM table t JOIN charsToKeep c ON t.ID = c.ID WHERE c.Keep = true 

Jednak prawdopodobnie masz powody do strukturyzacji danych w taki sposób, w jaki masz (mój schemat wymaga dużo więcej miejsca w pamięci na znak, a czas przetwarzania jest prawdopodobnie znacznie dłuższy od tego, co zamierzam zasugerować).

Ponieważ SQL nie ma narzędzi do zrozumienia schematu, który został osadzony w tabeli, należy dodać je za pomocą funkcji zdefiniowanej przez użytkownika.Przykład dynamicznego SQL firmy Kevin może również zadziałać, ale z mojego doświadczenia wynika, że ​​nie jest tak szybki jak funkcja zdefiniowana przez użytkownika.

Zrobiłem to w MS SQL wiele razy, ale nigdy w MySql. Zasadniczo potrzebujesz funkcji napisanej w języku C lub C++, która pobiera rozdzielaną przecinkami listę indeksów, które chcesz wyodrębnić, oraz ciąg, z którego chcesz je wyodrębnić. Następnie funkcja zwróci rozdzieloną przecinkami listę wyodrębnionych wartości. Zobacz te linki za dobry punkt wyjścia:

http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html

http://dev.mysql.com/doc/refman/5.1/en/adding-udf.html

Aby zbudować łączone listę indeksów chcesz wyodrębnić z tabeli char_index, spróbuj funkcji GROUP_CONCAT:

http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

Mam nadzieję, że to pomoże!

1

ta funkcja robi to, co chcesz:

CREATE DEFINER = `root`@`localhost` FUNCTION `test`.`getsubset`(selection mediumtext, longstring mediumtext) 
RETURNS varchar(200) 
LANGUAGE SQL 
NOT DETERMINISTIC 
CONTAINS SQL 
SQL SECURITY DEFINER 
COMMENT 'This function returns a subset of characters.' 
BEGIN 
    SET @res:=''; 
    SET @selection:=selection; 
    WHILE @selection<>'' DO 
    set @pos:=CONVERT(@selection, signed); 
    set @res := concat_ws('',@res,SUBSTRING(longstring,@pos,1)); 
    IF LOCATE(',',@selection)=0 THEN 
     SET @selection:=''; 
    END IF; 
    set @selection:=SUBSTRING(@selection,LOCATE(',',@selection)+1); 
    END WHILE; 
    RETURN @res; 
END 

Uwaga: CONVERT ('1,2,3,4', podpisany) przyniesie 1, ale to daje ostrzeżenie.

Mam to zdefiniowane jako dostępne w teście bazy danych.

Funkcja przyjmuje dwa parametry; ciąg znaków (!) z listą pozycji i długim ciągiem, od którego mają zostać wykonane znaki.

Przykładem zastosowania tego:

mysql> select * from keepdiscard; 
+---------+------------+ 
| charind | keepordisc | 
+---------+------------+ 
|  1 | keep  | 
|  2 | discard | 
|  3 | keep  | 
|  4 | discard | 
|  5 | keep  | 
|  6 | keep  | 
+---------+------------+ 
6 rows in set (0.00 sec) 

mysql> select * from test; 
+-------------------+ 
| longstring  | 
+-------------------+ 
| abcdefghijklmnopq | 
| 123456789   | 
+-------------------+ 
2 rows in set (0.00 sec) 

mysql> select getsubset(group_concat(charind ORDER BY charind),longstring) as result from keepdiscard, test where keepordisc='keep' group by longstring; 
+--------+ 
| result | 
+--------+ 
| 1356 | 
| acef | 
+--------+ 
2 rows in set, 6 warnings (0.00 sec) 

Ostrzeżenia wynika z szybkiego przekształcenia do liczby całkowitej, która odbywa się w tej funkcji. (Patrz komentarz powyżej)