MySQL - 按列值获取范围内的结果

MySQL - Getting results on a range by column value

解决方案 B

因为我接受了@Strawberry 的回答,而且这个解决方案不属于 post 本身的问题,所以我不会把它写成答案,而是留给任何可能有用的人.

主要问题是查询性能和结构问题。事实上,我正在搜索所有 slides 的所有属性,然后限制结果使其变得如此缓慢。解决方案是首先获取我想要获取的幻灯片,然后从较短的结果集中搜索所有额外信息。

因此,最终查询(运行不到 0.2 秒并获得我希望的所有结果)如下:

SELECT 
    sl.slide_id AS slide_id,        
    sl.time_in AS time_in,
    sl.time_out AS time_out, 
    sl.duration AS duration, 
    sl.slide_order AS slide_order,
    sl.title AS title,
    sl.slide_type AS slide_type, 
    sl.report_id AS report_id, 
    sltg.tag_category_id AS tag_category_id,
    sltg.tag_value_id AS tag_value_id,
    sltgc.txt AS tag_category_text,
    sltgv.txt AS tag_value_text,
FROM (SELECT 
        sl.time_in AS time_in,
        sl.time_out AS time_out, 
        sl.duration AS duration, 
        sl.slide_order AS slide_order,
        sl.media_id AS media_id,
        sl.title AS title,
        sl.slide_type AS slide_type, 
        slrep.report_id AS report_id, 
        slrep.slide_id AS slide_id
    FROM er_slides sl 
    INNER JOIN er_slides_in_report slrep 
        ON slrep.slide_id = sl.unique_id 
            AND slrep.report_id IN (1461317308472,1461597566425,1461598458236)  
            AND slrep.deleted_date IS NULL 
    LIMIT 0, 5
) sl  
INNER JOIN er_slides_tags sltg 
    ON sltg.deleted_date IS NULL 
        AND sltg.slide_id = sl.slide_id 
INNER JOIN er_slide_tags_categories sltgc
    ON sltgc.id = sltg.tag_category_id 
INNER JOIN er_slide_tags_values sltgv
    ON sltgv.id = sltg.tag_value_id 
ORDER BY slide_id, tag_value_id;

如您所见,我在搜索其他内容之前限制了集合。之后,查询运行得非常快。我希望这种方法可以帮助别人改进他们自己的查询,以我原来的查询为例,说明你不能做什么。

原题

我有一个 table,其中包含一些元素(幻灯片),这些元素在一些容器(报告)上分组,每个元素都分配有一些属性(标签值)和类别(标签类别) .它就像一个标签系统,其中元素 X 可以具有来自类别 Z 的标签 Y。

数据库结构

CREATE TABLE IF NOT EXISTS `er_reports` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `unique_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `name` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
  `user_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `author` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
  `report_status` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'incomplete',
  `num_slides` int(4) NOT NULL DEFAULT '0',
  `report_type` varchar(25) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'report',
  `target_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_date` datetime DEFAULT NULL,
  `uploaded_date` datetime DEFAULT NULL,
  `deleted_date` datetime DEFAULT NULL,
  `modified_date` datetime DEFAULT NULL,
  `item_reference` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `is_favourite` tinyint(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_id` (`unique_id`),
  KEY `unique_id_2` (`unique_id`),
  KEY `unique_id_3` (`unique_id`),
  KEY `user_id` (`user_id`),
  KEY `deleted_date` (`deleted_date`),
  KEY `is_favourite` (`is_favourite`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=714 ;


CREATE TABLE IF NOT EXISTS `er_slides` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `unique_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
  `report_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `action_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `media_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `thumbnail_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `media_offset` int(6) NOT NULL DEFAULT '0',
  `time_in` decimal(9,3) DEFAULT NULL,
  `time_out` decimal(9,3) DEFAULT NULL,
  `duration` decimal(9,3) NOT NULL DEFAULT '10.000',
  `title` text COLLATE utf8_unicode_ci,
  `slide_comment` text COLLATE utf8_unicode_ci,
  `note_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `content` text COLLATE utf8_unicode_ci,
  `media_object` text COLLATE utf8_unicode_ci,
  `slide_type` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'action',
  `user_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `slide_order` int(4) NOT NULL DEFAULT '0',
  `count_slide` tinyint(1) NOT NULL DEFAULT '1',
  `visible` tinyint(1) NOT NULL DEFAULT '1',
  `deleted_date` datetime DEFAULT NULL,
  `created_date` datetime DEFAULT NULL,
  `uploaded_date` datetime DEFAULT NULL,
  `modified_date` datetime DEFAULT NULL,
  `item_reference` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `unique_id` (`unique_id`),
  KEY `report_id` (`report_id`),
  KEY `deleted_date` (`deleted_date`),
  KEY `action_id` (`action_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=31899 ;


CREATE TABLE IF NOT EXISTS `er_slides_in_report` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `report_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `slide_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `slide_order` int(3) NOT NULL DEFAULT '1',
  `added_date` datetime DEFAULT NULL,
  `modified_date` datetime DEFAULT NULL,
  `deleted_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `report_id` (`report_id`,`slide_id`,`deleted_date`),
  KEY `slide_order` (`slide_order`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16843 ;


CREATE TABLE IF NOT EXISTS `er_slides_tags` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `slide_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `tag_category_id` bigint(20) NOT NULL,
  `tag_value_id` bigint(20) NOT NULL,
  `created_date` datetime NOT NULL,
  `deleted_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `slide_id` (`slide_id`,`tag_category_id`,`tag_value_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=220623 ;    

CREATE TABLE IF NOT EXISTS `er_slide_tags_categories` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `txt` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `txt` (`txt`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=17 ;


CREATE TABLE IF NOT EXISTS `er_slide_tags_values` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `txt` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `txt` (`txt`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=439 ;

我试过的

SELECT @slide_num := if(@sl_id = slrep.slide_id collate utf8_unicode_ci, @slide_num, @slide_num := @slide_num+1) as slide_num,
    @sl_id := slrep.slide_id collate utf8_unicode_ci as sl_id,
    slrep.report_id AS report_id, 
    slrep.slide_id AS slide_id,
    sltg.tag_category_id AS tag_category_id,
    sltg.tag_value_id AS tag_value_id,
    sltgc.txt AS tag_category_text,
    sltgv.txt AS tag_value_text,
    sl.time_in AS time_in,
    sl.time_out AS time_out, 
    sl.duration AS duration, 
    sl.slide_order AS slide_order,
    sl.title AS title,
    sl.slide_type AS slide_type 
    FROM (SELECT @sl_id:=1, @slide_num := 0) v, er_slides sl
    INNER JOIN er_slides_in_report slrep 
        ON slrep.slide_id = sl.unique_id 
            AND slrep.report_id IN (1461317308472,1461597566425,1461598458236) 
            AND slrep.deleted_date IS NULL 
    INNER JOIN er_slides_tags sltg 
        ON sltg.deleted_date IS NULL 
            AND sltg.slide_id = sl.unique_id 
    INNER JOIN er_slide_tags_categories sltgc
        ON sltgc.id = sltg.tag_category_id 
    INNER JOIN er_slide_tags_values sltgv
        ON sltgv.id = sltg.tag_value_id 
    WHERE @slide_num >= 0 AND @slide_num <= 5
    ORDER BY slide_id, tag_value_id;

我放了一些假报告 ID 供您查看查询的构造方式。

问题

问题是这不够快 - 获取 5 张幻灯片、200 行的信息需要将近 3 秒 - 而且我无法修改 from limit。如果我写:

WHERE @slide_num >= 10 AND @slide_num <= 15

我得到一个空结果(当然,我已经检查过有足够的幻灯片)。

我也不明白为什么需要 3 秒才能得到 200 行。

我需要什么

我需要能够以最快的方式仅查询所选范围之间的那些幻灯片,这是动态的。

如果您发现缺少什么,请评论它是什么,这样我就可以 post 它。

谢谢。

编辑:解释查询(草莓方法)

正如@strawberry 所建议的,我尝试应用他的方法。但是,查询的响应时间与写入 BETWEEN 0 AND 5 的范围与写入 BETWEEN 0 AND 200 的范围相同(两者都在 17 秒左右)。

因为这可能是因为错误的索引,我决定在这里写 EXPLAIN,因为我看不到任何错误的索引(WHERE 子句中的每个条件都有它的指数)。

考虑这个简化的例子...

DROP TABLE IF EXISTS slides;

CREATE TABLE slides 
(slide_id INT NOT NULL);

INSERT INTO slides VALUES
(1),
(2),
(4),
(5),
(6),
(7);

DROP TABLE IF EXISTS slides_tags;

CREATE TABLE slides_tags
(slide_id INT NOT NULL
,tag_id INT NOT NULL
,PRIMARY KEY(slide_id,tag_id)
);

INSERT INTO slides_tags VALUES
(1,101),
(1,103),
(1,105),
(1,107),
(2,102),
(2,104),
(2,106),
(2,108),
(4,105),
(4,110),
(5,101);

SELECT slide_id
     , tag_id 
     , i
  FROM 
     ( SELECT s.*
            , st.tag_id
            , CASE WHEN @prev = s.slide_id THEN @i:=@i ELSE @i:=@i+1 END i
            , @prev:=s.slide_id 
         FROM slides s 
         LEFT 
         JOIN slides_tags st 
           ON st.slide_id = s.slide_id 
         JOIN 
            ( SELECT @prev:=null,@i:=0) vars 
        ORDER 
           BY s.slide_id
     ) x 
 WHERE i BETWEEN 3 AND 5;
+----------+--------+------+
| slide_id | tag_id | i    |
+----------+--------+------+
|        4 |    105 |    3 |
|        4 |    110 |    3 |
|        5 |    101 |    4 |
|        6 |   NULL |    5 |
+----------+--------+------+

为了清楚起见,我在结果中包含了 i 列。当然,如果不需要,从超级查询中省略。

编辑:

看来您可以按如下方式重写此查询,但老实说,我对为什么这样做感到困惑:

SELECT s.slide_id 
     , st.tag_id
     , CASE WHEN @prev = s.slide_id THEN @i:=@i ELSE @i:=@i+1 END i
     , @prev:=s.slide_id 
  FROM (SELECT @i:=0, @prev := 0) vars
  JOIN slides s
  LEFT
  JOIN slides_tags st
    ON st.slide_id = s.slide_id
 HAVING i BETWEEN 3 AND 5
 ORDER 
    BY slide_id
     , tag_id;

+----------+--------+------+-------------------+
| slide_id | tag_id | i    | @prev:=s.slide_id |
+----------+--------+------+-------------------+
|        4 |    105 |    3 |                 4 |
|        4 |    110 |    3 |                 4 |
|        5 |    101 |    4 |                 5 |
|        6 |   NULL |    5 |                 6 |
+----------+--------+------+-------------------+