2009-01-29 8 views
2

J'ai une table avec environ 2 000 000 lignes. J'ai besoin d'interroger l'une des colonnes pour récupérer les lignes où une chaîne exsiste dans le cadre de la valeur.Vérification efficace de la présence de texte dans une colonne de texte

Lorsque j'exécuterai la requête, je connaîtrai la position de la chaîne, mais pas avant. Donc, une vue qui prend une sous-chaîne n'est pas une option.

Pour autant que je peux voir, je trois options

  1. en utilisant comme '%%'
  2. utilisant instr
  3. utilisant substr

J'ai la possibilité de créer un index basé sur la fonction, si je suis gentil avec le dba.

Pour le moment, toutes les requêtes prennent environ deux secondes. Quelqu'un at-il l'expérience de laquelle de ces options fonctionnera le mieux, ou s'il y a une autre option? La sélection sera utilisée pour les suppressions toutes les quelques secondes, elle sélectionnera généralement 10 lignes.

modifier avec un peu plus d'informations

Le problème est à peu près aussi nous utilisons une table pour stocker des objets avec des clés arbitraires et des valeurs. Les objets proviennent de l'extérieur de notre système, donc nous avons une portée limitée pour les contrôler, donc la colonne de texte est quelque chose comme 'key1 = abc, key2 = def, keyn = ghi' je sais que c'est horriblement dénormalisé mais comme nous ne savons pas les clés seront (dans une certaine mesure) c'est un moyen fiable pour stocker et récupérer des valeurs. Récupérer une ligne est assez rapide car nous recherchons l'ensemble de la colonne, qui est indexée. Mais la performance n'est pas bonne si on veut récupérer les lignes avec key2 = def.

Nous pourrions être en mesure de créer une table avec des colonnes pour les clés les plus courantes, mais je me demandais s'il y avait un moyen d'améliorer les performances avec la configuration existante.

+0

Veuillez publier quelques exemples de données et le résultat souhaité de la requête – Quassnoi

+0

Vous ne pouvez pas avoir les données dont vous avez besoin dans sa propre colonne? Le contenu de la colonne est censé être "atomique" – siukurnin

Répondre

2

Dans Oracle 10:

CREATE TABLE test (tst_test VARCHAR2(200)); 

CREATE INDEX ix_re_1 ON test(REGEXP_REPLACE(REGEXP_SUBSTR(tst_test, 'KEY1=[^,]*'), 'KEY1=([^,]*)', '\1')) 

SELECT * 
FROM TEST 
WHERE REGEXP_REPLACE(REGEXP_SUBSTR(TST_TEST, 'KEY1=[^,]*'), 'KEY1=([^,]*)', '\1') = 'TEST' 

Cela utilisera l'index nouvellement sélectionné.

Vous aurez besoin d'autant d'indices qu'il y a KEY s dans vos données.

Présence d'un INDEX, bien sûr, l'impact des performances, mais cela dépend très peu REGEXP être là:

SQL> CREATE INDEX ix_test ON test (tst_test) 
    2/
Index created 
Executed in 0,016 seconds 

SQL> INSERT 
    2 INTO test (tst_test) 
    3 SELECT 'KEY1=' || level || ';KEY2=' || (level + 10000) 
    4 FROM dual 
    5 CONNECT BY 
    6  LEVEL <= 1000000 
    7/
1000000 rows inserted 
Executed in 47,781 seconds 

SQL> TRUNCATE TABLE test 
    2/
Table truncated 
Executed in 2,546 seconds 

SQL> DROP INDEX ix_test 
    2/
Index dropped 
Executed in 0 seconds 

SQL> CREATE INDEX ix_re_1 ON test(REGEXP_REPLACE(REGEXP_SUBSTR(tst_test, 'KEY1=[^,]*'), 'KEY1=([^,]*)', '\1')) 
    2/
Index created 
Executed in 0,015 seconds 

SQL> INSERT 
     2 INTO test (tst_test) 
     3 SELECT 'KEY1=' || level || ';KEY2=' || (level + 10000) 
     4 FROM dual 
     5 CONNECT BY 
     6  LEVEL <= 1000000 
     7/
1000000 rows inserted 
Executed in 53,375 seconds 

Comme vous pouvez le voir, sur ma machine pas très rapide (Core2 4300, 1 Gb RAM) vous pouvez insérer 20000 enregistrements par seconde à un champ indexé, et ce taux ne dépend presque pas du type de INDEX utilisé: simple ou basé sur la fonction.

+0

J'aime ça, nous allons tester et voir si cela fonctionne pour nous. Merci –

+0

Cela fonctionne probablement pour les sélections, mais notre dba a souligné qu'il rendra les insertions beaucoup plus lent car il doit construire l'index. Merci pour une réponse très néet tout de même. –

1

Je suggère de reconsidérer votre logique. Au lieu de chercher où une chaîne existe, il peut être plus rapide de vérifier si elle a une longueur> 0 et n'est pas une chaîne.

Vous pouvez utiliser la fonction TRANSLATE dans oracle pour convertir tous les caractères non-string en valeurs NULL, puis vérifier si le résultat est null.

+0

D'où vient "seulement numérique"? Je ne vois rien dans la question qui pourrait le suggérer. –

1

Pouvez-vous fournir un peu plus d'informations?

Demandez-vous une sous-chaîne arbitraire d'une colonne de chaîne ou y a-t-il une syntaxe dans le magasin de chaînes dans les colonnes qui permettrait un prétraitement pour minimiser le travail répété?

Avez-vous déjà effectué des tests de synchronisation sur vos trois options pour déterminer leur performance relative sur les données que vous interrogez?

2

Vous pouvez utiliser Tom Kyte's runstats package pour comparer les performances de différentes implémentations - en cours d'exécution, disons 1000 fois dans une boucle. Par exemple, je viens de comparer LIKE avec SUBSTR et il a dit que LIKE était plus rapide, prenant environ 80% du temps de SUBSTR. Notez que "col LIKE '% xxx%'" est différent de "SUBSTR (col, 5,3) = 'xxx'".L'équivalent LIKE serait:

col LIKE '____xxx%' 

en utilisant un '_' pour chaque caractère principal à ignorer.

Je pense que de quelque façon que vous le ferez, les résultats seront similaires - il s'agit toujours d'un balayage de table complet (ou peut-être index complet). Un index basé sur une fonction ne serait utile que si vous connaissiez le décalage de la sous-chaîne au moment de la création de l'index.

Je suis plutôt inquiet quand vous dites que "Le select sera utilisé pour les suppressions toutes les quelques secondes". Cela suggère plutôt un défaut de conception quelque part, mais sans connaître les exigences, c'est difficile à dire.

MISE À JOUR:

Si vos valeurs de colonnes sont comme 'key1 = abc, key2 = def, keyn = ghi' alors vous pourriez peut-être envisager d'ajouter une autre table comme ceci:

create table key_values 
    (main_table_id references main_table 
    , key_value varchar2(50) 
    , primary key (fk_col, key_value) 
    ); 

create index key_values_idx on key_values (key_value); 

de Split les valeurs clés et de les stocker dans ce tableau comme ceci:

main_table_id key_value 
123   key1=abc 
123   key2=def 
123   key3=ghi 

(cela pourrait se faire dans un déclencheur AFTER INSERT sur main_table pour ex ample)

Ensuite, votre suppression pourrait être:

delete main_table 
where id in (select main_table_id from key_values 
      where key_value = 'key2=def'); 
0

Si vous êtes toujours à la recherche de la même sous-chaîne, l'utilisation de INSTR et d'un index basé sur les fonctions est logique pour moi. Vous pouvez également le faire si vous avez un petit ensemble de sous-chaînes constantes que vous rechercherez, en créant un FBI pour chacune d'entre elles.

L'idée REGEXP de Quassnoi semble également prometteuse. Je n'ai pas encore utilisé d'expressions régulières dans Oracle. Je pense que Oracle Text serait une autre façon de procéder. Info sur cela here

0

Je ne suis pas sûr d'améliorer les choses d'installation existantes, mais Lucene (bibliothèque de recherche en texte intégral, porté sur de nombreuses plateformes) peut vraiment aider. Il y a un fardeau supplémentaire de synchroniser l'index avec la base de données, mais si vous avez quelque chose qui ressemble à une couche de service dans un langage de programmation, cela devient une tâche facile.

1

Interposer la réponse pour commenter la conception de la table.

Tu ne peux pas avoir au moins une structure clé/valeur, donc au lieu de stocker dans une seule colonne, 'key1 = abc, key2 = def, keyn = ghi' vous auriez une table enfant comme

KEY  VALUE 
key1 abc 
key2 def 
key3 ghi 

Ensuite, vous pouvez créer un seul index sur la clé et la valeur et vos requêtes sont beaucoup plus simples (puisque je suppose que vous recherchez réellement une correspondance exacte sur la valeur d'une clé donnée). Certains diront probablement que c'est une conception horrible, mais je pense que c'est mieux que ce que vous avez maintenant.

+0

nous pouvons changer la table afin qu'elle ait des colonnes pour les clés les plus communes. Cependant, nous utilisons ceci pour stocker les valeurs d'un autre système, donc nous n'avons pas de liste définitive de clés. Nous cherchons à utiliser une vue matérialisée pour cela –

+0

Ce que je suggère encore permettre le stockage de noms de clés arbitraires. "key1", "key2", etc. seraient des valeurs dans une colonne, pas des colonnes en elles-mêmes. –

0

similaires à la réponse de Anton Gogolev, Oracle n'intègre un moteur de recherche de texte documenté here

Il y a aussi l'indexation extensible, de sorte que vous pouvez créer vos propres structures d'index, documenté here

Comme vous l'avez accepté, ce est une structure de données très pauvre, et je pense que vous aurez du mal à atteindre l'objectif de supprimer des éléments toutes les quelques secondes. Selon la façon dont ces données sont entrées, je chercherais à structurer correctement les données sur la charge, au moins dans la mesure où les lignes de "parent_id", "key_name", "key_value" sont présentes.

Questions connexes