根据先前的选择结果优化选择行

时间:2016-12-11 19:48:39

标签: mysql sql

我有一个像这样的字段表:

product_id  |aid    |value|
------------|-------|-----|
789         |6      |1    |
789         |6      |3    | -->aid = 6 , value = 3
789         |      8|    8| -->rows that i want with aid 8
789         |      8|   11| -->rows that i want with aid 8
789         |      8|   82| -->rows that i want with aid 8
------------|-------|-----|
790         |6      |2    |
790         |6      |3    | -->aid = 6 , value = 3
790         |6      |4    |
790         |      8|    8| -->rows that i want with aid 8
790         |      8|   16| -->rows that i want with aid 8
------------|-------|-----|
791         |6      |7    |
791         |8      |13   |
------------|-------|-----|

对于具有一对(援助= 6 AND值= 3)的特定产品,我希望所有行的辅助= 8 我做了什么: 首先,我选择具有aid = 6value = 3的不同product_ids 然后我选择所有行,其中product_ids是IN先前的选择查询。 这是我的查询,大约需要1秒钟。

SELECT DISTINCT `value` FROM `fields`
WHERE aid = 8 AND product_id IN 
(
    SELECT DISTINCT `fields`.product_id FROM `fields` 
    WHERE aid = 6 AND `value` = 3
)

值结果为8,11,82,16
有更优化的方法吗?

3 个答案:

答案 0 :(得分:1)

首先,子查询中的distinct应该是不必要的。我不确定MySQL是否会优化它。所以,从:

开始
SELECT DISTINCT f.`value`
FROM `fields` f
WHERE f.aid = 8 AND
      f.product_id IN (SELECT f2.product_id
                       FROM `fields` f2
                       WHERE f2.aid = 6 AND f2.`value` = 3
                      );

对于此查询,您需要fields(aid, value, product_id)上的索引。

在早期版本的MySQL中,最好用IN替换EXISTS子查询。如果您的查询现在在一秒钟内完成,那么您可能处于更新版本。

答案 1 :(得分:0)

Gordon向您展示了IN方法,如果您最终需要调整性能,那么您可能还需要考虑EXISTS和/或JOIN方法3所有方法都有不同的优缺点,具体取决于您的数据大小和复杂性。

EXISTS只使用相关的子查询

SELECT f.*
FROM
    `fields` f
WHERE
    f.aid = 8
    EXISTS (
       SELECT 1
       FROM `fields` f2
       WHERE
          f2.aid = 6
          AND f2.`value` = 3
          AND f1.product_id = f2.product_id)   

对于在这种情况下保持不同的连接方法,因为如果6& 3可以代表不止一次。

SELECT f.*
FROM
    `fields` f
    INNER JOIN (
       SELECT DISTINCT `fields`.product_id FROM `fields` 
       WHERE aid = 6 AND `value` = 3
    ) t
    ON f.product_id = t.product_i
WHERE
    f.aid = 8

答案 2 :(得分:0)

首先,如果您还没有索引,请将以下索引添加到您的表格中。

ALTER TABLE fields ADD KEY (aid, product_id, value);

顺便说一句,在询问SQL问题时,如果你发布SHOW CREATE TABLE的输出会有帮助,这样我们就可以看到你是否已经定义了任何索引或约束。

sql> SHOW CREATE TABLE fields\G
       Table: fields
Create Table: CREATE TABLE `fields` (
  `product_id` int(11) DEFAULT NULL,
  `aid` int(11) DEFAULT NULL,
  `value` int(11) DEFAULT NULL,
  KEY `aid` (`aid`,`product_id`,`value`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

接下来,尝试此查询:

SELECT DISTINCT f1.value
FROM fields AS f1
INNER JOIN fields AS f2
  ON f1.product_id=f2.product_id
WHERE f1.aid=8 AND f2.aid=6 AND f2.value=3;

这不使用子查询,只使用索引查找。我们并不关心减少f2中匹配行的集合,因为无论如何都会由DISTINCT处理。

输出(在MySQL 8.0.0-dmr上测试):

+-------+
| value |
+-------+
|     8 |
|    11 |
|    82 |
|    16 |
+-------+

这里是EXPLAIN报告以显示优化:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: f2
   partitions: NULL
         type: ref
possible_keys: aid
          key: aid
      key_len: 5
          ref: const
         rows: 6
     filtered: 10.00
        Extra: Using where; Using index; Using temporary
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: f1
   partitions: NULL
         type: ref
possible_keys: aid
          key: aid
      key_len: 10
          ref: const,test.f2.product_id
         rows: 2
     filtered: 100.00
        Extra: Using index

两个表都获得"使用索引"优化,因此他们使用覆盖索引。

两个表都使用索引查找来缩小已检查行的数量。

还有一个临时表会导致一些开销。但由于DISTINCT,这是不可避免的。但至少它只是一个临时表,而不是由于在子查询中使用DISTINCT而导致的多个临时表。临时表应该很小,因为它只需要存储已匹配的行。