Question

Je me demande quelle serait la performance d'une requête en utilisant le mot-clé LIKE et le caractère générique comme valeur par rapport à l'absence de clause where.

Envisagez une clause where telle que "WHERE a LIKE '%'". Cela correspondra à toutes les valeurs possibles de la colonne 'a'. Comment cela se compare-t-il au fait de ne pas avoir la clause where du tout?

La raison pour laquelle je pose cette question est que j’ai une application dans laquelle certains champs peuvent être spécifiés par l’utilisateur. Dans certains cas, l'utilisateur souhaite obtenir tous les résultats possibles. J'utilise actuellement une seule requête comme celle-ci:

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Les valeurs de '%' et '%' peuvent être fournies pour correspondre à toutes les valeurs possibles pour a et ou b. C'est pratique car je peux utiliser une seule requête nommée dans mon application pour cela. Je me demande quelles sont les considérations de performance pour cela. Est-ce que l'optimiseur de requête réduit LIKE '%' pour qu'il corresponde simplement à tous? Je réalise que, parce que j'utilise une requête nommée (instruction préparée), cela peut également affecter la réponse. Je me rends compte que la réponse est probablement spécifique à la base de données. Ainsi, en particulier, comment cela fonctionnerait-il avec Oracle, MS SQL Server et Derby?

L’approche alternative consiste à utiliser 3 requêtes distinctes en fonction du choix du caractère générique par l’utilisateur.

A est une requête générique:

SELECT * FROM TableName WHERE b LIKE ?

B est une requête générique:

SELECT * FROM TableName WHERE a LIKE ?

A et B sont des caractères génériques:

SELECT * FROM TableName

Aucun caractère générique:

<*>

Évidemment, avoir une seule requête est la plus simple et la plus facile à gérer. Je préférerais utiliser la seule requête si les performances seront toujours bonnes.

Était-ce utile?

La solution 3

J'espérais qu'il y aurait une réponse de manuel à cela, mais il semble que cela variera largement avec différents types de bases de données. La plupart des réponses ont indiqué que je devrais faire un test, c’est exactement ce que j’ai fait.

Mon application cible principalement les bases de données Derby, MS SQL et Oracle. Puisque derby peut être exécuté de manière intégrée et est facile à configurer, j’ai d'abord testé les performances. Les résultats étaient surprenants. J'ai testé le pire des cas contre une table assez grande. J'ai effectué le test 1000 fois et mesuré les résultats en moyenne.

Requête 1:

SELECT * FROM TableName

Requête 2 (avec les valeurs de a = "%" et b = "%"):

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Durée moyenne de la requête 1: 178 ms

Temps moyen de la requête 2: 181 ms

Les performances en mode derby sont donc pratiquement identiques entre les deux requêtes.

Autres conseils

SQL Server verra généralement

WHERE City LIKE 'A%'

et traitez-le comme

WHERE City >= 'A' AND City < 'B'

... et utilisez avec bonheur une recherche d'index, le cas échéant. Je dis «en général», parce que je l’ai vu, échouer dans certains cas. »

Si quelqu'un essaie de faire:

WHERE City LIKE '%ville'

... alors une recherche d'index sera essentiellement impossible.

Mais quelque chose d'aussi simple que:

WHERE City LIKE '%'

sera considéré comme équivalent à:

WHERE City IS NOT NULL

Vous pouvez utiliser l'analyse de requête proposée par le SGBD (par exemple, EXPLAIN pour MySQL, SET SHOWPLAN_ALL ON pour MS SQL (ou utilisez l'un des autres méthodes ), EXPLAINER PLAN POUR pour Oracle) pour voir comment la requête sera exécutée.

Tout SGBD digne de ce nom éliminerait les clauses LIKE '%' avant même d'essayer d'exécuter la requête. Je suis à peu près certain que j'ai vu DB2 / z faire cela dans ses plans d'exécution.

L'instruction préparée ne devrait pas faire de différence, car elle doit être transformée en real SQL avant de parvenir au moteur d'exécution.

Mais, comme pour toutes les questions d'optimisation, mesurez, ne devinez pas ! Les administrateurs de base de données existent car ils adaptent constamment le SGBD en fonction des données réelles (qui évoluent dans le temps). Au minimum, vous devriez prévoir (et obtenir les plans d'exécution) toutes les variantes avec des données statiques appropriées pour voir s'il y a une différence.

Je sais que des requêtes telles que:

select c from t where ((1 = 1) or (c = ?))

sont optimisés pour supprimer l'intégralité de la clause where avant exécution (de toute façon sur DB2 et, avant de demander, la construction est utile lorsque vous devez supprimer l'effet de la clause where tout en conservant le paramètre espace réservé (utilisation de BIRT avec Javascript pour modifier les requêtes relatives aux caractères génériques)).

Derby propose également des outils permettant d’examiner le plan de requête réellement utilisé. Vous pouvez ainsi exécuter des expériences à l’aide de Derby et consulter le plan de requête choisi par Derby. Vous pouvez exécuter Derby avec -Dderby.language.logQueryPlan = true et Derby écrira le plan de requête dans derby.log. Vous pouvez également utiliser la fonction RUNTIMESTATISTICS, comme décrit ici: http://db.apache.org/derby/docs/10.5/tuning/ctundepth853133.html

Je ne sais pas si Derby supprimera le A LIKE '%' à l'avance, mais je ne pense pas non plus que la présence de cette clause entraînera un ralentissement important de la vitesse d'exécution.

Je serais très intéressé de voir la sortie du plan de requête que vous obtenez dans votre environnement, avec et sans la clause A LIKE '%' en place.

Oracle 10gR2 ne semble pas effectuer d’optimisation spéciale pour cette situation, mais il reconnaît que LIKE '%' exclut les valeurs NULL.

create table like_test (col1)
as select cast(dbms_random.string('U',10) as varchar2(10))
from dual
connect by level <= 1000
/
insert into like_test values (null)
/
commit
/

exec dbms_stats.gather_table_stats(user,'like_test')

explain plan for
select count(*)
from   like_test
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 like '%'
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 is not null
/
select plan_table_output from table(dbms_xplan.display)
/

... donnant ...

Plan hash value: 3733279756

------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |            |          |
|   2 |   TABLE ACCESS FULL| LIKE_TEST |  1001 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------

... et ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" LIKE '%')

... et ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" IS NOT NULL)

Notez la cardinalité (lignes) sur la ligne TABLE ACCESS FULL

En fonction de la structure du prédicat LIKE et du champ sur lequel vous testez, vous aurez peut-être besoin d'une analyse complète du tableau. Sémantiquement, un '%' peut impliquer une analyse complète de la table mais Sql Server effectue toutes sortes d'optimisations en interne sur les requêtes. La question devient alors: Sql Server optimise-t-il un prédicat LIKE formé avec '%' et le jette-t-il hors de la clause WHERE?

Un aspect qui, à mon avis, manque à la discussion est le fait que le PO souhaite utiliser une déclaration préparée. Au moment où la déclaration est préparée, la base de données / l'optimiseur ne sera pas en mesure de résoudre les simplifications mentionnées par d'autres et ne sera donc pas en mesure d'optimiser le comme '%' en tant que valeur réelle. la valeur ne sera pas connue au moment de la préparation.

Par conséquent:

  • lorsque vous utilisez des instructions préparées, disposez de quatre instructions différentes (0, uniquement a, uniquement b, les deux) et utilisez celle qui convient en cas de besoin
  • voyez si vous obtenez de meilleures performances lorsque vous n'utilisez pas une instruction préparée lorsque vous vous en tenez à une seule déclaration (bien qu'il serait alors assez facile de ne pas inclure les conditions "vides")

Que se passe-t-il si une colonne a une valeur vide non nulle? Votre requête va probablement correspondre.

S'il s'agit d'une requête pour une application du monde réel, essayez d'utiliser les fonctionnalités d'indexation de texte libre de la plupart des bases de données SQL modernes. Les problèmes de performance deviendront insignifiants.

Une simple déclaration de si si (A B)  rechercher un b sinon (A)  rechercher un sinon B  recherche b autre  dire à l'utilisateur qu'il n'a rien spécifié

est simple à maintenir et devient beaucoup plus facile à comprendre au lieu de faire des hypothèses sur l'opérateur LIKE. De toute façon, vous allez probablement le faire dans l’interface utilisateur lorsque vous affichez les résultats "Votre recherche de A trouvé x". ou "Votre recherche de A B trouvé ..."

Je ne suis pas sûr de l'intérêt d'utiliser une instruction préparée avec le type de paramètres que vous décrivez. La raison en est que vous pourriez tromper l'optimiseur de requêtes en préparant un plan d'exécution qui serait complètement faux en fonction du paramètre défini par '%'.

Par exemple, si l'instruction a été préparée avec un plan d'exécution utilisant l'index de la colonne A, mais que le paramètre de la colonne A s'est avéré être '%', les performances risquent d'être médiocres.

une clause where avec " comme '%' " car le seul prédicat se comportera exactement de la même manière que la clause no where.

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