2011-06-23 6 views
5

Tengo una tabla con millones de filas y una sola columna de texto que tiene exactamente 11,159 caracteres. Se ve así:¿Cómo selecciono caracteres no contiguos de una cadena de texto en MySQL?

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

Soy consciente de que puedo usar

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

... si quería sacar caracteres 2, 3, 4 y 5:

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

Pero necesito extraer caracteres no contiguos, por ejemplo 1,5,7 caracteres:

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

Soy consciente de que se pueden hacer con una consulta como:

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

Pero esta consulta se pone muy difícil de manejar para construir miles de caracteres que tengo que seleccionar. Así que para la primera parte de la pregunta - ¿Cómo construir una consulta que hace algo como esto:

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

Por otra parte, los índices de los caracteres que desea seleccionar son de una tabla diferente que se ve algo de esta manera:

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

Así que para la segunda parte de la pregunta, ¿cómo podría crear una consulta para seleccionar los caracteres específicos de la primera tabla en función de si keep_or_discard = "mantener" para el índice de ese personaje en la segunda tabla?

+0

Puede considerar hacerlo en un procedimiento almacenado. Nunca escribí un SP para MySQL, pero en Postgres, usaría PL/Perl para eso, ya que Perl es excelente en ese tipo de manipulación de texto. – Flimzy

+0

Además, extraía toda la cadena y la analizaba con algo así como Perl, pero la tabla es algo así como 100 GB (millones de filas), y solo necesito una pequeña parte de esa cadena de 11.159 caracteres de longitud. –

+0

Solo para aclarar, esta tabla tiene 11159 * filas * o la tabla tiene una columna con 11159 * caracteres * en cada fila? – JYelton

Respuesta

0

Puede escribir un script php para hacer esto para usted:

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

Y allí se tiene una matriz con los números correctos.

Disculpa si no puedes/no quieres usar PHP para esto, simplemente no sé cómo hacerlo sin PHP, Perl, Python o algún otro lenguaje similar. Esperemos que esta solución ayude de alguna manera ...

0

¿Qué hay de dinámico sql? (Usted necesita para construir la parte SELECT de la consulta)

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

El origen de su dificultad es que su esquema no representa las verdaderas relaciones entre los elementos de datos. Si se quería lograr esto con SQL "puro", se necesitaría un esquema más como:

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 

A continuación, puede realizar una consulta como:

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

Sin embargo, es probable que tenga buenas razones para estructurar sus datos de la manera que tiene (mi esquema requiere mucho más espacio de almacenamiento por carácter y el tiempo de procesamiento también es probablemente mucho más largo de lo que estoy a punto de sugerir).

Dado que SQL no tiene las herramientas para comprender el esquema que ha incrustado en su tabla, deberá agregarlas con una función definida por el usuario.El ejemplo de Kevin de SQL dinámico también puede funcionar, pero en mi experiencia esto no es tan rápido como una función definida por el usuario.

he hecho esto en MS SQL muchas veces, pero nunca en MySql. Básicamente necesita una función, escrita en C o C++, que tome una lista delimitada por comas de los índices que desea extraer, y la cadena de la que desea extraerlos. Luego, la función devolverá una lista delimitada por comas de esos valores extraídos. Ver estos enlaces para un buen punto de partida:

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

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

para crear la lista concatenada de índices que desea extraer de la tabla char_index, pruebe la función GROUP_CONCAT:

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

Espero que esto ayude!

1

esta función hace lo que quiere:

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 

Nota: la CONVERT ('1,2,3,4', firmado) producirán 1, pero le dará una advertencia.

Lo tengo definido para estar disponible en la prueba de la base de datos.

La función toma dos parámetros; una cadena (!) con una lista de posiciones y una cadena larga desde donde desea que se tomen los caracteres.

Un ejemplo del uso de este:

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) 

Las advertencias se derivan de la conversión rápida a un entero que se hace en la función. (Consulte el comentario anterior)

Cuestiones relacionadas