-1

我有一个正在处理的媒体中心网站,该网站有一个显示媒体类别的页面。每个类别可以分配有多个媒体项,并且每个媒体项可以分配给多个类别。

该页面包含一个文本输入,该输入必须能够过滤显示的类别。

我需要做的是获取与当前用户 ( $user_id) 关联的每个类别以及属于该用户的媒体项目(未显示的类别不显示)。通常这很简单,但我还必须能够根据与媒体关联的其他表中的字段过滤类别。

我需要能够应用文本过滤器的字段如下:

  • message_numbermedia表中
  • keywordsmedia表中
  • speaker_namemedia_speakers表中
  • series_namemedia_series表中
  • book_namemedia_books表中
  • category_namemedia_categories表中

就像现在一样,查询需要几秒钟才能完成。我不是 MySQL 专业人士,所以我确信必须有更好的方法来做我需要做的事情。如果有帮助,我正在通过 PHP 使用 MySQLi。我的查询有几个子查询,我很肯定这是问题的原因,但我不知道有什么其他方法可以做我想做的事情。

下面是相关的表结构和当前查询。我已经包含了尽可能多的信息,可以帮助某人帮助我解决这个问题,但如果您需要更多信息,请告诉我。

media表(省略了一些不相关的字段)(series、speaker 和 book 字段包含相应表中记录的 ID):

  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `date` date NOT NULL DEFAULT '0000-00-00',
  `message_number` varchar(32) DEFAULT NULL,
  `series` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
  `speaker` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
  `book` int(10) unsigned zerofill NOT NULL DEFAULT '0000000000',
  `keywords` text NOT NULL,
  PRIMARY KEY (`id`)

media_series

  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `series_name` varchar(255) NOT NULL DEFAULT '',
  `cover` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)

media_speakers

  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `speaker_name` varchar(255) NOT NULL DEFAULT '',
  `cover` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)

media_books

  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `book_name` varchar(64) NOT NULL DEFAULT '',
  `book_shortname` varchar(10) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)

media_categories

  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `category_name` varchar(255) NOT NULL DEFAULT '',
  `cover` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id` (`id`)

media_categories_assoc

  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned DEFAULT NULL,
  `media_id` int(10) unsigned zerofill DEFAULT NULL,
  `category_id` int(10) unsigned zerofill DEFAULT NULL,
  `marked_for_deletion` int(1) DEFAULT '0',
  PRIMARY KEY (`id`)

最后,过于复杂的查询:

 SELECT media_categories.id            `media_categories.id`,
       media_categories.user_id       `media_categories.user_id`,
       media_categories.category_name `media_categories.category_name`,
       media_categories.cover         `media_categories.cover`,
       (SELECT id
        FROM   media
        WHERE  user_id = '$user_id'
               AND media_categories.id IN (SELECT category_id
                                           FROM   media_category_assoc
                                           WHERE  user_id = '$user_id')
        ORDER  BY `date` DESC
        LIMIT  1)                     `media.id`,
       (SELECT `date`
        FROM   media
        WHERE  user_id = '$user_id'
               AND media_categories.id IN (SELECT category_id
                                           FROM   media_category_assoc
                                           WHERE  user_id = '$user_id')
        ORDER  BY `date` DESC
        LIMIT  1)                     `media.date`,
       (SELECT series
        FROM   media
        WHERE  user_id = '$user_id'
               AND media_categories.id IN (SELECT category_id
                                           FROM   media_category_assoc
                                           WHERE  user_id = '$user_id')
        ORDER  BY `date` DESC
        LIMIT  1)                     `media.series`,
       (SELECT speaker
        FROM   media
        WHERE  user_id = '$user_id'
               AND media_categories.id IN (SELECT category_id
                                           FROM   media_category_assoc
                                           WHERE  user_id = '$user_id')
        ORDER  BY `date` DESC
        LIMIT  1)                     `media.speaker`
FROM   media_categories
       LEFT JOIN media
              ON media.id IN (SELECT media_id
                              FROM   media_category_assoc
                              WHERE  media_id = media.id
                                     AND user_id = '$user_id')
       LEFT JOIN media_series
              ON media.series = media_series.id
       LEFT JOIN media_speakers
              ON media.speaker = media_speakers.id
       LEFT JOIN media_books
              ON media.book = media_books.id
WHERE  media_categories.user_id = '$user_id'
       AND media_categories.id IN (SELECT category_id
                                   FROM   media_category_assoc
                                   WHERE  user_id = '$user_id')
       AND ( media.title LIKE '%filter_text%'
              OR media.message_number LIKE '%filter_text%'
              OR media.keywords LIKE '%filter_text%'
              OR media_speakers.speaker_name LIKE '%filter_text%'
              OR media_categories.category_name LIKE '%filter_text%'
              OR media_series.series_name LIKE '%filter_text%'
              OR media_books.book_name LIKE '%filter_text%' )
GROUP  BY `media_categories.id`
ORDER  BY `media.date` DESC
LIMIT  0, 12;  
4

2 回答 2

1

如果我能够正确理解查询,您可以尝试从给定用户的每个类别的最新媒体中获取一些额外信息。据我所知,SELECT 子句中的所有子查询都可以移至 FROM 子句。

也许这样做会有所帮助?

SELECT media_categories.id,
       media_categories.user_id,
       media_categories.category_name,
       media_categories.cover,
       newest_media.id,
       newest_media.'date',
       newest_media.series,
       newest_media.speaker
FROM   media_categories
        LEFT JOIN media_category_assoc
              ON media_categories.id = media_category_assoc.category_id AND media_categories.user_id = media_category_assoc.user_id
        LEFT JOIN (
                    SELECT id, 'date', series, speaker
                    FROM   media
                    WHERE  media.id = media_category_assoc.media_id
                    ORDER  BY `date` DESC
                    LIMIT  1
                ) newest_media ON newest_media.user_id = '$user_id'
       LEFT JOIN media_series
              ON newest_media.series = media_series.id
       LEFT JOIN media_speakers
              ON newest_media.speaker = media_speakers.id
       LEFT JOIN media_books
              ON newest_media.book = media_books.id
       LEFT JOIN media
              ON media.id = media_category_assoc.media_id AND media.user_id = '$user_id'
WHERE  media_categories.user_id = '$user_id'
       AND ( media.title LIKE '%filter_text%'
              OR media.message_number LIKE '%filter_text%'
              OR media.keywords LIKE '%filter_text%'
              OR media_speakers.speaker_name LIKE '%filter_text%'
              OR media_categories.category_name LIKE '%filter_text%'
              OR media_series.series_name LIKE '%filter_text%'
              OR media_books.book_name LIKE '%filter_text%' )
GROUP  BY `media_categories.id`
ORDER  BY `media.date` DESC
LIMIT  0, 12;
于 2013-05-01T22:59:03.093 回答
1

正如我在评论中提到的,子查询可能是您查询中的瓶颈。首先,explain select...在查询上运行以检查执行计划。

请参阅参考手册:

现在,关于我提出的关于使用临时表的建议,我将以您的第一个子查询为例。

你用这个:

SELECT 
...,
(SELECT id
    FROM   media
    WHERE  user_id = '$user_id'
           AND media_categories.id IN (SELECT category_id
                                       FROM   media_category_assoc
                                       WHERE  user_id = '$user_id')
    ORDER  BY `date` DESC
    LIMIT  1),
....

你可以这样做:

drop table if exists temp_step1;
create temporary table temp_step1
    select id
    from media
    where user_id = @user_id -- I'm assuming you are putting this in a stored procedure
        and media_categories.id in (SELECT category_id 
                                    FROM media_category_assoc
                                    WHERE  user_id = @user_id)
    order by `date` desc
    limit 1;

然后,您可以将此temp_step1表用作大查询的行源。

请注意,此示例仅返回一行,因此对它进行索引没有意义。对于那些包含多于一行并且您在查询子句中使用的临时表,您至少需要在您正在执行连接的所有字段上FROM ... JOIN ...创建索引。为此,在创建临时表(例如)之后,您应该这样做:temp_step_X

alter table temp_step_X
   add index idx_indexName(field1),
   ...;

希望这可以帮助你

于 2013-04-30T20:49:51.407 回答