我正在对具有100,000+行的表运行以下查询。我试图弄清楚如何使用全文索引,以及如何在与它连接的其他列上使用索引。 (phppos_items的创建表在底部)
SELECT name
FROM `phppos_items`
WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description)
AGAINST ('"Search* "' IN BOOLEAN MODE)
or phppos_items.item_id = 'Search')
and phppos_items.deleted=0 and system_item = 0;
需要0.21秒;可以但它没有完全索引。
这里是解释(如您所见,它检查了58,188行):
mysql> EXPLAIN SELECT name FROM `phppos_items` WHERE (MATCH (phppos_items.name, phppo LEAN MODE) or phppos_items.item_id = 'Search') and phppos_items.deleted=0 and system_item = 0;
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+
| 1 | SIMPLE | phppos_items | NULL | ref | PRIMARY,deleted,deleted_system_item | deleted | 4 | const | 58188 | 2.00 | Using where |
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+
如果我将查询更改为:
SELECT name
FROM `phppos_items`
WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description) AGAINST ('"Search* "' IN BOOLEAN MODE))
花费0.0005秒并被完全索引。参见说明:
mysql> EXPLAIN SELECT name FROM `phppos_items` WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description) AGAINST ('"Search* "' IN BOOL
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+
| 1 | SIMPLE | phppos_items | NULL | fulltext | full_search | full_search | 0 | const | 1 | 100.00 | Using where; Ft_hints: no_ranking |
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+
这里是创建表:
mysql> show create table phppos_items;
+--------------+---------------------------------------------------------------------+
| Table | Create Table |
+--------------+---------------------------------------------------------------------+
| phppos_items | CREATE TABLE `phppos_items` (
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`category_id` int(11) DEFAULT NULL,
`supplier_id` int(11) DEFAULT NULL,
`manufacturer_id` int(11) DEFAULT NULL,
`item_number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`product_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`ecommerce_product_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`ecommerce_product_quantity` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`size` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`tax_included` int(1) NOT NULL DEFAULT '0',
`cost_price` decimal(23,10) NOT NULL,
`unit_price` decimal(23,10) NOT NULL,
`promo_price` decimal(23,10) DEFAULT NULL,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
`reorder_level` decimal(23,10) DEFAULT NULL,
`expire_days` int(10) DEFAULT NULL,
`item_id` int(10) NOT NULL AUTO_INCREMENT,
`allow_alt_description` tinyint(1) NOT NULL,
`is_serialized` tinyint(1) NOT NULL,
`override_default_tax` int(1) NOT NULL DEFAULT '0',
`is_ecommerce` int(1) DEFAULT '1',
`is_service` int(1) NOT NULL DEFAULT '0',
`is_ebt_item` int(1) NOT NULL DEFAULT '0',
`commission_percent` decimal(23,10) DEFAULT '0.0000000000',
`commission_percent_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
`commission_fixed` decimal(23,10) DEFAULT '0.0000000000',
`change_cost_price` int(1) NOT NULL DEFAULT '0',
`disable_loyalty` int(1) NOT NULL DEFAULT '0',
`deleted` int(1) NOT NULL DEFAULT '0',
`last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ecommerce_last_modified` timestamp NULL DEFAULT NULL,
`tax_class_id` int(10) DEFAULT NULL,
`replenish_level` decimal(23,10) DEFAULT NULL,
`system_item` int(1) NOT NULL DEFAULT '0',
`max_discount_percent` decimal(15,3) DEFAULT NULL,
`max_edit_price` decimal(23,10) DEFAULT NULL,
`min_edit_price` decimal(23,10) DEFAULT NULL,
`custom_field_1_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_2_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_3_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_4_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_5_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_6_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_7_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_8_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_9_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`custom_field_10_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`required_age` int(10) DEFAULT NULL,
`verify_age` int(1) NOT NULL DEFAULT '0',
`weight` decimal(23,10) DEFAULT NULL,
`length` decimal(23,10) DEFAULT NULL,
`width` decimal(23,10) DEFAULT NULL,
`height` decimal(23,10) DEFAULT NULL,
`ecommerce_shipping_class_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`long_description` longtext COLLATE utf8_unicode_ci NOT NULL,
`allow_price_override_regardless_of_permissions` int(1) DEFAULT '0',
`main_image_id` int(10) DEFAULT NULL,
`only_integer` int(1) NOT NULL DEFAULT '0',
`is_series_package` int(1) NOT NULL DEFAULT '0',
`series_quantity` int(10) DEFAULT NULL,
`series_days_to_use_within` int(10) DEFAULT NULL,
`is_barcoded` int(1) NOT NULL DEFAULT '1',
`default_quantity` decimal(23,10) DEFAULT NULL,
`disable_from_price_rules` int(1) DEFAULT '0',
`last_edited` timestamp NULL DEFAULT NULL,
`info_popup` text COLLATE utf8_unicode_ci,
`item_inactive` int(1) DEFAULT '0',
PRIMARY KEY (`item_id`),
UNIQUE KEY `item_number` (`item_number`),
UNIQUE KEY `product_id` (`product_id`),
KEY `phppos_items_ibfk_1` (`supplier_id`),
KEY `deleted` (`deleted`),
KEY `phppos_items_ibfk_3` (`category_id`),
KEY `phppos_items_ibfk_4` (`manufacturer_id`),
KEY `phppos_items_ibfk_5` (`ecommerce_product_id`),
KEY `description` (`description`(255)),
KEY `size` (`size`),
KEY `reorder_level` (`reorder_level`),
KEY `cost_price` (`cost_price`),
KEY `unit_price` (`unit_price`),
KEY `promo_price` (`promo_price`),
KEY `last_modified` (`last_modified`),
KEY `name` (`name`),
KEY `phppos_items_ibfk_6` (`tax_class_id`),
KEY `deleted_system_item` (`deleted`,`system_item`),
KEY `custom_field_1_value` (`custom_field_1_value`),
KEY `custom_field_2_value` (`custom_field_2_value`),
KEY `custom_field_3_value` (`custom_field_3_value`),
KEY `custom_field_4_value` (`custom_field_4_value`),
KEY `custom_field_5_value` (`custom_field_5_value`),
KEY `custom_field_6_value` (`custom_field_6_value`),
KEY `custom_field_7_value` (`custom_field_7_value`),
KEY `custom_field_8_value` (`custom_field_8_value`),
KEY `custom_field_9_value` (`custom_field_9_value`),
KEY `custom_field_10_value` (`custom_field_10_value`),
KEY `verify_age` (`verify_age`),
KEY `phppos_items_ibfk_7` (`main_image_id`),
KEY `item_inactive_index` (`item_inactive`),
FULLTEXT KEY `full_search` (`name`,`item_number`,`product_id`,`description`),
FULLTEXT KEY `name_search` (`name`),
FULLTEXT KEY `item_number_search` (`item_number`),
FULLTEXT KEY `product_id_search` (`product_id`),
FULLTEXT KEY `description_search` (`description`),
FULLTEXT KEY `size_search` (`size`),
FULLTEXT KEY `custom_field_1_value_search` (`custom_field_1_value`),
FULLTEXT KEY `custom_field_2_value_search` (`custom_field_2_value`),
FULLTEXT KEY `custom_field_3_value_search` (`custom_field_3_value`),
FULLTEXT KEY `custom_field_4_value_search` (`custom_field_4_value`),
FULLTEXT KEY `custom_field_5_value_search` (`custom_field_5_value`),
FULLTEXT KEY `custom_field_6_value_search` (`custom_field_6_value`),
FULLTEXT KEY `custom_field_7_value_search` (`custom_field_7_value`),
FULLTEXT KEY `custom_field_8_value_search` (`custom_field_8_value`),
FULLTEXT KEY `custom_field_9_value_search` (`custom_field_9_value`),
FULLTEXT KEY `custom_field_10_value_search` (`custom_field_10_value`),
CONSTRAINT `phppos_items_ibfk_1` FOREIGN KEY (`supplier_id`) REFERENCES `phppos_suppliers` (`person_id`),
CONSTRAINT `phppos_items_ibfk_3` FOREIGN KEY (`category_id`) REFERENCES `phppos_categories` (`id`),
CONSTRAINT `phppos_items_ibfk_4` FOREIGN KEY (`manufacturer_id`) REFERENCES `phppos_manufacturers` (`id`),
CONSTRAINT `phppos_items_ibfk_6` FOREIGN KEY (`tax_class_id`) REFERENCES `phppos_tax_classes` (`id`),
CONSTRAINT `phppos_items_ibfk_7` FOREIGN KEY (`main_image_id`) REFERENCES `phppos_item_images` (`image_id`)
) ENGINE=InnoDB AUTO_INCREMENT=20000966 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+--------------+-------------------------------------------------------------------------------------------------------------
1 row in set (0.00 sec)
这里存在严重的数据类型不一致:
`item_id` int(10) NOT NULL AUTO_INCREMENT,
phppos_items.item_id = 'Search'
这特别难以优化:
WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description)
AGAINST ('"Search* "' IN BOOLEAN MODE)
or phppos_items.item_id = 'Search')
and phppos_items.deleted=0
and system_item = 0;
基本上,执行查询的唯一方法是检查表的每一行。此外,FT测试喜欢“坐在驾驶员座位上”,但这是不允许的。
第一步是摆脱OR
:
( SELECT ...
WHERE MATCH(..) AGAINST(..)
AND phppos_items.deleted=0
AND system_item = 0 )
UNION DISTINCT
( SELECT ...
WHERE phppos_items.item_id = 'Search'
AND phppos_items.deleted=0
AND system_item = 0 )
第一个SELECT
将进行FT测试(非常快),然后根据0测试筛选出任何行。
第二个SELECT
将仅使用PRIMARY KEY(item_id)
(除非有错字!)并检查数字item_id是否为零,并且可能找不到任何行。
然后UNION
将收集两个结果集,对其进行消减并非常快速地提供结果。
(将[C0]转换为OR
是一种通用的优化技术;它对您的查询特别有用。)
我发现在一个表上拥有两个以上的UNIQUE键几乎是不明智的。您确定您有3个吗?