web-dev-qa-db-fra.com

Différence de performance énorme lors de l'utilisation groupe par vs distinct

J'effectue des tests sur un serveur HSQLDB avec une table contenant 500 000 entrées. La table n'a pas d'index. Il existe 5000 clés commerciales distinctes. J'ai besoin d'une liste d'entre eux. Naturellement, j'ai commencé avec une requête DISTINCT:

SELECT DISTINCT business_key FROM memory WHERE
   concept <> 'case' or 
   attrib <> 'status' or 
   value <> 'closed'

Cela prend environ 90 secondes !!!

Puis j'ai essayé d'utiliser GROUP BY:

SELECT business_key FROM memory WHERE
       concept <> 'case' or 
       attrib <> 'status' or 
       value <> 'closed'
GROUP BY business_key

Et ça prend 1 seconde !!!

En essayant de comprendre la différence, j'ai couru EXLAIN PLAN FOR mais il semble donner les mêmes informations pour les deux requêtes.

EXLAIN PLAN FOR DISTINCT ...

isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

EXLAIN PLAN FOR SELECT ... GROUP BY ...

isDistinctSelect=[false]
isGrouped=[true]
isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
groupColumns=[
COLUMN: PUBLIC.MEMORY.BUSINESS_KEY]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

EDIT : J'ai fait des tests supplémentaires. Avec 500 000 enregistrements dans HSQLDB avec toutes les clés de gestion distinctes, les performances de DISTINCT sont désormais meilleures - 3 secondes, vs GROUP BY qui a pris environ 9 secondes.

Dans MySQL, les deux requêtes ont la même chose:

MySQL: 500 000 lignes - 5 000 clés commerciales distinctes: Les deux requêtes: 0,5 seconde MySQL: 500 000 lignes - toutes les clés commerciales distinctes: SELECT DISTINCT ... - 11 secondes SELECT ... GROUP BY business_key - 13 secondes

Donc, le problème est uniquement lié à HSQLDB.

Je serai très reconnaissant si quelqu'un peut expliquer pourquoi il y a une différence si radicale.

67
Martin Dimitrov

Les deux requêtes expriment la même question. Apparemment, l’optimiseur de requêtes choisit deux plans d’exécution différents. J'imagine que l'approche distinct est exécutée comme suit:

  • Tout copier business_key valeurs dans une table temporaire
  • Trier la table temporaire
  • Numérisez la table temporaire en renvoyant chaque élément différent de celui qui le précède

Le group by pourrait être exécuté comme:

  • Scannez la table complète en stockant chaque valeur de business key dans une table de hachage
  • Renvoie les clés de la hashtable

La première méthode optimise l'utilisation de la mémoire: elle fonctionnerait quand même assez bien lorsqu'une partie de la table temporaire doit être permutée. La deuxième méthode optimise la vitesse, mais nécessite potentiellement une grande quantité de mémoire s'il y a beaucoup de clés différentes.

Puisque vous avez suffisamment de mémoire ou peu de clés différentes, la seconde méthode est plus performante que la première. Il n’est pas rare de voir des écarts de performances de 10x voire 100x entre deux plans d’exécution.

66
Andomar