Henrique Barcelos
Henrique Barcelos

Reputation: 7900

Index not being used for sort in joined view

I have the following schema:

CREATE TABLE `news` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `news_category_id` int(10) unsigned NOT NULL,
  `news_type_id` int(10) unsigned NOT NULL,
  `news_pictures_main_id` int(10) unsigned DEFAULT NULL,
  `title` tinytext COLLATE latin1_general_ci,
  `body` text COLLATE latin1_general_ci,
  `tmstp` timestamp NULL DEFAULT NULL,
  `subcategory` varchar(64) COLLATE latin1_general_ci DEFAULT NULL,
  `source` varchar(128) COLLATE latin1_general_ci DEFAULT NULL,
  `old_id` int(10) unsigned DEFAULT NULL,
  `tags` text COLLATE latin1_general_ci,
  PRIMARY KEY (`id`),
  KEY `news_time_idx` (`tmstp`),
  KEY `fk_news_news_pictures1` (`news_pictures_main_id`),
  KEY `fk_news_news_category1` (`news_category_id`),
  KEY `fk_news_news_type1` (`news_type_id`),
  CONSTRAINT `fk_news_news_category1` FOREIGN KEY (`news_category_id`) REFERENCES `news_category` (`id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_news_news_pictures1` FOREIGN KEY (`news_pictures_main_id`) REFERENCES `news_pictures` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `fk_news_news_type1` FOREIGN KEY (`news_type_id`) REFERENCES `news_type` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB

CREATE TABLE `news_pictures` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `path` text COLLATE latin1_general_ci,
  `description` text COLLATE latin1_general_ci,
  `author` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
  `news_id` int(10) unsigned DEFAULT NULL,
  `temp_id` varchar(40) COLLATE latin1_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `filename_old_id_unq` (`path`(20),`temp_id`(6)),
  KEY `fk_news_pictures_news1` (`news_id`),
  KEY `temp_id_idx` (`temp_id`(8)),
  CONSTRAINT `fk_news_pictures_news1` FOREIGN KEY (`news_id`) REFERENCES `news` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB

CREATE TABLE `news_category` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

CREATE TABLE `news_type` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
  `slug` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
  KEY `news_type_slug_idx` (`slug`)
) ENGINE=InnoDB

From that, there is derived the following view:

CREATE OR REPLACE VIEW `news_full` AS select `n`.`id` AS `id`,
`n`.`title` AS `title`,
`n`.`body` AS `body`,
`n`.`tmstp` AS `tmstp`,
`n`.`subcategory` AS `subcategory`,
`n`.`source` AS `source`,
`n`.`old_id` AS `old_id`,
`n`.`news_type_id` AS `news_type_id`,
`n`.`tags` AS `tags`,
`nt`.`name` AS `news_type_name`,
`nt`.`slug` AS `news_type_slug`,
`n`.`news_pictures_main_id` AS `news_pictures_main_id`,
`np`.`path` AS `news_pictures_main_path`,
`np`.`description` AS `news_pictures_main_description`,
`np`.`author` AS `news_pictures_main_author`,
`np`.`temp_id` AS `news_pictures_main_temp_id`,
`n`.`news_category_id` AS `news_category_id`,
`nc`.`name` AS `news_category_name` 
from (((`news` `n` 
            left join `news_pictures` `np` on((`n`.`news_pictures_main_id` = `np`.`id`))) 
        join `news_category` `nc` on((`n`.`news_category_id` = `nc`.`id`))) 
    join `news_type` `nt` on((`n`.`news_type_id` = `nt`.`id`)));

However, if I try to run the following query:

select * from news_full order by tmstp limit 100

I get the following execution plan (please click on the image to expand it):

Explain result

Notice the Using temporary; Using filesort field in the first step. But this is weird, because tmstp field is indexed on the base table.

First I thought this was due the left join on the view, but I've changed it to inner join and I got the same results.


Edit

As @Michael-sqlbot cleverly noticed, the query optimizer is inverting the order of the base tables, putting news_category (nc) first.

If I change the query that creates the view to use only LEFT JOINs it seems to work:

New query plan

The execution times, as expected, as blatantly different: Query comparison

Not satisfied, I created another view with the original query, adding the STRAIGHT_JOIN statement. So, the query plan comes as follows:

Query plan - straight join

So, it's not using the index.

However, if I run the plan for the base query adding the same ORDER BY and LIMIT clauses, it does uses the index:

Query plan - straight join no view

Upvotes: 0

Views: 40

Answers (1)

Rick James
Rick James

Reputation: 142316

(Not an answer, but some other issues to bring up...)

UNIQUE KEY `filename_old_id_unq` (`path`(20),`temp_id`(6))
  • That constrains the first 20 characters of path, together with the first 6 characters of temp_id to be unique across the table. Did you really want that?
  • I suspect the optimizer will never use both columns of that index. (In general, prefixing is useless.)

And...

`title` tinytext COLLATE latin1_general_ci

Change to VARCHAR(255). There are disadvantages of TINYTEXT and perhaps no advantages.

Upvotes: 1

Related Questions