Question

J'ai créé un index de texte Oracle comme suit:

create index my_idx on my_table (text) indextype is ctxsys.context; 

Et je puis procédez comme suit:

select * from my_table where contains(text, '%blah%') > 0;

Mais disons que nous avons une autre colonne ont dans ce tableau, group_id de dire, et je voulais faire la requête suivante à la place:

select * from my_table where contains(text, '%blah%') > 0 and group_id = 43;

Avec l'index ci-dessus, Oracle devra rechercher tous les éléments qui contiennent 'blah', puis vérifier tous leurs group_ids.

Idéalement, je préférerais rechercher uniquement les articles avec group_id = 43, donc je veux un indice comme ceci:

create index my_idx on my_table (group_id, text) indextype is ctxsys.context; 

Un peu comme un index normal, donc une recherche de texte séparé peut être fait pour chaque group_id.

Est-il possible de faire quelque chose comme ça dans Oracle (j'utilise 10g si cela est important)?

Edit (clarification)

Soit une table avec un million de lignes et les deux colonnes suivantes, entre autres, A et B, à la fois numérique. Disons que il y a 500 valeurs différentes de A et 2000 différentes valeurs de B, et chaque ligne est unique.

permet maintenant considérer select ... where A = x and B = y

Un index sur A et B séparément pour autant que je peux dire faire une recherche d'index sur B, qui retournera 500 lignes différentes, puis effectuez une jointure / scan sur ces lignes. Dans tous les cas, au moins 500 lignes doivent être regardé (en dehors de la base de données étant chanceux et trouver au début de la ligne nécessaire.

considérant qu'un indice sur (A,B) est beaucoup plus efficace, il trouve une ligne dans une recherche d'index.

Mettre des index séparés sur group_id et le texte que je sens ne laisse que le générateur de requête avec deux options.

(1) Utilisez l'index de group_id et analyser toutes les lignes résultant pour le texte.
(2) Utilisez l'index de texte et analyser toutes les lignes résultantes pour la group_id.
(3) Utilisez les deux index et faire une jointure.

Alors que je veux:

(4) Utilisez l'index de (group_id, "text") pour trouver l'index de texte sous le group_id particulier et analyser cet index de texte pour la ligne / lignes particulières dont j'ai besoin. Pas de numérisation et de vérification ou de se joindre nécessaire, un peu comme lors de l'utilisation d'un index sur (A,B).

Était-ce utile?

La solution

Oracle Text

1 - Vous pouvez améliorer les performances en créant l'indice CONTEXTE avec TRIER PAR :

create index my_idx on my_table(text) indextype is ctxsys.context filter by group_id;

Dans mes tests, le filter by certainement amélioré la performance, mais il était encore un peu plus rapide d'utiliser simplement un indice btree sur group_id.

2 - index CTXCAT utilisent « sous-index », et semblent fonctionner semblable à un index à plusieurs colonnes. Cela semble être l'option (4), vous êtes à la recherche:

begin
  ctx_ddl.create_index_set('my_table_index_set');
  ctx_ddl.add_index('my_table_index_set', 'group_id');
end;
/

create index my_idx2 on my_table(text) indextype is ctxsys.ctxcat
    parameters('index set my_table_index_set');

select * from my_table where catsearch(text, 'blah', 'group_id = 43') > 0

Il est probable que l'approche la plus rapide. Utilisation de la requête ci-dessus contre 120MB de texte aléatoire similaire à votre scénario A et B requis seulement 18 cohérente obtient. Mais à la baisse, la création de l'indice de CTXCAT a pris près de 11 minutes et 1,8 Go d'espace utilisé.

(Note:. Oracle Text semble fonctionner correctement, mais je ne suis pas familier avec le texte et je ne peux pas gaurentee ce n'est pas une mauvaise utilisation de ces indices comme @NullUserException dit)

index multi-colonne vs indice rejoint

Pour la situation que vous décrivez dans votre édition, normalement il n'y aurait pas de différence significative entre l'utilisation d'un index sur (A, B) et se joindre à des index séparés sur A et B. J'ai construit des tests avec des données semblables à ce que vous avez décrit et un join index nécessaire seulement 7 cohérente obtient par rapport à 2 obtient pour l'uniforme index à plusieurs colonnes.

La raison est parce que Oracle récupère les données dans des blocs. Un bloc est généralement 8K, et un bloc d'index est déjà trié, vous pouvez probablement adapter les 500 à 2000 valeurs dans quelques blocs. Si vous êtes inquiet au sujet du rendement, généralement le IO à lire et à blocs d'écriture est la seule chose qui compte. Que ce soit ou non Oracle doit réunir quelques milliers de lignes est une quantité négligeable de temps CPU.

Toutefois, cela ne concerne pas les index Oracle Text. Vous pouvez joindre un indice CONTEXTE avec un indice de btree (un « bitmap et »?), Mais la performance est médiocre.

Autres conseils

Je mettrais un index sur group_id et voir si cela est assez bon. Vous ne dites pas combien de lignes dont nous parlons ou que la performance dont vous avez besoin.

Rappelez-vous, l'ordre dans lequel les prédicats sont traitées est pas nécessairement l'ordre dans lequel vous les avez écrit dans la requête. Ne pas essayer de déjouer l'optimiseur sauf si vous avez une vraie raison de.

Version courte: Il n'y a pas besoin de le faire. L'optimiseur de requêtes est assez intelligent pour décider quelle est la meilleure façon de sélectionner vos données. Il suffit de créer un indice de btree sur group_id, à savoir:

CREATE INDEX my_group_idx ON my_table (group_id); 

Version longue: J'ai créé un script ( testperf.sql ) qui insère 136 lignes de données factices.

DESC my_table;

Name     Null     Type      
-------- -------- --------- 
ID       NOT NULL NUMBER(4) 
GROUP_ID          NUMBER(4) 
TEXT              CLOB      

Il y a un indice de btree sur group_id. Pour assurer l'indice sera effectivement utilisé, exécutez en tant qu'utilisateur dba:

EXEC DBMS_STATS.GATHER_TABLE_STATS('<YOUR USER HERE>', 'MY_TABLE', cascade=>TRUE);

Voici le nombre de lignes chaque group_id a et le pourcentage correspondant:

GROUP_ID               COUNT                  PCT                    
---------------------- ---------------------- ---------------------- 
1                      1                      1                      
2                      2                      1                      
3                      4                      3                      
4                      8                      6                      
5                      16                     12                     
6                      32                     24                     
7                      64                     47                     
8                      9                      7         

Notez que l'optimiseur de requêtes utilisera un index que si elle pense que c'est une bonne idée - qui est, vous récupérez jusqu'à un certain pourcentage de lignes. Donc, si vous demandez un plan de requête sur:

SELECT * FROM my_table WHERE group_id = 1;
SELECT * FROM my_table WHERE group_id = 7;

Vous verrez que pour la première requête, il utilisera l'indice, alors que pour la deuxième requête, il effectuera une analyse complète de la table, car il y a trop de lignes pour l'indice efficace lorsqu'il group_id = 7.

Maintenant, considérons une condition différente -. WHERE group_id = Y AND text LIKE '%blah%' (puisque je ne suis pas très familier avec ctxsys.context)

SELECT * FROM my_table WHERE group_id = 1 AND text LIKE '%ipsum%';

En regardant le plan de requête, vous verrez que utiliser l'index sur group_id. Notez que l'ordre de vos conditions n'est pas important:

SELECT * FROM my_table WHERE text LIKE '%ipsum%' AND group_id = 1;

Génère le même plan de requête. Et si vous essayez d'exécuter la même requête sur group_id = 7, vous verrez que cela remonte à l'analyse complète de la table:

SELECT * FROM my_table WHERE group_id = 7 AND text LIKE '%ipsum%';

Notez que les statistiques sont collectées automatiquement par Oracle tous les jours (il est prévu de courir tous les soirs et le week-end), afin d'améliorer sans cesse l'efficacité de l'optimiseur de requêtes. Bref, Oracle fait de son mieux pour optimiser l'optimiseur, de sorte que vous n'avez pas.

Je n'ai pas une instance Oracle à portée de main pour tester, et ne l'ai pas utilisé l'indexation de texte intégral dans Oracle, mais j'ai généralement eu une bonne performance avec vues inline , ce qui pourrait être une alternative à la sorte d'index que vous aviez en tête. La legit syntaxe suivante lorsque contient () est impliqué?

Ce point de vue en ligne vous obtient les valeurs de PK des lignes dans le groupe 43:

             (
             select T.pkcol
             from T
             where group = 43
             )

Si le groupe a un indice normal et ne pas faible cardinalité, aller chercher cet ensemble devrait être rapide. Ensuite, vous jointure avec cet ensemble T à nouveau:

           select * from T
           inner join
            (
             select T.pkcol
             from T
             where group = 43
             ) as MyGroup

           on T.pkcol = MyGroup.pkcol
           where contains(text, '%blah%') > 0

Espérons que l'optimiseur serait en mesure d'utiliser l'index PK pour optimiser la jonction et Appy puis contient prédicat uniquement au groupe 43 lignes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top