问题
This is the current query that I was working with and it was doing a good job until now:
SELECT a.*
FROM ads AS a
WHERE id = (
SELECT id
FROM ads AS b
WHERE a.zone_name=b.zone_name
AND publish_date <= CURDATE() AND expire_date >= CURDATE() AND active = 1
LIMIT 1
)
It returns one ad per every existing zone, and then I display the appropriate ad in the appropriate place in my template after sorting them by zones.
Now we have introduced a responsive layout, so the zones can't always fit the same dimension of the ad, and I would like to always fetch the biggest one that fits. I am reading the browser resolution with Javascript first, passing it back to PHP and then PHP and MySQL can decide which ads to fit.
But I don't know how to fit that condition into the query easier than the solution I have provided below, and I would like a better one. Since different zones will fit different dimensions on the same resolution, I need a way to specify a size per every zone, and the only way I can think of doing that is to fetch them one by one, specifying the desired size for each zone, and then do a UNION ALL
on them.
But that seems like rather sluggish to execute, and I was wondering if there is a better one that would simplify the conditions and possibly remove the repetition of the publish_date
condition, avoid UNION
and therefore (hopefully) execute faster?
(SELECT ads.* FROM ads WHERE zone_name = 'b' AND width=468 AND publish_date <= CURDATE() AND expire_date >= CURDATE() AND active = 1 LIMIT 1)
UNION ALL
(SELECT ads.* FROM ads WHERE zone_name = 'a1' AND width=728 AND publish_date <= CURDATE() AND expire_date >= CURDATE() AND active = 1 LIMIT 1)
...
回答1:
This is your query:
SELECT a.*
FROM ads a
WHERE id = (SELECT id
FROM ads b
WHERE a.zone_name=b.zone_name AND
publish_date <= CURDATE() AND
expire_date >= CURDATE() AND
active = 1
LIMIT 1
);
You don't appear to care about which ad you get (such as getting the most recent).
A good place to start for performance is an index:
create index idx_ads_4 on ads(zone_name, active, expire_date, publish_date)
This is a "covering index", meaning that all the columns used in the subquery use the index.
Next, if you have different sizes for the zones, then put that into a table and use a join
. The complete query looks something like:
select a.*
from (select 'b' as zone_name, 468 as width union all
select 'a1', 728
) ZoneWidths join
ads a
on a.zone_name = zonewidths.zone_name and a.width = zonewidths.width
where id = (SELECT id
FROM ads b
WHERE a.zone_name=b.zone_name AND
a.width = b.width AND
publish_date <= CURDATE() AND
expire_date >= CURDATE() AND
active = 1
LIMIT 1
);
The appropriate index for this query also needs width
:
create index idx_ads_5 on ads(zone_name, width, active, expire_date, publish_date)
来源:https://stackoverflow.com/questions/25059939/a-better-way-to-modify-this-query-so-the-subquery-will-be-more-specific