我有以下查询,该查询需要1.141秒才能执行。该查询具有一堆联接。有没有一种方法可以优化查询?任何帮助都将受到赞赏。
SELECT `cou`.`id` AS `country_id`,
`a`.`id` AS `area_id`,
`y`.`id` AS `year_id`,
`su`.`id` AS `subject_id`,
`co1`.`name` AS `course_name`,
`ca1`.`id` AS `root_category_id`,
`ca1`.`name` AS `root_category_name`,
`ca4`.`id` AS `chapter_id`,
`ca4`.`name` AS `chapter_name`,
`ca4`.`no_of_assets` AS `no_of_assets`,
`ca4`.`active_status` AS `status`,
0 AS `READ_IT`,
0 AS `WATCH_IT`,
0 AS `PLAY_IT`,
0 AS `PROVE_IT`,
count(DISTINCT `pa`.`id`) AS `APROVE_IT`,
if((count(`pa`.`id`) > 0),'True', 'False') AS `sections_with_content`,
count(`pa`.`id`) AS `content_count`,
`pa`.`status` AS `content_flag`
FROM (((((((((((((((((`edu_db`.`category_relation_xref` `crx1`
JOIN `edu_db`.`category` `ca1` on((`crx1`.`parent_id` = `ca1`.`id`)))
LEFT JOIN `edu_db`.`course` `co1` on((`ca1`.`course_id` = `co1`.`id`)))
JOIN `edu_db`.`category_relation_xref` `crx2` on((`crx1`.`child_id` = `crx2`.`parent_id`)))
JOIN `edu_db`.`category` `ca2` on((`crx2`.`parent_id` = `ca2`.`id`)))
JOIN `edu_db`.`category` `ca3` on((`crx2`.`child_id` = `ca3`.`id`)))
JOIN `edu_db`.`category_relation_xref` `crx3` on((`crx2`.`child_id` = `crx3`.`parent_id`)))
JOIN `edu_db`.`category` `ca4` on((`crx3`.`child_id` = `ca4`.`id`)))
LEFT JOIN `edu_db`.`category_relation_xref` `crx4` on((`crx3`.`child_id` = `crx4`.`parent_id`)))
LEFT JOIN `edu_db`.`category` `ca5` on((`crx4`.`child_id` = `ca5`.`id`)))
JOIN `edu_db`.`course` `co2` on((`ca4`.`course_id` = `co2`.`id`)))
JOIN `edu_db`.`curriculum` `cu` on((`co2`.`curriculum_id` = `cu`.`id`)))
JOIN `edu_db`.`year` `y` on((`cu`.`year_id` = `y`.`id`)))
JOIN `edu_db`.`subject` `su` on((`su`.`id` = `cu`.`subject_id`)))
JOIN `edu_db`.`area` `a` on((`y`.`area_id` = `a`.`id`)))
JOIN `edu_db`.`country` `cou` on((`a`.`country_id` = `cou`.`id`)))
LEFT JOIN `edu_db`.`qbnk_category_published_assessment_xref` `qcpa` on((`ca4`.`id` = `qcpa`.`category_id`)))
LEFT JOIN `edu_db`.`qbnk_published_assessment` `pa` on((`qcpa`.`published_assessment_id` = `pa`.`id`)))
WHERE ((`pa`.`status` <> 'non_active')
AND (`qcpa`.`status` <> 'deleted'))
GROUP BY `ca4`.`id`
这是explain命令的输出。在这里有一个使用文件排序的选择类型,这意味着查询不使用索引。有没有一种使用索引优化此查询的方法?
+------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+
| "id" | "select_type" | "table" | "partitions" | "type" | "possible_keys" | "key" | "key_len" | "ref" | "rows" | "filtered" | "Extra" |
+------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+
| "1" | "SIMPLE" | "pa" | \N | "index" | "PRIMARY,status" | "status" | "2" | \N | "7714" | "50.00" | "Using where; Using index; Using temporary; Using filesort" |
| "1" | "SIMPLE" | "qcpa" | \N | "ref" | "PRIMARY,FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id,cat_pub_status" | "PRIMARY" | "4" | "edu_db.pa.id" | "6" | "50.00" | "Using where" |
| "1" | "SIMPLE" | "ca4" | \N | "eq_ref" | "PRIMARY,FK_curriculum_item_id_category_tbl_to_id_curriculum_item_tbl,FK_curriculum_id_category_tbl_to_id_curriculum_tbl,name,Fk_category_tbl_course_id_to_course_tbl_id,Index 6,Index 7" | "PRIMARY" | "4" | "edu_db.qcpa.category_id" | "1" | "100.00" | "Using where" |
| "1" | "SIMPLE" | "co2" | \N | "eq_ref" | "PRIMARY,FK_curriculum_id_to_id_curriculum_tbl" | "PRIMARY" | "4" | "edu_db.ca4.course_id" | "1" | "100.00" | "Using where" |
| "1" | "SIMPLE" | "cu" | \N | "eq_ref" | "PRIMARY,FK_subject_id_to_id_subject_tbl,FK_year_id_to_id_year_tble" | "PRIMARY" | "4" | "edu_db.co2.curriculum_id" | "1" | "100.00" | "Using where" |
| "1" | "SIMPLE" | "su" | \N | "eq_ref" | "PRIMARY" | "PRIMARY" | "4" | "edu_db.cu.subject_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "y" | \N | "eq_ref" | "PRIMARY,FK_year_tbl_area_id_to_id_area_tbl" | "PRIMARY" | "4" | "edu_db.cu.year_id" | "1" | "100.00" | "Using where" |
| "1" | "SIMPLE" | "a" | \N | "eq_ref" | "PRIMARY,FK_country_id_to_country_tbl" | "PRIMARY" | "4" | "edu_db.y.area_id" | "1" | "100.00" | \N |
| "1" | "SIMPLE" | "cou" | \N | "eq_ref" | "PRIMARY" | "PRIMARY" | "4" | "edu_db.a.country_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "crx3" | \N | "ref" | "PRIMARY,FK_child_id_to_id_category_tbl" | "FK_child_id_to_id_category_tbl" | "4" | "edu_db.qcpa.category_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "ca3" | \N | "eq_ref" | "PRIMARY,Index 6,Index 7" | "PRIMARY" | "4" | "edu_db.crx3.parent_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "crx2" | \N | "ref" | "PRIMARY,FK_child_id_to_id_category_tbl" | "FK_child_id_to_id_category_tbl" | "4" | "edu_db.crx3.parent_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "ca2" | \N | "eq_ref" | "PRIMARY,Index 6,Index 7" | "PRIMARY" | "4" | "edu_db.crx2.parent_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "crx1" | \N | "ref" | "PRIMARY,FK_child_id_to_id_category_tbl" | "FK_child_id_to_id_category_tbl" | "4" | "edu_db.crx2.parent_id" | "1" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "ca1" | \N | "eq_ref" | "PRIMARY,Index 6,Index 7" | "PRIMARY" | "4" | "edu_db.crx1.parent_id" | "1" | "100.00" | \N |
| "1" | "SIMPLE" | "co1" | \N | "eq_ref" | "PRIMARY" | "PRIMARY" | "4" | "edu_db.ca1.course_id" | "1" | "100.00" | \N |
| "1" | "SIMPLE" | "crx4" | \N | "ref" | "PRIMARY" | "PRIMARY" | "4" | "edu_db.qcpa.category_id" | "4" | "100.00" | "Using index" |
| "1" | "SIMPLE" | "ca5" | \N | "eq_ref" | "PRIMARY,Index 6,Index 7" | "PRIMARY" | "4" | "edu_db.crx4.child_id" | "1" | "100.00" | "Using index" |
+------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+
以下是 category_relation_xref 和 qbnk_category_published_assesssment_xref 表的创建代码
CREATE TABLE `category_relation_xref` (
`parent_id` INT(11) NOT NULL,
`child_id` INT(11) NOT NULL,
`template_id` INT(11) NOT NULL DEFAULT '1',
`possition_id` INT(11) NOT NULL DEFAULT '1',
`comment` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf16_unicode_ci',
`display_order` INT(11) NOT NULL DEFAULT '0',
`created_at` TIMESTAMP NULL DEFAULT NULL,
`updated_at` TIMESTAMP NULL DEFAULT NULL,
`created_by` INT(11) NULL DEFAULT NULL,
`updated_by` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`parent_id`, `child_id`),
INDEX `FK_child_id_to_id_category_tbl` (`child_id`),
INDEX `FK_cat_rel_xref_tbl_template_id_to_content_template_tbl` (`template_id`),
INDEX `FK_cat_rel_xref_tbl_possition_id_to_content_template_tbl_id` (`possition_id`),
CONSTRAINT `FK_cat_rel_xref_tbl_possition_id_to_content_template_tbl_id` FOREIGN KEY (`possition_id`) REFERENCES `content_possition` (`id`),
CONSTRAINT `FK_cat_rel_xref_tbl_template_id_to_content_template_tbl` FOREIGN KEY (`template_id`) REFERENCES `content_template` (`id`),
CONSTRAINT `category_relation_xref_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `category` (`id`),
CONSTRAINT `category_relation_xref_ibfk_2` FOREIGN KEY (`child_id`) REFERENCES `category` (`id`)
)
COMMENT='store parent child relations'
COLLATE='utf16_unicode_ci'
ENGINE=InnoDB
;
CREATE TABLE `qbnk_category_published_assessment_xref` (
`published_assessment_id` INT(11) NOT NULL,
`category_id` INT(11) NOT NULL,
`status` ENUM('active','deleted') NOT NULL DEFAULT 'active' COLLATE 'utf16_unicode_ci',
`comment` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf16_unicode_ci',
`created_at` TIMESTAMP NULL DEFAULT NULL,
`updated_at` TIMESTAMP NULL DEFAULT NULL,
`created_by` INT(11) NULL DEFAULT '0',
`updated_by` INT(11) NULL DEFAULT '0',
PRIMARY KEY (`published_assessment_id`, `category_id`),
INDEX `FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id` (`category_id`),
INDEX `cat_pub_status` (`status`),
CONSTRAINT `FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE CASCADE,
CONSTRAINT `FK_qbnk_cat_pub_ass_id_to_pub_ass_tbl_id` FOREIGN KEY (`published_assessment_id`) REFERENCES `qbnk_published_assessment` (`id`) ON DELETE CASCADE
)
COMMENT='Store category published assessments mappings'
COLLATE='utf16_unicode_ci'
ENGINE=InnoDB
;
答案 0 :(得分:2)
清理查询,除去不必要的井字号和括号后,我相信下面的内容很容易理解,可以直接查看表之间的关系。通过leftAlias.leftColumn = rightAlias.rightColumn的联接,您做得很好。
现在,我可以更好地了解情况了,请考虑一下您的桌子。它们似乎大部分是查找表,您在其中具有ID和要返回的描述性列。我将创建覆盖这些表上的索引的内容,以便可以直接从索引处理联接解析,而不用转到原始数据页面。
在时间上的另一个考虑因素是添加MySQL关键字“ STRAIGHT_JOIN”,该关键字告诉引擎按照我列出的顺序查询表。不要为我思考。您的每个表都从最高级别开始,并在下游进行所有查找。您还可以通过删除STRAIGHT_JOIN子句来比较时间。在我以前使用YEARS的系统上,经过12个多小时的处理后,它挂起了具有约1500万条记录和20多个查找表的主表,并减少为一个完整的表。查询运行了一个多小时。
原始查询已清理
SELECT STRAIGHT_JOIN
cou.id AS country_id,
a.id AS area_id,
y.id AS year_id,
su.id AS subject_id,
co1.`name` AS course_name,
ca1.id AS root_category_id,
ca1.`name` AS root_category_name,
ca4.id AS chapter_id,
ca4.`name` AS chapter_name,
ca4.no_of_assets AS no_of_assets,
ca4.active_status AS `status`,
0 AS READ_IT,
0 AS WATCH_IT,
0 AS PLAY_IT,
0 AS PROVE_IT,
count(DISTINCT pa.id) AS APROVE_IT,
if((count(pa.id) > 0),'True', 'False') AS sections_with_content,
count(pa.id) AS content_count,
pa.`status` AS content_flag
FROM
edu_db.category_relation_xref crx1
JOIN edu_db.category ca1
ON crx1.parent_id = ca1.id
LEFT JOIN edu_db.course co1
ON ca1.course_id = co1.id
JOIN edu_db.category_relation_xref crx2
ON crx1.child_id = crx2.parent_id
JOIN edu_db.category ca2
ON crx2.parent_id = ca2.id
JOIN edu_db.category ca3
ON crx2.child_id = ca3.id
JOIN edu_db.category_relation_xref crx3
ON crx2.child_id = crx3.parent_id
JOIN edu_db.category ca4
ON crx3.child_id = ca4.id
JOIN edu_db.course co2
ON ca4.course_id = co2.id
JOIN edu_db.curriculum cu
ON co2.curriculum_id = cu.id
JOIN edu_db.`year` y
ON cu.year_id = y.id
JOIN edu_db.area a
ON y.area_id = a.id
JOIN edu_db.country cou
ON a.country_id = cou.id
JOIN edu_db.subject su
ON cu.subject_id = su.id
LEFT JOIN edu_db.qbnk_category_published_assessment_xref qcpa
ON ca4.id = qcpa.category_id
LEFT JOIN edu_db.qbnk_published_assessment pa
ON qcpa.published_assessment_id = pa.id
LEFT JOIN edu_db.category_relation_xref crx4
ON crx3.child_id = crx4.parent_id
LEFT JOIN edu_db.category ca5
ON crx4.child_id = ca5.id
WHERE
pa.`status` <> 'non_active'
AND qcpa.`status` <> 'deleted'
GROUP BY
ca4.id
另一个项目...您有一些表已左连接,甚至没有在查询中使用,并且可以完全删除。明确显示为“ LEFT JOIN edu_db.category ca5”。您没有从CA5别名中提取任何值,左连接表示您根本不在乎它。对于“ LEFT JOIN edu_db.category_relation_xref crx4”,同样如此
SELECT STRAIGHT_JOIN
a.country_id,
a.id AS area_id,
y.id AS year_id,
cu.subject_id,
co1.`name` AS course_name,
ca1.id AS root_category_id,
ca1.`name` AS root_category_name,
ca4.id AS chapter_id,
ca4.`name` AS chapter_name,
ca4.no_of_assets AS no_of_assets,
ca4.active_status AS `status`,
0 AS READ_IT,
0 AS WATCH_IT,
0 AS PLAY_IT,
0 AS PROVE_IT,
count(DISTINCT pa.id) AS APROVE_IT,
if((count(pa.id) > 0),'True', 'False') AS sections_with_content,
count(pa.id) AS content_count,
pa.`status` AS content_flag
FROM
edu_db.category_relation_xref crx1
JOIN edu_db.category ca1
ON crx1.parent_id = ca1.id
LEFT JOIN edu_db.course co1
ON ca1.course_id = co1.id
JOIN edu_db.category_relation_xref crx2
ON crx1.child_id = crx2.parent_id
JOIN edu_db.category_relation_xref crx3
ON crx2.child_id = crx3.parent_id
JOIN edu_db.category ca4
ON crx3.child_id = ca4.id
JOIN edu_db.course co2
ON ca4.course_id = co2.id
JOIN edu_db.curriculum cu
ON co2.curriculum_id = cu.id
JOIN edu_db.`year` y
ON cu.year_id = y.id
JOIN edu_db.area a
ON y.area_id = a.id
JOIN edu_db.qbnk_category_published_assessment_xref qcpa
ON ca4.id = qcpa.category_id
AND qcpa.`status` <> 'deleted'
JOIN edu_db.qbnk_published_assessment pa
ON qcpa.published_assessment_id = pa.id
AND pa.`status` <> 'non_active'
GROUP BY
ca4.id
与“ pa”和“ qcpa”相关联的WHERE子句会抵消LEFT JOIN部分,因为where将其变为WHERE子句。因此,我删除了“ LEFT”组件,并将where子句部分直接移至该连接组件。
您正在拉“主题”表(别名su),但仅抓住su.id。由于您具有来自“ cu”别名的主题ID,因此您可以仅使用“ cu.subject_id”并从查询中删除另一个表-除非您计划从主题表中获取其他描述。您所在的国家/地区,年份加入情况也可能相同。如果您已经拥有先前表格中的ID,请使用该ID并删除不需要的内容...
不要将“ ca2”或“ ca3”别名用于任何潜在的额外详细信息,描述,而不必使用它。
因此,我对每个表的索引建议将包括以下内容。这些将是更多的COVERING索引。这些不应该是同一表上的单个索引,例如ID上的Tbl1索引,Description上的Tbl1索引,而Tbl1索引应作为单个索引打开(id,description)。
table index
qbnk_published_assessment ( id, `status` )
qbnk_category_published_assessment_xref ( category_id, `status`, published_assessment_id )
area ( id )
`year` ( id, area_id )
curriculum ( id, year_id )
course ( id, curriculum_id )
category ( id, course_id )
category_relation_xref ( parent_id, child_id )
答案 1 :(得分:0)
由于EXPLAIN以pa
和ocpa
表开头,因此我们可以看到优化器在执行其他任何操作之前先尝试过滤结果。这很聪明,但是另一个聪明的方法是先执行GROUP BY
,然后过滤结果。
考虑到这一点,让我们尝试帮助优化器做到这一点,看看是否有任何改进:
ALTER TABLE qbnk_category_published_assessment_xref
ADD INDEX so52589130_qcpa (`status`,`category_id`,`published_assessment_id`)
上面的索引允许从category
表的GROUP BY
开始,然后使用WHERE
列上的status
子句与category_id
进行匹配该索引最左边的两列。这样就可以立即访问published_assessment_id
列,而无需进行辅助查找。
首先不尝试执行此操作的原因是,qbnk_category_published_assessment_xref
上的索引不允许在没有辅助查找的情况下进行操作。
cat_pub_status
允许使用status
,但随后在published_assessment_id
之前使用category_id
,这不允许它使用category_id
来快速找到所需的记录。
FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id
允许首先使用category_id
,但随后仍必须返回到聚集索引以获取status
。
我们的新索引仅允许一次访问该表,这将有助于节省时间。
与两个WHERE
值为<>
相比,这两个=
子句为status
的事实可能会稍微降低性能。如果每个=
列只有两个状态值,则我将其交换为使用EXPLAIN
。但是,如果有两个以上的值,那并不是太重要。
因此,尝试上述索引。运行两次,丢弃第一个计时结果,因为缓冲区高速缓存将在该调用上加载。第二次答复,并且EXPLAIN计划是否更改。如果EXPLAIN计划发生了变化,但时间安排还不够快,请将新的construir_cruz()
计划添加到您的问题中,我会再看看。 (那时我可能还需要更多的表定义,但是我们将拭目以待,首先看看它是如何实现的。)
答案 2 :(得分:0)
我称此为“过度标准化”。
country
不需要额外的表-所有国家/地区的CHAR(2) CHARACTER SET ascii
值都非常好。请注意,INT
是4个字节,因此此更改节省了空间!
这是数据类型YEAR
;用它。如果您有完整的日期或日期时间,请使用DATE
或DATETIME
,然后从YEAR
中选择SELECT
。 YEAR
小于INT
。
了解ENUM
作为可能值较小的列的可能数据类型。
等