mysql - 关于对比较的 SQL 查询的优化

标签 mysql sql

我正在开发一个配对比较网站,用户可以在该网站上加载来自另一个网站的电影和等级列表。然后,我的网站随机挑选两部电影并将它们相互匹配,用户从两部电影中选择较好的一部,然后加载一对新电影。这给出了按最佳顺序排序的完整电影列表。

数据库包含三个表;

fm_film_data - 这包含所有导入的电影

fm_film_data(id int(11), 
             imdb_id varchar(10), 
             tmdb_id varchar(10), 
             title varchar(255),     
             original_title varchar(255),    
             year year(4),
             director text,
             description text,
             poster_url varchar(255))

fm_films - 这包含与用户相关的所有信息,用户看过哪些电影,用户给出的评分,以及该用户每部电影的输赢信息。

fm_films(id int(11),
         user_id int(11),
         film_id int(11),
         grade int(11),  
         wins int(11),   
         losses int(11))

fm_log - 这包含已发生的每一场决斗的记录。

fm_log(id int(11),
       user_id int(11),
       winner int(11),
       loser int(11))

为了选择一对显示给用户,我创建了一个 mySQL 查询来检查日志并随机选择一对。

SELECT pair.id1, pair.id2 
FROM
    (SELECT part1.id AS id1, part2.id AS id2 
    FROM fm_films AS part1, fm_films AS part2 
    WHERE part1.id <> part2.id 
        AND part1.user_id = [!!USERID!!] 
        AND part2.user_id = [!!USERID!!]) 
AS pair
LEFT JOIN
    (SELECT winner AS id1, loser AS id2 
    FROM fm_log
    WHERE fm_log.user_id = [!!USERID!!]
    UNION
    SELECT loser AS id1, winner AS id2 
    FROM fm_log
    WHERE fm_log.user_id = [!!USERID!!])
AS log
ON pair.id1 = log.id1 AND pair.id2 = log.id2
WHERE log.id1 IS NULL
ORDER BY RAND()
LIMIT 1

此查询需要一些时间来加载,在我们的测试中大约需要 6 秒,两个用户各有大约 800 个成绩。

我正在寻找一种优化方法,但仍然限制所有决斗只出现一次。

服务器运行 MySQL 版本 5.0.90-community。

最佳答案

我认为您最好创建一个存储过程/函数,它会在找到有效的一对后立即返回一对。

确保有适当的索引:

  • fm_films.user_id(也尝试包括 film_id)
  • fm_log.user_id(尝试包括获胜者失败者)

DELIMITER $$

DROP PROCEDURE IF EXISTS spu_findPair$$

CREATE PROCEDURE spu_findPair
(
    IN vUserID INT
)
BEGIN
    DECLARE done BOOLEAN DEFAULT FALSE;
    DECLARE vLastFilmID INT;
    DECLARE vCurFilmID INT;
    DECLARE cUserFilms CURSOR FOR
        SELECT id
        FROM fm_films
        WHERE user_id  = vUserID
        ORDER BY RAND();
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=TRUE;
    OPEN cUserFilms;
    ufLoop: LOOP
        FETCH cUserFilms INTO vCurFilmID;
        IF done THEN
            CLOSE cUserFilms;
            LEAVE ufLoop;
        END IF;
        IF vLastFilmID IS NOT NULL THEN
            IF NOT EXISTS
                (
                    SELECT 1
                    FROM fm_log
                    WHERE user_id  = vUserID
                        AND ((winner = vCurFilmID AND loser = vLastFilmID) OR (winner = vLastFilmID AND loser = vCurFilmID))
                ) THEN

                CLOSE cUserFilms;
                LEAVE ufLoop;
                #output
                SELECT vLastFilmID, vCurFilmID;
            END IF;
        END IF;
    END LOOP;

END$$

DELIMITER ;

关于mysql - 关于对比较的 SQL 查询的优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4606404/

相关文章:

php - 显示它们之前如何从数据库中订购获取的对象?

c# - 如何将gridview绑定(bind)到另一个gridview?

SQL 执行计划高成本 70%

mysql - 如何在限制一个 SQL 查询中不同 id 的数量的同时获取不同数量的行?

sql - 将 get_absolute_url 组合成 django 中的原始 SQL 语句

sql - Oracle SQL - 根据列中的值为结果集中的每一行提供唯一标识符

mysql - mysql创建临时表存储过程报错

mysql - 从表A到表B的触发器ID

PHP 和 MYSQL : Why does A work and B not work?

php - 将 WordPress 从 xampp 迁移到 xampp