如何优化我复杂的MySQL查询?

时间:2012-05-17 10:09:20

标签: mysql

每行代表在特定日期特定时间播出的视频。每天大约有1600个视频。

CREATE TABLE `air_video` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `date` DATE NOT NULL,
    `time` TIME NOT NULL,
    `duration` TIME NOT NULL,
    `asset_id` INT(10) UNSIGNED NOT NULL,
    `name` VARCHAR(100) NOT NULL,
    `status` VARCHAR(100) NULL DEFAULT NULL,
    `updated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `date_2` (`date`, `time`),
    INDEX `date` (`date`),
    INDEX `status` (`status`),
    INDEX `asset_id` (`asset_id`)
)
ENGINE=InnoDB

任务

有两个条件。

  1. 每个视频每天必须显示不超过24次。
  2. 每个视频的轮播时间不得超过72小时。
  3. 轮换表示视频播出时的第一次和最后一次之间的时间跨度。

    因此,我需要根据用户指定的日期范围选择所有违反这些条件的视频。

    结果必须按天和asset_id(视频ID)分组。例如:

          date      asset_id          name           dailyCount      rotationSpan
       2012-04-27      123      whatever_the_name         35              76
       2012-04-27      134      whatever_the_name2        39              20
       2012-04-28      125      whatever_the_name3        26              43
    

    查询

    到目前为止,我已经写了这个查询:

    SELECT
        t1.date, t1.asset_id, t1.name,
        (SELECT 
            COUNT(t3.asset_id) 
            FROM air_video AS t3 
            WHERE t2.asset_id = t3.asset_id AND t3.date = t1.date
        ) AS 'dailyCount',
        MIN(CONCAT(t2.date, ' ', t2.time)) AS 'firstAir',
        MAX(CONCAT(t2.date, ' ', t2.time)) AS 'lastAir',
        ROUND(TIMESTAMPDIFF(
            MINUTE,
            MIN(CONCAT(t2.date, ' ', t2.time)),
            MAX(CONCAT(t2.date, ' ', t2.time)) 
        ) / 60) as 'rotationSpan'
    FROM
        air_video AS t1
    INNER JOIN
        air_video AS t2 ON
            t1.asset_id = t2.asset_id 
    WHERE
        t1.status NOT IN ('bumpers', 'clock', 'weather')
        AND t1.date BETWEEN '2012-04-01' AND '2012-04-30'
    GROUP BY
        t1.asset_id, t1.date
    HAVING
        `rotationSpan` > 72
        OR `dailyCount` > 24
    ORDER BY
        `date` ASC,
        `rotationSpan` DESC,
        `dailyCount` DESC    
    

    问题

    1. 用户指定日期之间的范围越大 - 完成查询所需的时间越长(一个月的范围大约需要9秒)
    2. lastAir时间戳不是视频在特定日期播出的最晚时间,而是最近一次播出的时间。

1 个答案:

答案 0 :(得分:3)

如果您需要加快查询速度,则需要删除第3行的select子查询。 要仍然具有该计数,您可以使用最初使用的确切参数在from子句中再次将其连接起来。这应该是它的样子:

SELECT
    t1.date, t1.asset_id, t1.name,
    COUNT(t3.asset_id) AS 'dailyCount',
    MIN(CONCAT(t2.date, ' ', t2.time)) AS 'firstAir',
    MAX(CONCAT(t2.date, ' ', t2.time)) AS 'lastAir',
    ROUND(TIMESTAMPDIFF(
        MINUTE,
        MIN(CONCAT(t2.date, ' ', t2.time)),
        MAX(CONCAT(t2.date, ' ', t2.time)) 
    ) / 60) as 'rotationSpan'
FROM
    air_video AS t1
INNER JOIN
    air_video AS t2 ON
        (t1.asset_id = t2.asset_id)
INNER JOIN
    air_video AS t3 
        ON (t2.asset_id = t3.asset_id AND t3.date = t1.date)
WHERE
    t1.status NOT IN ('bumpers', 'clock', 'weather')
    AND t1.date BETWEEN '2012-04-01' AND '2012-04-30'
GROUP BY
    t1.asset_id, t1.date
HAVING
    `rotationSpan` > 72
    OR `dailyCount` > 24
ORDER BY
    `date` ASC,
    `rotationSpan` DESC,
    `dailyCount` DESC

由于t2不受日期限制,因此您显然在查看整个表格,而不是日期范围。

修改 由于大量的日期绑定,查询仍然运行得太慢。然后我采取了不同的方法。我创建了3个视图(显然你可以将它们组合成一个没有视图的普通查询,但我更喜欢最终结果查询)

- T1 -

CREATE VIEW t1 AS select date,asset_id,name from air_video where (status not in ('bumpers','clock','weather')) group by asset_id,date order by date;

- T2 -

CREATE VIEW t2 AS select t1.date,t1.asset_id,t1.name,min(concat(t2.date,' ',t2.time)) AS 'firstAir',max(concat(t2.date,' ',t2.time)) AS 'lastAir',round((timestampdiff(MINUTE,min(concat(t2.date,' ',t2.time)),max(concat(t2.date,' ',t2.time))) / 60),0) AS 'rotationSpan' from (t1 join air_video t2 on((t1.asset_id = t2.asset_id))) group by t1.asset_id,t1.date;

- T3 -

CREATE VIEW t3 AS select t2.date,t2.asset_id,t2.name,count(t3.asset_id) AS 'dailyCount',t2.firstAir,t2.lastAir,t2.rotationSpan AS rotationSpan from (t2 join air_video t3 on(((t2.asset_id = t3.asset_id) and (t3.date = t2.date)))) group by t2.asset_id,t2.date;

然后,您可以运行以下查询:

SELECT
    date,
    asset_id, 
    name,
    dailyCount,
    firstAir,
    lastAir,
    rotationSpan
FROM
    t3
WHERE
    date BETWEEN '2012-04-01' AND '2012-04-30'
    AND (
        rotationSpan > 72
        OR 
        dailyCount > 24
    )
ORDER BY
    date ASC,
    rotationSpan DESC,
    dailyCount DESC