2

Update: The where clauses are the same. The results are as outlined below.

The following query runs within 0.1s and returns 0 results.

SELECT id from content WHERE compliance_overridden = 1 AND compliance_overridden_date < DATE_SUB(NOW(), INTERVAL 30 DAY);

Hence I would expect the following query to return within the same time (as it doesn't actually have to update anything).

UPDATE content SET compliance_overridden = 0 WHERE compliance_overridden = 1 AND compliance_overridden_date < DATE_SUB(NOW(), INTERVAL 30 DAY);

It is taking a few minutes to complete. enter image description here a) Why?

Using EXPLAIN shows that the first query is not using any indexes (key=None) but the second query is (key=PRIMARY). That confuses me even more.

SELECT query UPDATE query

b) Why does one query use an index, but not the other? Maybe MySQL selects the rows first and then updates by primary key? I have tried to run the two queries separately (select ids, then update) and both are fast.

c) Why would the second query be slower when using the PRIMARY index over no index?

PS: I have tried adding USE INDEX (PRIMARY) to the first query and IGNORE INDEX (PRIMARY) to the second query, both have no effect on query times.

PPS: Using SQL_NO_CACHE for the first query has no effect on query times either.

Engine: Aurora MySQL 5.6.10a

SHOW CREATE TABLE content;

CREATE TABLE `content` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content_key` char(40) NOT NULL,
  `body` mediumtext,
  `original_body` mediumtext,
  `meta` mediumblob,
  `status` enum('approved','deleted','draft','library','flagged','in progress','pending','published','publishing error','queued','ready','retracted','scheduled','removed','for review','reviewed','disabled') NOT NULL DEFAULT 'in progress',
  `is_flagged` int(11) NOT NULL DEFAULT '0',
  `extraction_status` enum('queued','processing','processed','processing error') DEFAULT NULL,
  `type` enum('internal','external','library') NOT NULL DEFAULT 'internal',
  `compliance_status` enum('ok','error','warning','unchecked','fail','overridden-ok','overridden-error','queued') DEFAULT 'unchecked' COMMENT 'Status when content is reviewed against guidelines',
  `queued_for_compliance` tinyint(4) NOT NULL DEFAULT '0',
  `queued_date` timestamp NULL DEFAULT NULL,
  `review_retries` int(11) NOT NULL,
  `review_notes` varchar(255) DEFAULT NULL,
  `needs_review` tinyint(1) DEFAULT '0' COMMENT 'Indicates that the content items needs the compliance reviewed to be checked',
  `pushed_for_review` enum('no','yes','error') NOT NULL DEFAULT 'no' COMMENT 'Flags if content is pushed to Minerva.',
  `risk_score` tinyint(1) DEFAULT '0',
  `keywords` text,
  `track` varchar(60) DEFAULT NULL,
  `purpose` enum('arbitrary','compliance_file_check','compliance_text_check','document','promotion','reply','retweet','site','site_post','soa','status_update','url') NOT NULL DEFAULT 'site' COMMENT 'Changed to an enum with applicable values. This will allow us to optimise queries on this column.',
  `purpose_note` text COMMENT 'Optional note explaining the purpose of this item',
  `format` varchar(20) DEFAULT NULL,
  `media` varchar(255) DEFAULT NULL,
  `media_alternate` varchar(255) DEFAULT NULL,
  `mime_type` varchar(255) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `tag_id` int(11) DEFAULT NULL,
  `email_template_id` int(11) DEFAULT NULL,
  `social_account_id` int(11) DEFAULT NULL,
  `target_social_account_id` int(11) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `notes` text,
  `options` text,
  `compliance_notes` text,
  `notified` tinyint(1) NOT NULL COMMENT 'Indicates if legal have been notified of the content change',
  `publish_immediately` tinyint(1) DEFAULT '0',
  `score` int(11) DEFAULT NULL,
  `insights` text,
  `publish_date` timestamp NULL DEFAULT NULL,
  `date_published` timestamp NULL DEFAULT NULL,
  `external_id` varchar(40) DEFAULT NULL,
  `feed_id` int(11) DEFAULT NULL,
  `site_id` int(11) DEFAULT NULL,
  `client_id` int(11) DEFAULT NULL,
  `has_changed` tinyint(1) DEFAULT '0' COMMENT 'Indicates if the content has ever changed',
  `was_flagged` tinyint(4) NOT NULL DEFAULT '0',
  `last_flagged` timestamp NULL DEFAULT NULL COMMENT 'Date content was last flagged',
  `compliance_overridden` tinyint(4) NOT NULL DEFAULT '0',
  `compliance_overridden_date` timestamp NULL DEFAULT NULL,
  `was_risky` tinyint(1) DEFAULT '0' COMMENT 'Indicates if the content was ever risky',
  `last_risky` timestamp NULL DEFAULT NULL COMMENT 'Date content was last assigned risk',
  `reference_urls` enum('none','ok','unchecked','error') NOT NULL DEFAULT 'none',
  `screenshot_url` text,
  `rescan` enum('no','manual-queued','compliance-changed','compliance-unchanged','compliance-unchecked') NOT NULL DEFAULT 'no',
  `rescan_rules` varchar(50) DEFAULT NULL,
  `applicable_rules` text COMMENT 'Applicable rules for a review',
  `lsh_hash` char(70) DEFAULT NULL,
  `comment_count` int(11) NOT NULL DEFAULT '0',
  `modified` timestamp NULL DEFAULT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `content_key` (`content_key`),
  KEY `fk_content_user` (`user_id`),
  KEY `fk_content_content` (`parent_id`),
  KEY `fk_content_email_template` (`email_template_id`),
  KEY `fk_content_target_social_account` (`social_account_id`),
  KEY `fk_content_feed` (`feed_id`),
  KEY `fk_content_site` (`site_id`),
  KEY `title` (`title`),
  KEY `status` (`status`),
  KEY `type` (`type`),
  KEY `needs_review` (`needs_review`),
  KEY `compliance_status` (`compliance_status`),
  KEY `risk_score` (`risk_score`),
  KEY `purpose` (`purpose`),
  KEY `extraction_status_idx` (`extraction_status`),
  KEY `client_id` (`client_id`),
  KEY `is_flagged_idx` (`is_flagged`),
  KEY `purpose_idx` (`purpose`),
  FULLTEXT KEY `content_index` (`title`,`body`),
  CONSTRAINT `content_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `client` (`id`),
  CONSTRAINT `fk_content_content` FOREIGN KEY (`parent_id`) REFERENCES `content` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_content_feed` FOREIGN KEY (`feed_id`) REFERENCES `feed` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_content_site` FOREIGN KEY (`site_id`) REFERENCES `site` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_content_social_account` FOREIGN KEY (`social_account_id`) REFERENCES `social_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_content_target_social_account` FOREIGN KEY (`social_account_id`) REFERENCES `social_account` (`id`),
  CONSTRAINT `fk_content_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=247236 DEFAULT CHARSET=utf8
1
  • Comments on this question have been moved to chat. Please continue there.
    – Paul White
    Mar 8, 2018 at 8:39

3 Answers 3

2

In the update statement, MySQL has to compute "date_sub(created, interval 30 day)" for each row, and compare it with compliance_overridden_date. In the select, it only has to compute date_sub(now(), interval 30 day) once. Your where-clause does not allow MySQL to use an index to select the rows to be modified. And since all rows must be read, it's better to scan the table than skipping between index and data. I have no explanation for the "PRIMARY" in the explain. Your guess is better than mine.

5
  • 1
    PRIMARY in the output could be a bug. But technically it does use the primary key - because at the physical level, the PK is the table. But it doesn't take advantage of the primary key. Mar 7, 2018 at 8:18
  • "at the physical level, the PK is the table"?? I'm curious what you mean by that. Mar 7, 2018 at 8:20
  • In this case, MySQL should have no use for the PK at all. He's scanning the table, no data will be modified that would cause a row to be moved, if a modification is needed, MySQL is already at the right spot, ... My imagination fails me. A bug in the explain is certainly possible. It happens with others. Mar 7, 2018 at 8:25
  • My bad, the where clauses are exactly the same. The results are too unfortunately. Is MySQL still computing DATE_SUB(NOW(), INTERVAL 30 DAY) once per row maybe?
    – kev
    Mar 7, 2018 at 23:22
  • @GerardH.Pille for the tables that use InnoDB engine, the primary index is indeed the table. The structure is similar to SQL Server's clustered indexes. Mar 7, 2018 at 23:27
1

This query doesn't take advantage of the primary key in any way. The number of rows examined (about 160K rows) shows this. So your assumption that your UPDATE does nothing is incorrect: it examines several rows.

The reason why your SELECT is faster is pretty clear. This expression is computed once, at the beginning of query execution:

DATE_SUB(NOW(), INTERVAL 30 DAY) 

But this one is computer of each row (and is the reason why this query cannot use an index):

DATE_SUB(created, INTERVAL 30 DAY);

Why does EXPAIN show PRIMARY? I'm not sure. But technically, whenever you don't use a secondary index, you always use the primary key - because data are stored in the primary key, just not in a particular order.

0
INDEX(compliance_overridden, compliance_overridden_date)

show make both queries faster.

Better yet would be to get rid of the flag (overridden) and simply have NULL in the date mean that it is not "overridden". And then have

INDEX(compliance_overridden_date)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.