Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slow MySQL SELECT performance

Tags:

database

mysql

I have the following database schema: https://dl.dropbox.com/u/37915176/schema.PNG

I have more than 2 millions records in meter_relevation. The data is coming from several electrical meters which are associated to appliances (devices) with the meter_history table.

I try to get the data of a device for a specific date range:

SELECT MR.* 
FROM device AS D, meter_history AS MH, meter AS M, meter_relevation AS MR
WHERE D.Id=MH.Id_Device 
   AND MH.Id_Meter=M.Id 
   AND M.Id=MR.Id_Meter 
   AND D.Id="8" 
   AND MR.Date>="2012-10-04" 
   AND MR.Date<="2012-10-04"

But the performance are very slow, I can get 10 seconds even if there are no record for the specified date range.

I tried EXPLAIN and I clearly see that my query is not optimal, on the meter_relevation table is listing the total number of rows, more than 2 millions: https://dl.dropbox.com/u/37915176/explain.png

Any suggestions? There is a better way? Of course I could do some work on the client side and split in several queries. But I would like to know if there are better way for a single SELECT query.

like image 910
Davide Rivola Avatar asked Mar 02 '26 22:03

Davide Rivola


1 Answers

If that is the query you're running, it looks sub-optimal. You don't really need the D table, or do you? Seeing as how the condition on device could be satisfied by MH.Id_device = "8".

But supposing there are other fields which aren't shown, then let's rewrite:

SELECT MR.*
    FROM meter_relevation AS MR
    JOIN meter AS M ON ( M.Id = MR.ID_Meter )
    JOIN meter_history AS MH ON ( MH.Id_Meter = M.Id )
    JOIN device AS D ON ( D.Id=MH.Id_Device AND D.Id = "8" )
WHERE 
    MR.Date BETWEEN "2012-10-04" AND "2012-10-04";

So we need indexes. The first is the most important

CREATE INDEX mr_ndx     ON meter_relevation ( Date, Id_Meter );

But try also deleting the index above and using instead:

CREATE INDEX mr_ndx     ON meter_relevation ( Id_Meter, Date );


CREATE INDEX m_ndx      ON meter(Id); -- This probably already exists
CREATE INDEX mh_ndx     ON meter_history( Id_Device, Id_Meter );
CREATE INDEX d_ndx      ON device (Id); -- This too probably already exists

The above, if written just like that, is equivalent (but MySQL should realize this, so I don't think it's slowing you too much) to

SELECT MR.*
    FROM meter_relevation AS MR
    JOIN meter AS M ON ( MR.ID_Meter = M.Id)
    JOIN meter_history AS MH ON (MH.Id_Device = "8" AND MH.Id_Meter = M.Id)
WHERE 
    MR.Date BETWEEN "2012-10-04" AND "2012-10-04";
like image 62
LSerni Avatar answered Mar 04 '26 19:03

LSerni



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!