2017-01-17 1 views
1

En regardant le various answers for ORDER BY with CASE like this one, je vois que ce que je suis obligé de faire dans cette application héritée est probablement une méthode experte; cependant, il est trop lent lorsque les lignes sont moins que triviales (les lignes de 100 000 ou plus provoquent des charges de page de 10 secondes).mysql ORDER BY avec CASE - trop lent, moyen plus rapide?

Veuillez noter que la requête d'origine cherche à résoudre un problème apparemment commun où l'analyste de requête a besoin de dates vides triées en fonction de la façon dont elles seraient normalement triées. Dans ce cas, datefirstprinted doit être descendant, mais tous les enregistrements qui ne sont pas imprimés doivent être remplis en haut de la liste.

La requête originale permet de résoudre, mais le point de la question est d'éviter la perte de performance filesort qui vient avec la colonne dérivée notprintedyet.

requête originale

SELECT SQL_NO_CACHE 
    id, daterun, datefirstprinted, 
    case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet 
FROM 
    patientrecords 
WHERE 
    dateuploaded <> '0000-00-00 00:00:00' 
ORDER BY 
    notprintedyet desc,         /* ordered via alias */ 
    datefirstprinted desc 
LIMIT 10; 

temps 1.52s


J'ai trouvé que pas le tri sur l'alias notprintedyet sauve un peu:

légèrement plus rapide Interrogation

SELECT SQL_NO_CACHE 
    id, daterun, datefirstprinted, 
    case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet 
FROM 
    patientrecords 
WHERE 
    dateuploaded <> '0000-00-00 00:00:00' 
ORDER BY 
    datefirstprinted = "0000-00-00 00:00:00" desc,  /* directly ordered */ 
    datefirstprinted 
LIMIT 10; 

temps 1.37s


vitesse optimale, mais il manque le tri requis de la date vide s premier

SELECT SQL_NO_CACHE 
    id, daterun, datefirstprinted, 
    case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet 
FROM 
    patientrecords 
WHERE 
    dateuploaded <> '0000-00-00 00:00:00' 
ORDER BY       
    datefirstprinted          /* not ordered properly */ 
LIMIT 10; 

temps 0.48s


J'ai essayé d'utiliser un view

create view notprinted_patientrecords as (
    SELECT id, daterun, datefirstprinted, case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end notprintedyet 
    FROM patientrecords 
    WHERE dateuploaded <> '0000-00-00 00:00:00' 
); 

malheureusement quand je cours explain

explain select * from notprinted_patientrecords order by notprintedyet desc limit 10; 

il montre que je suis encore en utilisant filesort et prend1.51saka aucune économie


Serait-il plus rapide si défaut datefirstprinted est NULL?

peut-être, mais dans cette application héritage qui pourrait faire plus de mal que les 5 secondes supplémentaires en page de chargement du temps


Que pourrions-nous essayer? Procédures stockées? Les fonctions?


MISES À JOUR

Comme suggéré @strawberry - ORDER BY CASE

... 
ORDER BY       
    case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end, datefirstprinted 
LIMIT 10; 

temps 1.52s


tel que demandé par @ e4c5, la sortie explain:

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: patientrecords 
     type: range 
possible_keys: dateuploaded,uploads_report 
      key: dateuploaded 
     key_len: 5 
      ref: NULL 
     rows: 299095 
     Extra: Using index condition; Using filesort 

sauf pour pas ordonné correctement qui a la variance suivante

 rows: 10 
     Extra: Using where 

create table

*************************** 1. row *************************** 
Table: patientrecords 
Create Table: CREATE TABLE `patientrecords` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `datecreated` datetime NOT NULL, 
    `dateuploaded` datetime NOT NULL, 
    `daterun` datetime NOT NULL, 
    `datebilled` datetime NOT NULL, 
    `datefirstprinted` datetime NOT NULL, 
    `datelastprinted` datetime NOT NULL, 
    `client` varchar(5) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `dateuploaded` (`dateuploaded`), 
    KEY `daterun` (`daterun`), 
    KEY `uploads_report` (`dateuploaded`,`client`), 
    KEY `datefirstprinted` (`datefirstprinted`), 
    KEY `datelastprinted` (`datelastprinted`) 
) 
+1

Vous essayez d'accélérer une requête qui prend une ou deux millisecondes? Ça n'a pas de sens. –

+0

@gordonlinoff - je ne sais pas pourquoi j'ai mis "ms" dans le temps ... c'est en quelques secondes. thx - mis à jour. – WEBjuju

+0

Avez-vous essayé de commander par «cas ...' – Strawberry

Répondre

1

En regardant votre table, la première chose à noter est que l'indice suivant est redondant

KEY `dateuploaded` (`dateuploaded`), 

son rôle peut être fullfilled par celui-ci

KEY `uploads_report` (`dateuploaded`,`client`), 

Alors laissons tomber de la clé dateuploaded. Il n'est pas clair si vous utilisez réellement la colonne client dans les requêtes. Si vous ne le faites pas, je ne crois changer votre index comme suit vous donnera une grande vitesse jusqu'à

KEY `uploads_report` (`dateuploaded`,`datefirstprinted`,`client`), 

C'est parce que MySQL ne peut utiliser un index par table. Comme l'index de la colonne dateuploaded est utilisé dans la clause where, l'index du datefirstprinted ne peut pas être utilisé. Mais si vous combinez les deux colonnes dans le même index, il peut être utilisé dans le tri et l'endroit où.

Après avoir fait l'index ci-dessus, celui-ci pourrait probablement être abandonné:

KEY `datefirstprinted` (`datefirstprinted`), 

Avoir moins d'index feront des insertions et des mises à jour plus rapides.

+0

Étant donné que ["Un index concaténé est un index sur plusieurs colonnes ... (et) un index à deux colonnes ne prend pas en charge la recherche sur le deuxième colonne seulement "] (http://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys#sb-full-table-scan) cela brise des dizaines d'autres legs requêtes dans l'application et semble ne rien faire pour la question à portée de main. – WEBjuju

+0

Prenez note de cette mise en garde: «Il n'est pas clair si vous utilisez réellement la colonne client dans les requêtes. Si vous ne le faites pas, je crois que changer votre index comme suit vous donnera une grande accélération ' – e4c5

+0

Hmm ... en regardant plus loin, je pense que vous m'avez amené à une solution. Comment est-ce que je devrais présenter ce qui a fonctionné puisque c'est réellement votre poteau qui m'a amené à lui? Dois-je éditer votre message en soulignant ce qui a fonctionné pour moi dans une mise à jour? – WEBjuju

1

Après des idées apprises sur les indices chaînés grâce à @ e4c5, j'ai essayé d'ajouter une clé sur les deux colonnes (colonne utilisée dans where et colonne utilisée dans case base order clause):

alter table 
    patientrecords 
add index 
    printedvsuploaded (datefirstprinted, dateuploaded); 

Cela avait d'abord pas effet depuis mysql a continué à utiliser l'index dateuploaded.

Cependant l'ajout force index réduit le temps de requête:

SELECT SQL_NO_CACHE 
    id, daterun, datefirstprinted 
FROM 
    patientrecords 
FORCE INDEX (printedvsuploaded) 
WHERE 
    dateuploaded <> '0000-00-00 00:00:00' 
ORDER BY 
    case when datefirstprinted = "0000-00-00 00:00:00" then 1 else 0 end desc, 
    datefirstprinted 
LIMIT 10; 

temps 0,64 secondes

il convient de noter que je suis d'accord avec @ e4c5 que l'indice supplémentaire finira par causer écrit à un coup de performance; Je compte sur le développement d'autres feuilles de route pour aider à réduire le nombre d'indices. Pour l'instant, la mise en œuvre de cette option réduira les charges de 10 secondes des ensembles de résultats les plus importants à la plage gérable de 3 secondes et sera alors la solution qui sera implémentée.

+0

Avez-vous essayé d'utiliser aucun index (par exemple avec 'ignoreindex' ou en forçant la clé primaire)? L'index actuel ne devrait pas améliorer votre requête autant que la clé primaire. – Solarflare