Question

How do I best round down (i.e. "floor") the value of now() to the nearest hour?

This is in order to make the query cache work efficiently with statements such as:

SELECT SQL_CACHE * FROM table WHERE appointment_date >= NOW()
Was it helpful?

Solution

Unless you do the rounding in your application layer, do not expect MySQL to be smart enough to use the query cache. Any query that includes NOW() (and many other functions) will never be cached.

That said, if you do compute the nearest hour in the application layer, it should work fine.

OTHER TIPS

If you have query that cannot be cached, you can split it into two simpler queries. The first query will return row IDs based on non-cacheable functions; the second query will do the rest. So at least the second query will be cached.

Using your example, you can make two queries:

  1. SELECT ID FROM table WHERE appointment_date >= NOW();

... so your application will collect IDs. If there are any IDs returned, combine them to a coma-separated string and run the second query:

  1. SELECT * FROM table WHERE ID IN (".$ids.");"

Of course, your query is quite simple, but if you have very complex and large queries, you can split them programmatically. For example, you may use a regular expression and a function to find out whether the query is non-cacheable and it is worth splitting it. For example, you can use the following PHP function for that purpose:

function query_cannot_be_cached($query)
{
  return preg_match("/\b(?:AES_DECRYPT|AES_ENCRYPT|BENCHMARK|CONNECTION_ID|CONVERT_TZ|CURDATE|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURTIME|DATABASE|ENCRYPT|FOUND_ROWS|GET_LOCK|IS_FREE_LOCK|IS_USED_LOCK|LAST_INSERT_ID|LOAD_FILE|MASTER_POS_WAIT|NOW|PASSWORD|RAND|RANDOM_BYTES|RELEASE_ALL_LOCKS|RELEASE_LOCK|SLEEP|SYSDATE|UNIX_TIMESTAMP|USER|UUID|UUID_SHORT)\b *\(/i", $query);
}

Another solution is just to avoid server-side comparison (Now()) and use client-side comparison only. We use this approach in our server. If a query return just a few data, you can not split it. But if it returns lot of data split it as I have explained below. So your query will be like that:

  • SELECT appointment_date, id FROM table;

or you can set a date granularity (say, one month, one-day or one-hour) and add it to the comparison. This granular date should be calculated in advance by your software, for example:

  • SELECT appointment_date, id FROM table WHERE appointment_date > '2017-05-10';

(this '2017-05-10' value should be calculated by your software, it changes only once a day).

and then you receive the appointment_date and compare it with current time on client side. If the time matches, you run the second query as shown in the example #2 above.

Of course, these examples are very simple, we are using much more complex queries in our server.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top