2010-05-30 15 views
5

Ho una tabella legacy mysql che contiene un identificativo client e un elenco di elementi, quest'ultimo come stringa delimitata da virgole. Per esempio. "xyz001", "foo,bar,baz". Si tratta di materiale legacy e l'utente insiste per essere in grado di modificare una stringa delimitata da virgole.Conversione di stringhe delimitate su più valori in mysql

Ora hanno un requisito per una tabella di rapporto con il suddetto suddiviso in righe separate, ad es.

"xyz001", "foo" 
"xyz001", "bar" 
"xyz001", "baz" 

Rompere la stringa in sottostringhe è facilmente fattibile e ho scritto una procedura per fare questo attraverso la creazione di un tavolo separato, ma questo richiede trigger a che fare con eliminazioni, gli aggiornamenti e gli inserti. Questa query è richiesta raramente (diciamo una volta al mese) ma deve essere assolutamente aggiornata quando viene eseguita, quindi ad es. il sovraccarico dei trigger non è garantito e le attività pianificate per creare la tabella potrebbero non essere abbastanza tempestive.

C'è un modo per scrivere una funzione per restituire una tabella o un set in modo che possa unire l'identificatore ai singoli elementi su richiesta?

risposta

3

Questo è chiamato walking a string. Ecco un esempio di come potresti farlo con le specifiche fornite:

Avrai bisogno di creare una tabella che contenga un numero di interi pari alla lunghezza del campo + 1. Quindi se la lunghezza del campo è 255, dovrai bisogno di 256 record che contengono solo un numero singolo da 0-255.

int_table:

+---+ 
| i | 
+---+ 
| 0 | 
| 1 | 
| 2 | 
| 3 | 
| 4 | 
| 5 | 
| 6 | 
+---+ 

Successivamente, si avrà bisogno di una query che unisce su questo tavolo e controlla se una virgola esiste in quella posizione o no. (Ho chiamato il tuo tavolo legacy_table con i campi client e items, rispettivamente.)

select 
    legacy_table.client, 
    substring(
    legacy_table.items, 
    int_table.i + 1, 
    if(
     locate(',', legacy_table.items, int_table.i + 1) = 0, 
     length(legacy_table.items) + 1, 
     locate(',', legacy_table.items, int_table.i + 1) 
    ) - (int_table.i + 1) 
) as item 
from legacy_table, int_table 
where legacy_table.client = 'xyz001' 
    and int_table.i < length(legacy_table.items) 
    and (
    (int_table.i = 0) 
    or (substring(legacy_table.items, int_table.i, 1) = ',') 
) 

potrebbe non essere abbastanza efficace per utilizzare effettivamente, ma ho pensato di presentarlo come un esempio solo così si sapere cosa è disponibile

+0

Questo funziona a meraviglia, non si può dire che comprendo pienamente, ma non mancherò di studiare fino a quando lo faccio. Grazie mille. – epo

+0

Attenzione: il codice sopra riportato contiene un bug. Non restituisce il primo elemento nell'elenco (ad esempio "pippo"). Il codice qui sotto di Thomas non contiene questo bug. – GregW

0

Interessante.

Non riesco a pensare ad una soluzione pulita e ordinata, ma potrei offrire alcune idee.

Credo che si potrebbe combinare unione con il seguente:

http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

Se si conosce il numero massimo di elementi in una riga, allora si potrebbe fare un sindacato sui risultati .. qualcosa di simile:

(SELECT col1, SPLIT_STR(col2, ',', 1) c2 from tbl) 
UNION (SELECT col1, SPLIT_STR(col2, ',', 2) c2 from tbl where col2 like '%,%') 
UNION (SELECT col1, SPLIT_STR(col2, ',', 3) c2 from tbl where col2 like '%,%,%') 
... 

Non sono molto in vena di stored procedure mysql, ma forse si potrebbe fare qualcosa di intelligente con i loop per renderlo più dinamico.

Spero che questo ti punti nella giusta direzione -?

2

Si può fare questo con un numeri o tavolo Tally che contiene un elenco sequenziale di numeri interi:

Select Substring(T.List, N.Value, Locate(', ', T.List + ', ', N.Value) - N.Value) 
From Numbers As N 
    Cross Join MyTable As T 
Where N.Value <= Len(T.List) 
    And Substring(', ' + T.List, N.Value, 1) = ', ' 

Nel caso di cui sopra, la mia tabella Numbers è strutturato in questo modo:

Create Table Numbers(Value int not null primary key) 
+0

Affinché il codice precedente funzioni in MySQL per i tag associati a questa domanda, è necessario utilizzare la funzione CONCAT di MySQL (A, B) piuttosto che l'approccio A + B che utilizza l'operatore + di Microsoft SQL Server per concatenare le stringhe. – GregW

0

I volevo davvero farlo funzionare usando SQL ma non era in grado di farlo. Ho scritto questo script PHP veloce per fare il lavoro.

È veloce e sporca e NON come vorrei mai consigliare codice di produzione. Ma a volte fai quello che serve per fare il lavoro velocemente.

<?php 
$table = "ref_app"; 
$pri_column = "Repo"; 
$column = "Topics"; 
$newTable = "topics"; 

$conn = mysql_connect("localhost", "dev", "password"); 
mysql_select_db("esb_data"); 
if($conn==null){ 
    echo "Connection not made"; 
    exit; 
} 

$result = mysql_query("select ".$pri_column.", ".$column." from ".$table); 
if(mysql_errno()){ 
    echo "<br>".mysql_error(); 
    exit; 
} 

$applications = array(); 
while($row = mysql_fetch_array($result)){ 
    echo "<br>".$row[$pri_column]; 
    $topics = explode(",", $row[$column]); 
    foreach($topics as $topic){ 
     $topic = trim($topic); 
     $applications[$row[$pri_column]][$topic] = $topic; 
    } 
    echo "<br>".$row[$column]; 
} 
echo "<pre>"; 
print_r($applications); 
echo "</pre>"; 

foreach($applications as $app => $topics){ 
    foreach($topics as $topic){ 
     $query = "insert into ".$newTable." values ('', \"".$app."\", \"".$topic."\")"; 
     echo "<br>".$query; 
     mysql_query($query); 
     if(mysql_errno()){ 
      echo "<br>".mysql_error(); 
     } 
    } 
} 
?> 
Problemi correlati