Reputation: 11
Good day. Why date modification in a subquery reduces performance?
This request is fast (0.015 sec):
SELECT p.id, p.name, s.value
FROM points p
LEFT JOIN settings s ON p.id = s.point_id AND s.type_id = 1
WHERE p.parent_id = 1310
AND NOT EXISTS (SELECT 1 FROM events e WHERE e.point_id = p.id AND e.start_at > '2019-09-09 20:00:00')
UPD Explain:
'1', 'PRIMARY', 'p', 'ref', 'idx_parent_id_type', 'idx_parent_id_type', '5', 'const', '29', 'Using where'
'1', 'PRIMARY', 's', 'ref', 'p_id_idx,sid_idx', 'p_id_idx', '4', 'rm_api.p.id', '1', 'Using where'
'2', 'MATERIALIZED', 'e', 'range', 'idx_start_at_end_at,point', 'idx_start_at_end_at', '5', NULL, '3855', 'Using index condition'
This request is slow (~ 18 sec):
SELECT p.id, p.name, s.value
FROM points p
LEFT JOIN settings s ON p.id = s.point_id AND s.type_id = 1
WHERE p.parent_id = 1310
AND NOT EXISTS (SELECT 1 FROM events e WHERE e.point_id = p.id AND e.start_at > DATE_ADD('2019-09-09 20:00:00', INTERVAL COALESCE(s.value, 0) MINUTE))
UPD Explain:
1 PRIMARY p ref idx_parent_id_type idx_parent_id_type 5 const 29
1 PRIMARY s ref p_id_idx,sid_idx p_id_idx 4 rm_api.p.id 1 Using where
2 DEPENDENT SUBQUERY e ref idx_start_at_end_at,point point 4 rm_api.p.id 478 Using index condition; Using where
What can be done? I doubt that I am using s.value correctly in the subquery
p.s. this request is also fast:
SELECT p.id, p.name, s.value
FROM points p
LEFT JOIN settins s ON p.id = s.point_id AND s.type_id = 1
WHERE p.parent_id = 1310
AND NOT EXISTS (SELECT 1 FROM events e WHERE e.point_id = p.id AND e.start_at > DATE_ADD('2019-09-09 20:00:00', INTERVAL 15 MINUTE))
UPD
Table points
:
CREATE TABLE `points` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(160) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Не указано',
`parent_id` int(10) unsigned DEFAULT 1,
`type` int(11) NOT NULL DEFAULT 0,
`status` int(11) NOT NULL DEFAULT 1,
`created_at` timestamp NOT NULL DEFAULT '1970-12-31 09:00:00',
`updated_at` timestamp NOT NULL DEFAULT '1970-12-31 09:00:00',
`capacity` int(11) NOT NULL DEFAULT 0,
`is_building` int(11) DEFAULT 0,
`is_ews_linked` int(11) NOT NULL DEFAULT 0,
`ews_resource_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`map_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`other` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`human_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`booking_max_duration` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_parent_id_type` (`parent_id`,`type`)
) ENGINE=InnoDB AUTO_INCREMENT=2958 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Table settings
:
CREATE TABLE `points_settins_types_storage` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`point_id` int(11) unsigned NOT NULL,
`settings_prop_type_id` int(11) unsigned NOT NULL,
`value` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '',
PRIMARY KEY (`id`,`settings_prop_type_id`,`point_id`),
KEY `p_id_idx` (`point_id`),
KEY `sid_idx` (`settings_prop_type_id`),
CONSTRAINT `pid` FOREIGN KEY (`point_id`) REFERENCES `points` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `sid` FOREIGN KEY (`settings_prop_type_id`) REFERENCES `points_settings_types` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1062 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Table events
:
CREATE TABLE `events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL,
`start_at` datetime NOT NULL,
`end_at` datetime NOT NULL,
`point_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT 0,
`owner_id` int(11) NOT NULL,
`approver_id` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`date` datetime NOT NULL,
`approver_message` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_message` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`linked_vks_id` int(11) DEFAULT NULL,
`ews_event_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`is_from_exchange` int(11) NOT NULL DEFAULT 0,
`ews_event_change_key` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` varchar(10000) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `owner_id_k` (`owner_id`),
KEY `idx_start_at_end_at` (`start_at`,`end_at`),
KEY `end_start` (`end_at`,`start_at`) USING BTREE,
KEY `date` (`date`) USING BTREE,
KEY `status` (`status`) USING BTREE,
KEY `point` (`point_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2338492 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Upvotes: 1
Views: 121
Reputation: 1270391
For your query, start with in index on events(point_id, start_at)
. Perhaps that will help.
Probably not.
If the interval values are always positive, then try this redundant version (with the above index):
WHERE p.parent_id = 1310 AND
NOT EXISTS (SELECT 1
FROM events e
WHERE e.point_id = p.id AND
e.start_at > '2019-09-09 20:00:00' AND
e.start_at > DATE_ADD('2019-09-09 20:00:00', INTERVAL COALESCE(s.value, 0) MINUTE)
)
Yes, this looks silly. But the additional condition should make it easier to MySQL to use the index correctly.
Upvotes: 1