well, when you plan to use cache you do it because:
a) the additional roundtrip is negligible
b) using the cache means your database is not used
I'll explain it further....
A query that takes 0.18ms to get "computed" on the database side can very well end up in 18sec as soon as your database gets "hit" by several requests.
A cached query that takes 0.18ms to get stored and 0.18ms to get retrieved (unpacked, decompressed, unpickled, choose your own "term" that suits the job) will take, if the cache backend is "proper", 0.36ms the first time and 0.18ms from the 2nd to the nth.
Even so, some complicated queries (or uncomplicated for you, but complicated for the db backend) can be speedied up just requesting less fields or computing in raw python something that can be usually done on the backend.
This kind of optimization (i.e. relying on python's datetime operations vs an extract(epoch from datefield) on the db) will be less and less useful as soon as the resultsets cardinality grows (i.e. the more rows you have the better chance for the db to get the results faster than having a basic query and processing the results in python).
Assuming your "python filter" cuts out the 5% on the total of the rows, as soon as the number of rows grows you'll just waste time unpacking that additional 5%, and the time difference will be more explicit.
On a total unrelated note, remember that Python is fast, but dbs are fast too, especially when leveraged by all their features (e.g. you could set an index on the "computed" epoch and that query will probably gain a lot of speedup).
This is meant to explain why Q2 is faster than Q1.
On the reason why Q1 with cache isn't faster than Q2 with cache, since the time difference is negligible, it's possible that the time difference gets "spent" on calculating the automatic key for the cache of the query (on the first implementation needs to add the "elapsed time" part) . Just to be sure, try timing
def Q1():
your first implementation, without cache
def Q2():
your second implementation, without cache, without the check in python
with
rows1 = cache('q1', lambda : Q1())
..... and, separately
rows2 = cache('q2', lambda: Q2())
rows2 = [r for row in rows ...]