Не могли бы вы объяснить аспект использования Composite MySQL INDEX для нескольких INNER JOIN? - PullRequest
0 голосов
/ 15 января 2020

Запрос

SELECT v.value
FROM products
         JOIN products_attributes_options pao
              ON 1 = 1
                  AND products.id = pao.product_id
         JOIN attributes_options ao
              ON 1 = 1
                  AND pao.attribute_option_id = ao.id
                  AND attribute_id = 12
         JOIN attributes_options_values aov
              ON
                      1 = 1
                      AND ao.id = aov.attribute_option_id
         JOIN `values` v
              ON
                      1 = 1
                      AND aov.value_id = v.id

         JOIN values_words vw_1
              ON
                      1 = 1
                      AND v.id = vw_1.value_id
         JOIN values_words vw_2
              ON
                      1 = 1
                      AND v.id = vw_2.value_id

         JOIN words w_1
              ON
                      1 = 1
                      AND vw_1.word_id = w_1.id
                      AND w_1.value LIKE '%a%'
         JOIN words w_2
              ON
                      1 = 1
                      AND vw_2.word_id = w_2.id
                      AND w_2.value LIKE '%a%'

GROUP BY v.id, v.value
ORDER BY v.id, v.value

Для этой части запроса

JOIN products_attributes_options pao
     ON 1 = 1
        AND products.id = pao.product_id
JOIN attributes_options ao
     ON 1 = 1
        AND pao.attribute_option_id = ao.id
        AND attribute_id = 12

У меня есть составные индексы

ix_aov_p (attribute_option_id, product_id)
ix_p_aov (product_id, attribute_option_id)

Через EXPLAIN мой запрос, который я видел - два из них могут быть возможными ключами. Но я думал, что достаточно только ix_aov_p.

Не могли бы вы объяснить, как составные индексы работают в этих случаях? Нужно ли мне два индекса или только один индекс? Какой из них мне нужно использовать? ix_aov_p или ix_p_aov

Я исследовал похожий вопрос, но нет достаточного объяснения для понимания - что происходит?

Отдельное предложение Join в составном индексе - похожий вопрос - принятый ответ недостаточно объяснил, что происходит и почему составной INDEX не работает.

Рад объяснить, что происходит со стороны INDEX с этими соединениями для понимания составных индексов, используемых в этом случае.


DB dump Таблицы для использования - DUMP (структура)

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `attributes_options`
--

DROP TABLE IF EXISTS `attributes_options`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `attributes_options` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `attribute_id` int(11) NOT NULL,
  `option_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `attributes_options`
--

LOCK TABLES `attributes_options` WRITE;
/*!40000 ALTER TABLE `attributes_options` DISABLE KEYS */;
/*!40000 ALTER TABLE `attributes_options` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `attributes_options_values`
--

DROP TABLE IF EXISTS `attributes_options_values`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `attributes_options_values` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `attribute_option_id` int(11) NOT NULL,
  `value_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `attributes_options_values`
--

LOCK TABLES `attributes_options_values` WRITE;
/*!40000 ALTER TABLE `attributes_options_values` DISABLE KEYS */;
/*!40000 ALTER TABLE `attributes_options_values` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `products`
--

DROP TABLE IF EXISTS `products`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `products`
--

LOCK TABLES `products` WRITE;
/*!40000 ALTER TABLE `products` DISABLE KEYS */;
/*!40000 ALTER TABLE `products` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `products_attributes_options`
--

DROP TABLE IF EXISTS `products_attributes_options`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `products_attributes_options` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `attribute_option_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ix_p_aov` (`product_id`,`attribute_option_id`),
  UNIQUE KEY `ix_aov_p` (`attribute_option_id`,`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `products_attributes_options`
--

LOCK TABLES `products_attributes_options` WRITE;
/*!40000 ALTER TABLE `products_attributes_options` DISABLE KEYS */;
/*!40000 ALTER TABLE `products_attributes_options` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `values`
--

DROP TABLE IF EXISTS `values`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `values` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `value` varchar(190) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `values`
--

LOCK TABLES `values` WRITE;
/*!40000 ALTER TABLE `values` DISABLE KEYS */;
/*!40000 ALTER TABLE `values` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `values_words`
--

DROP TABLE IF EXISTS `values_words`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `values_words` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `value_id` int(11) NOT NULL,
  `word_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `values_words`
--

LOCK TABLES `values_words` WRITE;
/*!40000 ALTER TABLE `values_words` DISABLE KEYS */;
/*!40000 ALTER TABLE `values_words` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `words`
--

DROP TABLE IF EXISTS `words`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `words` (
  `id` int(11) NOT NULL,
  `value` varchar(190) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `words`
--

LOCK TABLES `words` WRITE;
/*!40000 ALTER TABLE `words` DISABLE KEYS */;
/*!40000 ALTER TABLE `words` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

Tables visualization

1 Ответ

1 голос
/ 15 января 2020

Это слишком нормализовано.

Хорошо нормализовать product с product_id, если в таблице Products есть много информации - имя, размер и т. Д. c. Но все, что я вижу, это name. Так что избавьтесь от product_id и Products и просто получите столбец product_name.

После того, как вы это сделаете, определите многие: многие таблицы сопоставления. Им не нужен id, а только два столбца, каждый из которых является либо идентификатором, либо значением. Посмотрите это для совета относительно оптимальных составных индексов для таких таблиц: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

Но я думал, что достаточно только ix_aov_p.

Это обеспечивает эффективный способ для "заданного aov, найти значения p". В других ситуациях вам нужен обратный порядок. Составные индексы упорядочены. В случае таблицы сопоставления, подобной этой, «данные» должны стоять на первом месте.

В некоторых других ситуациях порядок важен:

WHERE b > 4 AND a = 8

нуждается в INDEX(a, b) в этот заказ . Он может быстро добраться до всех записей для a = 8 и b = 4, они сканируют вперед до a> 8. Подумайте о поиске фамилии = 'Джеймс' и имени LIKE 'R%'.

Между тем, любой из порядков INDEX(a,b) подходит для

WHERE b = 4 AND a = 8

Дополнительные обсуждения: http://mysql.rjweb.org/doc.php/index_cookbook_mysql

...