簡體   English   中英

在 MySQL 中使用 OR 避免對簡單連接進行全表掃描

[英]Avoid full table scan on a simple join using OR in MySQL

我有這個架構

create table table1
(
    id        int          auto_increment primary key,
    name      varchar(2)   null,
    position  int          null,
);

create index table1_position
    on table1 (position);


create table table_2
(
    id              int auto_increment primary key,
    table1_id       int          null,
    position        int          null,
    constraint table_2_ibfk_1
        foreign key (table1_id) references table1 (id)
);

create index ix_table_2_position
    on table_2 (position);

create index table1_id
    on table_2 (table1_id);

所以我在每個表的列position上添加了兩個索引。 現在我需要在 BOTH 表中查找一系列位置(通過加入 then 並應用 OR 查詢)

所以我有這個查詢

SELECT *
FROM table_1
    INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_1.position BETWEEN 5000 AND 5500
   OR table_2.position BETWEEN 5000 AND 5500

但是解釋查詢輸出給了我所有(全表掃描)

id             1
select_type    SIMPLE
table          table_1
partitions
type           ALL
possible_keys  PRIMARY,table1_position
key
key_len
ref
rows           9929
filtered       100.0
Extra

如果我更改為AND如果給我預期的范圍索引掃描

SELECT *
FROM table_1
    INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_1.position BETWEEN 5000 AND 5500
   AND table_2.position BETWEEN 5000 AND 5500
id             1
select_type    SIMPLE
table          table_1
partitions
type           range
possible_keys  PRIMARY,pos_idx2
key            pos_idx2
key_len        5
ref
rows           1
filtered       100.0
Extra          Using index condition

但我需要這里的OR語句......我怎么能讓 mysql 對 OR 語句使用范圍掃描索引? 我可以在這里改進我​​的索引table1_id (我考慮了positiontable1_id上的多值索引 - 外鍵,但它沒有幫助,它執行了全表掃描)。

SELECT *
FROM table_1
INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_1.position BETWEEN 5000 AND 5500

UNION ALL

SELECT *
FROM table_1
INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_1.position NOT BETWEEN 5000 AND 5500
  AND table_2.position BETWEEN 5000 AND 5500

還測試

SELECT *
FROM table_1
INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE NOT (    table_1.position NOT BETWEEN 5000 AND 5500
           AND table_2.position NOT BETWEEN 5000 AND 5500 )

據推測,出於性能原因,您希望避免表掃描。

因此,嘗試將 OR 交換為 UNION 操作。

首先,使用子查詢獲取所需的table1.id值集,如下所示。

    SELECT id FROM table_1 WHERE position BETWEEN 5000 AND 5500
    UNION
    SELECT table1_id FROM table_2 WHERE position BETWEEN 5000 AND 5500

UNION 的第二部分通過選擇 FK 列從 table_2 中檢索您需要的 table_1.id 值。

接下來,使用該子查詢從table1獲取您的行。

SELECT * FROM table_1
 WHERE id IN (
    SELECT id FROM table_1 WHERE position BETWEEN 5000 AND 5500
    UNION
    SELECT table1_id FROM table_2 WHERE position BETWEEN 5000 AND 5500           
)

為了加快速度,請在 table_2 上添加此復合索引。

CREATE INDEX ix_table_2_position_table1id 
          ON table_2 (position, table1_id);

請注意,table2 上的兩個單列索引都不適用於此查詢。

OR性能問題通常可以用UNION解決:

SELECT *
FROM table_1
INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_1.position BETWEEN 5000 AND 5500
UNION
SELECT *
FROM table_1
INNER JOIN table_2 ON table_1.id = table_2.table1_id
WHERE table_2.position BETWEEN 5000 AND 5500;

暫無
暫無

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

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