Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment optimiser la requête si la table contient 10000 entrées à l'aide de MySQL ?

J'essaierais de simplifier cela COMPLÈTEMENT en mettant des déclencheurs sur vos autres tables et en ajoutant simplement quelques colonnes à votre table User_Fans... Une pour chaque count() respectif que vous essayez d'obtenir... de Posts, PostLikes, PostComments, PostCommentaire J'aime.

Lorsqu'un enregistrement est ajouté à n'importe quelle table, mettez simplement à jour votre table user_fans pour ajouter 1 au décompte ... ce sera de toute façon pratiquement instantané en fonction de l'ID de clé de l'utilisateur. En ce qui concerne les "LIKES"... Similaire, uniquement à condition que quelque chose soit déclenché en tant que "Like", ajoutez 1. Votre requête sera alors un calcul direct sur le seul enregistrement et ne reposera sur AUCUNE jointure pour calculer un valeur totale "pondérée". Au fur et à mesure que votre table s'agrandit, les requêtes s'allongent également car elles ont plus de données à parcourir et à agréger. Vous parcourez CHAQUE enregistrement user_fan qui interroge essentiellement chaque enregistrement de toutes les autres tables.

Ceci étant dit, en gardant les tableaux tels que vous les avez, je restructurerais comme suit...

SELECT 
      uf.user_name,
      uf.user_id,
      @pc := coalesce( PostSummary.PostCount, 000000 ) as PostCount,
      @pl := coalesce( PostLikes.LikesCount, 000000 ) as PostLikes,
      @cc := coalesce( CommentSummary.CommentsCount, 000000 ) as PostComments,
      @cl := coalesce( CommentLikes.LikesCount, 000000 ) as CommentLikes,
      @pc + @cc AS sum_post,
      @pl + @cl AS sum_like, 
      @pCalc := (@pc + @cc) * 10 AS post_cal,
      @lCalc := (@pl + @cl) * 5 AS like_cal,
      @pCalc + @lCalc AS `total`
   FROM
      ( select @pc := 0,
               @pl := 0,
               @cc := 0,
               @cl := 0,
               @pCalc := 0
               @lCalc := 0 ) sqlvars,
      user_fans uf
        LEFT JOIN ( select user_id, COUNT(*) as PostCount
                       from post
                       group by user_id ) as PostSummary
           ON uf.user_id = PostSummary.User_ID

        LEFT JOIN ( select user_id, COUNT(*) as LikesCount
                       from post_likes
                       group by user_id ) as PostLikes
           ON uf.user_id = PostLikes.User_ID

        LEFT JOIN ( select user_id, COUNT(*) as CommentsCount
                       from post_comment
                       group by user_id ) as CommentSummary
           ON uf.user_id = CommentSummary.User_ID

        LEFT JOIN ( select user_id, COUNT(*) as LikesCount
                       from post_comment_likes
                       group by user_id ) as CommentLikes
           ON uf.user_id = CommentLikes.User_ID

   ORDER BY 
      `total` DESC 
   LIMIT 20

My variables are abbreviated as 
"@pc" = PostCount
"@pl" = PostLikes
"@cc" = CommentCount
"@cl" = CommentLike
"@pCalc" = weighted calc of post and comment count * 10 weighted value
"@lCalc" = weighted calc of post and comment likes * 5 weighted value

Le LEFT JOIN aux pré-requêtes exécute ces requêtes UNE FOIS, puis l'ensemble est joint au lieu d'être atteint en tant que sous-requête pour chaque enregistrement. En utilisant COALESCE(), s'il n'y a pas de telles entrées dans les résultats de la table LEFT JOINed, vous ne serez pas touché par des valeurs NULL gâchant les calculs, donc je les ai définis par défaut sur 000000.

CLARIFICATION DE VOS QUESTIONS

Vous pouvez avoir n'importe quelle requête en tant que "AS AliasResult". Le "As" peut également être utilisé pour simplifier les noms de table longs pour une lisibilité plus simple. Les alias peuvent également utiliser la même table mais sous un autre alias pour obtenir un contenu similaire, mais à des fins différentes.

select
      MyAlias.SomeField
   from
      MySuperLongTableNameInDatabase MyAlias ...

select
      c.LastName,
      o.OrderAmount
   from
      customers c
         join orders o
            on c.customerID = o.customerID  ...

select
      PQ.SomeKey
   from
      ( select ST.SomeKey
           from SomeTable ST
           where ST.SomeDate between X and Y ) as PQ
         JOIN SomeOtherTable SOT
            on PQ.SomeKey = SOT.SomeKey ...

Maintenant, la troisième requête ci-dessus n'est pas pratique nécessitant la (requête complète résultant en un alias "PQ" représentant "PreQuery" ). Cela pourrait être fait si vous vouliez pré-limiter un certain ensemble d'autres conditions complexes et vouliez un ensemble plus petit AVANT de faire des jointures supplémentaires à de nombreuses autres tables pour tous les résultats finaux.

Étant donné qu'un "FROM" ne DOIT PAS être une table réelle, mais peut être une requête en soi, à tout autre endroit utilisé dans la requête, il doit savoir comment référencer cet ensemble de résultats de pré-requête.

De plus, lors de l'interrogation des champs, ils peuvent également être "As FinalColumnName" pour simplifier les résultats là où ils seront également utilisés.

selectCONCAT( User.Salutation, User.LastName ) as CourtesyNamefrom ...

selectOrder.NonTaxable+ Order.Taxable+ ( Order.Taxable * Order.SalesTaxRate ) as OrderTotalWithTaxfrom ...

Le nom de colonne "As" n'est PAS obligatoire en tant qu'agrégat, mais il est le plus souvent vu de cette façon.

Maintenant, en ce qui concerne les variables MySQL... Si vous faisiez une procédure stockée, de nombreuses personnes les pré-déclareront en définissant leurs valeurs par défaut avant le reste de la procédure. Vous pouvez les faire en ligne dans une requête en définissant simplement et en donnant à ce résultat une référence "Alias". Lorsque vous faites ces variables, la sélection simulera toujours le retour d'un SEUL ENREGISTREMENT des valeurs. C'est presque comme un enregistrement unique pouvant être mis à jour utilisé dans la requête. Vous n'avez pas besoin d'appliquer de conditions "Joindre" spécifiques car cela peut n'avoir aucune incidence sur le reste des tables d'une requête... Essentiellement, crée un résultat cartésien, mais un enregistrement par rapport à une autre table ne créera jamais doublons de toute façon, donc aucun dommage en aval.

select 
       ...
   from 
      ( select @SomeVar := 0,
               @SomeDate := curdate(),
               @SomeString := "hello" ) as SQLVars

Maintenant, comment fonctionnent les sqlvars. Pensez à un programme linéaire... Une commande est exécutée dans l'ordre exact de l'exécution de la requête. Cette valeur est ensuite re-stockée dans l'enregistrement "SQLVars" prêt pour la prochaine fois. Cependant, vous ne le référencez pas comme SQLVars.SomeVar ou SQLVars.SomeDate... juste le @SomeVar :=someNewValue. Désormais, lorsque @var est utilisé dans une requête, il est également stocké en tant que "As ColumnName" dans le jeu de résultats. Parfois, il peut s'agir simplement d'une valeur calculée d'espace réservé en préparation du prochain enregistrement. Chaque valeur est alors directement disponible pour la ligne suivante. Donc, étant donné l'exemple suivant...

select
      @SomeVar := SomeVar * 2 as FirstVal,
      @SomeVar := SomeVar * 2 as SecondVal,
      @SomeVar := SomeVar * 2 as ThirdVal
   from
      ( select @SomeVar := 1 ) sqlvars,
      AnotherTable
   limit 3

Will result in 3 records with the values of 

FirstVal    SecondVal   ThirdVal
2           4           8
16          32          64
128         256         512

Remarquez comment la valeur de @SomeVar est utilisée lorsque chaque colonne l'utilise... Ainsi, même sur le même enregistrement, la valeur mise à jour est immédiatement disponible pour la colonne suivante... Cela dit, essayez maintenant de créer un nombre d'enregistrements simulés / classement par client...

select
      o.CustomerID,
      o.OrderID
      @SeqNo := if( @LastID = o.CustomerID, @SeqNo +1, 1 ) as CustomerSequence,
      @LastID := o.CustomerID as PlaceHolderToSaveForNextRecordCompare
   from
      orders o,
      ( select @SeqNo := 0, @LastID := 0 ) sqlvars
   order by
      o.CustomerID

La clause "Order By" force les résultats à être renvoyés dans l'ordre en premier. Donc, ici, les enregistrements par client sont retournés. La première fois, LastID est 0 et l'ID client est disons...5. Depuis différent, il renvoie 1 comme @SeqNo, ALORS il conserve cet ID client dans le champ @LastID pour le prochain enregistrement. Maintenant, prochain enregistrement pour le client... Le dernier ID est le même, donc il prend le @SeqNo (maintenant =1), et ajoute 1 à 1 et devient #2 pour le même client... Continuez sur le chemin... .

En ce qui concerne l'amélioration de l'écriture des requêtes, jetez un œil à la balise MySQL et regardez quelques-uns des gros contributeurs. Examinez les questions et certaines des réponses complexes et comment fonctionne la résolution de problèmes. Cela ne veut pas dire qu'il n'y en a pas d'autres avec des scores de réputation inférieurs qui débutent et qui sont complètement compétents, mais vous trouverez qui donne de bonnes réponses et pourquoi. Regardez aussi leur historique de réponses postées. Plus vous lisez et suivez, plus vous maîtriserez mieux la rédaction de requêtes plus complexes.