A better way to modify this query so the subquery will be more specific?

南楼画角 提交于 2019-12-11 20:34:58

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!