簡體   English   中英

在不同的表/列上按喜歡和排序的位置

[英]where like and order by on different tables/columns

有關信息,在以下示例中, big_table由數百萬行組成,而small_table由數百行組成。

這是我要執行的基本查詢:

SELECT b.id 
    FROM big_table b 
    LEFT JOIN small_table s 
    ON b.small_id=s.id
    WHERE s.name like 'something%' 
    ORDER BY b.name 
    LIMIT 10, 10;

這很慢,我可以理解為什么兩個索引都不能使用。

我最初的想法是將查詢分為多個部分。

這很快:

SELECT id FROM small_table WHERE name like 'something%';

這也很快:

SELECT id FROM big_table WHERE small_id IN (1, 2) ORDER BY name LIMIT 10, 10;

但是,放在一起,它變得很慢:

SELECT id FROM big_table 
    WHERE small_id 
    IN (
        SELECT id 
        FROM small_table WHERE name like 'something%'
    ) 
    ORDER BY name 
    LIMIT 10, 10;

除非對每行都重新評估子查詢,否則它不應該比單獨執行兩個查詢慢嗎?

我正在尋找優化初始查詢的任何幫助,並理解為什么第二個查詢不起作用。


搜尋最后一個查詢的結果:

| id   | select_type | table | type | possible_keys | key | key_len | ref | rows  | Extra
| 1 | PRIMARY | small_table | range | PRIMARY, ix_small_name | ix_small_name | 768 | NULL | 1 | Using where; Using index; Using temporary; Using filesort |
| 1 | PRIMARY | big_table | ref | ix_join_foreign_key   | ix_join_foreign_key | 9 | small_table.id | 11870 | |

臨時解決方案:

SELECT id FROM big_table ignore index(ix_join_foreign_key)
    WHERE small_id 
    IN (
        SELECT id 
        FROM small_table ignore index(PRIMARY)
        WHERE name like 'something%'
    ) 
    ORDER BY name 
    LIMIT 10, 10;

(結果和解釋與EXISTS而不是IN相同)

EXPLAIN輸出變為:

| 1 | PRIMARY | big_table | index  | NULL | ix_big_name | 768 | NULL | 20 | |
| 1 | PRIMARY | <subquery2> | eq_ref | distinct_key | distinct_key | 8 | func | 1 | |
| 2 | MATERIALIZED | small_table | range | ix_small_name | ix_small_name | 768 | NULL | 1 | Using where; Using index |

如果有人有更好的解決方案,我仍然很感興趣。

您正在尋找一個EXISTSIN查詢。 由於眾所周知MySQL在IN較弱,因此我會嘗試EXISTS ,盡管更喜歡IN因為它的簡單性。

select id
from big_table b
where exists
(
  select *
  from small_table s
  where s.id = b.small_id
  and s.name = 'something%'
)
order by name 
limit 10, 10;

big_table上具有良好的索引將很有幫助。 它應該首先包含small_id以找到匹配項,然后包含排序name 據我所知,該ID自動包含在MySQL索引中(否則也應將其添加到索引中)。 因此,您將擁有一個索引,該索引按所需順序包含big_table所需的所有字段(稱為覆蓋索引),因此可以從索引中單獨讀取所有數據,而不必訪問表本身。

create index idx_big_quick on big_table(small_id, name);

您面臨的問題是您在小表上有條件,但試圖避免在大表中進行排序。 在MySQL中,我認為您至少需要進行全表掃描。

第一步是使用exists編寫查詢,正如其他人提到的那樣:

SELECT b.id
FROM big_table b
WHERE EXISTS (SELECT 1
              FROM small_table s
              WHERE s.name LIKE 'something%' AND s.id = b.small_id
             )
ORDER BY b.name;

問題是:您可以欺騙MySQL使用索引執行ORDER BY嗎? 一種可能性是使用適當的索引。 在這種情況下,適當的索引是: big_table(name, small_id, id)small_table(id, name) 索引中鍵的順序很重要。 因為第一個是覆蓋索引,所以MySQL可能會按名稱順序依次讀取索引,選擇適當的ID。

您可以嘗試以下方法:

SELECT b.id
FROM big_table b
JOIN small_table s
  ON b.small_id = s.id
WHERE s.name like 'something%'
ORDER BY b.name;

要么

SELECT b.id FROM big_table b
WHERE EXISTS(SELECT 1 FROM small_table s
             WHERE s.name LIKE 'something%' AND s.id = b.small_id)
ORDER BY b.name;

注意:您似乎不需要LEFT JOIN 左外部big_table幾乎總是會導致對big_table進行全表掃描

PS確保您在big_table.small_id上有一個索引

計划A

SELECT  b.id
    FROM  big_table b
    JOIN  small_table s  ON b.small_id=s.id
    WHERE  s.name like 'something%'
    ORDER BY  b.name
    LIMIT  10, 10;

(請注意刪除LEFT 。)

你需要

small_table:  INDEX(name, id)
big_table:    INDEX(small_id), or, for 'covering': INDEX(small_id, name, id)

它將使用s索引查找'something%'並逐步瀏覽。 但是它必須找到所有這樣的行,然后將JOINb才能在那里找到所有這樣的行。 只有這樣,它才能執行ORDER BYOFFSETLIMIT 將會有一個文件排序( 可能發生在RAM中)。

索引中的列順序很重要。

計划B

另一個建議可能效果很好。 這取決於各種事情。

SELECT  b.id
    FROM  big_table b
    WHERE  EXISTS 
      ( SELECT  *
            FROM  small_table s
            WHERE  s.name LIKE 'something%'
              AND  s.id = b.small_id 
      )
    ORDER BY  b.name
    LIMIT 10, 10;

這需要這些:

big_table:    INDEX(name), or for 'covering',  INDEX(name, small_id, id)
small_table:  INDEX(id, name), which is 'covering'

(注意:如果您執行的操作不是SELECT b.id ,那么我對覆蓋的評論可能是錯誤的。)

哪個更快(A或B)? 如果不了解“某物百分比”的頻率以及多對一映射的“多少”,就無法預測。

設定值

如果這些表是InnoDB,請確保將innodb_buffer_pool_size設置為可用 RAM的大約70%。

分頁

您對OFFSET使用是否意味着您正在“分頁”數據? OFFSET是一種低效的方法。 請參閱我的博客 ,但是請注意,只有Plan B可以使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM