One to many to many












1















I have just inherited an app with a MySQL database in need of performance tuning.
One particular task that takes way too long involves three tables:




  • monitor(id, ...)


  • result(id, FK(monitor_id), timestamp, ...)


  • step(id, FK(result_id), ...)



where monitor is one to many results, and result is one to many steps.

Basic flow is the user asks for all the results for a monitor with monitor_id x and timestamp below y.



That works at an acceptable speed, but the user may also choose to see each individual step that makes up the result. The number and name of steps varies so they can't go into the result table.



My predecessor solved this by first get all results matching monitor_id and timestamp in one query.



select * from results where monitor_id = ? and timestamp >= ?  


and then iterate through all results and fetch the matching steps, one query per each result.



select * from steps where result_id = ?


I have indexed the columns used in the where caluse which sped things up considerably but even though the query to get each step is fast, it is executed once per each result which can quickly go into thousands, or tens of thousands which together takes way too much time.



I have tried to do something like this query:



select * from steps 
where result_id in
( select id as result_id from results
where monitor_id = ? and timestamp >= ?
)


which gives a performance boost but still far from where I'd like it to be.
This feels like a common problem, so my question is: what's the most time efficient solution to this bottleneck?



edit:



So I took @Falcon advice and tried it out.
This is how a query can look:



select count(*) 
from
monitors m join results r
on r.monitor_id = m.id
join steps s
on s.result_id=r.id

where r.monitor_id = 64
and r.completed >= '2014-10-01'
and r.completed <= '2014-10-03'


This particular query retuns 1728 rows.
MySQL's explain for this query gives:



select type  table type   key           ref      rows extra

SIMPLE, m const PRIMARY, const, 1
SIMPLE, r range mon_id_compl null 575 Using where
SIMPLE, s ref result_id db.r.id 2


However, queries like these take 5-10 seconds. The query uses the best indexes so that can't be the problem?



The tables' sizes are as following



monitors 133
results 8.441.842
steps 33.738.264


Is this too much to expect a quick query?










share|improve this question
















bumped to the homepage by Community 25 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.




















    1















    I have just inherited an app with a MySQL database in need of performance tuning.
    One particular task that takes way too long involves three tables:




    • monitor(id, ...)


    • result(id, FK(monitor_id), timestamp, ...)


    • step(id, FK(result_id), ...)



    where monitor is one to many results, and result is one to many steps.

    Basic flow is the user asks for all the results for a monitor with monitor_id x and timestamp below y.



    That works at an acceptable speed, but the user may also choose to see each individual step that makes up the result. The number and name of steps varies so they can't go into the result table.



    My predecessor solved this by first get all results matching monitor_id and timestamp in one query.



    select * from results where monitor_id = ? and timestamp >= ?  


    and then iterate through all results and fetch the matching steps, one query per each result.



    select * from steps where result_id = ?


    I have indexed the columns used in the where caluse which sped things up considerably but even though the query to get each step is fast, it is executed once per each result which can quickly go into thousands, or tens of thousands which together takes way too much time.



    I have tried to do something like this query:



    select * from steps 
    where result_id in
    ( select id as result_id from results
    where monitor_id = ? and timestamp >= ?
    )


    which gives a performance boost but still far from where I'd like it to be.
    This feels like a common problem, so my question is: what's the most time efficient solution to this bottleneck?



    edit:



    So I took @Falcon advice and tried it out.
    This is how a query can look:



    select count(*) 
    from
    monitors m join results r
    on r.monitor_id = m.id
    join steps s
    on s.result_id=r.id

    where r.monitor_id = 64
    and r.completed >= '2014-10-01'
    and r.completed <= '2014-10-03'


    This particular query retuns 1728 rows.
    MySQL's explain for this query gives:



    select type  table type   key           ref      rows extra

    SIMPLE, m const PRIMARY, const, 1
    SIMPLE, r range mon_id_compl null 575 Using where
    SIMPLE, s ref result_id db.r.id 2


    However, queries like these take 5-10 seconds. The query uses the best indexes so that can't be the problem?



    The tables' sizes are as following



    monitors 133
    results 8.441.842
    steps 33.738.264


    Is this too much to expect a quick query?










    share|improve this question
















    bumped to the homepage by Community 25 mins ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.


















      1












      1








      1








      I have just inherited an app with a MySQL database in need of performance tuning.
      One particular task that takes way too long involves three tables:




      • monitor(id, ...)


      • result(id, FK(monitor_id), timestamp, ...)


      • step(id, FK(result_id), ...)



      where monitor is one to many results, and result is one to many steps.

      Basic flow is the user asks for all the results for a monitor with monitor_id x and timestamp below y.



      That works at an acceptable speed, but the user may also choose to see each individual step that makes up the result. The number and name of steps varies so they can't go into the result table.



      My predecessor solved this by first get all results matching monitor_id and timestamp in one query.



      select * from results where monitor_id = ? and timestamp >= ?  


      and then iterate through all results and fetch the matching steps, one query per each result.



      select * from steps where result_id = ?


      I have indexed the columns used in the where caluse which sped things up considerably but even though the query to get each step is fast, it is executed once per each result which can quickly go into thousands, or tens of thousands which together takes way too much time.



      I have tried to do something like this query:



      select * from steps 
      where result_id in
      ( select id as result_id from results
      where monitor_id = ? and timestamp >= ?
      )


      which gives a performance boost but still far from where I'd like it to be.
      This feels like a common problem, so my question is: what's the most time efficient solution to this bottleneck?



      edit:



      So I took @Falcon advice and tried it out.
      This is how a query can look:



      select count(*) 
      from
      monitors m join results r
      on r.monitor_id = m.id
      join steps s
      on s.result_id=r.id

      where r.monitor_id = 64
      and r.completed >= '2014-10-01'
      and r.completed <= '2014-10-03'


      This particular query retuns 1728 rows.
      MySQL's explain for this query gives:



      select type  table type   key           ref      rows extra

      SIMPLE, m const PRIMARY, const, 1
      SIMPLE, r range mon_id_compl null 575 Using where
      SIMPLE, s ref result_id db.r.id 2


      However, queries like these take 5-10 seconds. The query uses the best indexes so that can't be the problem?



      The tables' sizes are as following



      monitors 133
      results 8.441.842
      steps 33.738.264


      Is this too much to expect a quick query?










      share|improve this question
















      I have just inherited an app with a MySQL database in need of performance tuning.
      One particular task that takes way too long involves three tables:




      • monitor(id, ...)


      • result(id, FK(monitor_id), timestamp, ...)


      • step(id, FK(result_id), ...)



      where monitor is one to many results, and result is one to many steps.

      Basic flow is the user asks for all the results for a monitor with monitor_id x and timestamp below y.



      That works at an acceptable speed, but the user may also choose to see each individual step that makes up the result. The number and name of steps varies so they can't go into the result table.



      My predecessor solved this by first get all results matching monitor_id and timestamp in one query.



      select * from results where monitor_id = ? and timestamp >= ?  


      and then iterate through all results and fetch the matching steps, one query per each result.



      select * from steps where result_id = ?


      I have indexed the columns used in the where caluse which sped things up considerably but even though the query to get each step is fast, it is executed once per each result which can quickly go into thousands, or tens of thousands which together takes way too much time.



      I have tried to do something like this query:



      select * from steps 
      where result_id in
      ( select id as result_id from results
      where monitor_id = ? and timestamp >= ?
      )


      which gives a performance boost but still far from where I'd like it to be.
      This feels like a common problem, so my question is: what's the most time efficient solution to this bottleneck?



      edit:



      So I took @Falcon advice and tried it out.
      This is how a query can look:



      select count(*) 
      from
      monitors m join results r
      on r.monitor_id = m.id
      join steps s
      on s.result_id=r.id

      where r.monitor_id = 64
      and r.completed >= '2014-10-01'
      and r.completed <= '2014-10-03'


      This particular query retuns 1728 rows.
      MySQL's explain for this query gives:



      select type  table type   key           ref      rows extra

      SIMPLE, m const PRIMARY, const, 1
      SIMPLE, r range mon_id_compl null 575 Using where
      SIMPLE, s ref result_id db.r.id 2


      However, queries like these take 5-10 seconds. The query uses the best indexes so that can't be the problem?



      The tables' sizes are as following



      monitors 133
      results 8.441.842
      steps 33.738.264


      Is this too much to expect a quick query?







      mysql






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 20 '15 at 13:25







      Pengtuzi

















      asked Mar 20 '15 at 8:19









      PengtuziPengtuzi

      62




      62





      bumped to the homepage by Community 25 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 25 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          2 Answers
          2






          active

          oldest

          votes


















          0














          Relational databases excel at joins. You could fetch all the data at once in a single result set:



          select * from monitor m join result r on r.monitor_id=m.monitor_id join steps s on s.result_id=r.result_id
          where m.monitor.id = ? and timestamp >= ?


          But I cannot guarantee that this makes a difference. You can generate the execution plans for all the statements you tried to tell if there're any bottlenecks or the engine makes mistakes in optimizations.






          share|improve this answer
























          • Thanks for your reply. Please see my edit for my latest discoveries.

            – Pengtuzi
            Mar 20 '15 at 13:14



















          0














          You need the "Compound" index: INDEX(monitor_id, completed)






          share|improve this answer























            Your Answer








            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "182"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f95762%2fone-to-many-to-many%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            Relational databases excel at joins. You could fetch all the data at once in a single result set:



            select * from monitor m join result r on r.monitor_id=m.monitor_id join steps s on s.result_id=r.result_id
            where m.monitor.id = ? and timestamp >= ?


            But I cannot guarantee that this makes a difference. You can generate the execution plans for all the statements you tried to tell if there're any bottlenecks or the engine makes mistakes in optimizations.






            share|improve this answer
























            • Thanks for your reply. Please see my edit for my latest discoveries.

              – Pengtuzi
              Mar 20 '15 at 13:14
















            0














            Relational databases excel at joins. You could fetch all the data at once in a single result set:



            select * from monitor m join result r on r.monitor_id=m.monitor_id join steps s on s.result_id=r.result_id
            where m.monitor.id = ? and timestamp >= ?


            But I cannot guarantee that this makes a difference. You can generate the execution plans for all the statements you tried to tell if there're any bottlenecks or the engine makes mistakes in optimizations.






            share|improve this answer
























            • Thanks for your reply. Please see my edit for my latest discoveries.

              – Pengtuzi
              Mar 20 '15 at 13:14














            0












            0








            0







            Relational databases excel at joins. You could fetch all the data at once in a single result set:



            select * from monitor m join result r on r.monitor_id=m.monitor_id join steps s on s.result_id=r.result_id
            where m.monitor.id = ? and timestamp >= ?


            But I cannot guarantee that this makes a difference. You can generate the execution plans for all the statements you tried to tell if there're any bottlenecks or the engine makes mistakes in optimizations.






            share|improve this answer













            Relational databases excel at joins. You could fetch all the data at once in a single result set:



            select * from monitor m join result r on r.monitor_id=m.monitor_id join steps s on s.result_id=r.result_id
            where m.monitor.id = ? and timestamp >= ?


            But I cannot guarantee that this makes a difference. You can generate the execution plans for all the statements you tried to tell if there're any bottlenecks or the engine makes mistakes in optimizations.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Mar 20 '15 at 11:34









            FalconFalcon

            165119




            165119













            • Thanks for your reply. Please see my edit for my latest discoveries.

              – Pengtuzi
              Mar 20 '15 at 13:14



















            • Thanks for your reply. Please see my edit for my latest discoveries.

              – Pengtuzi
              Mar 20 '15 at 13:14

















            Thanks for your reply. Please see my edit for my latest discoveries.

            – Pengtuzi
            Mar 20 '15 at 13:14





            Thanks for your reply. Please see my edit for my latest discoveries.

            – Pengtuzi
            Mar 20 '15 at 13:14













            0














            You need the "Compound" index: INDEX(monitor_id, completed)






            share|improve this answer




























              0














              You need the "Compound" index: INDEX(monitor_id, completed)






              share|improve this answer


























                0












                0








                0







                You need the "Compound" index: INDEX(monitor_id, completed)






                share|improve this answer













                You need the "Compound" index: INDEX(monitor_id, completed)







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Mar 20 '15 at 17:11









                Rick JamesRick James

                43.6k22259




                43.6k22259






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Database Administrators Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f95762%2fone-to-many-to-many%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Ronny Ackermann

                    Köttigit

                    MySQL 8.0.15 starts normally but any connection hangs