为具有多个 OR 条件和 AND 条件的 select 查询创建索引
create index for select query with multiple OR conditions and AND conditions
我有一个如下所示的查询:
select count(test.id) from table1
inner join table2 on table1.id = table2.id
where (table2.email = 'test@gmail.com'
OR (table2.phone1 IS NOT NULL AND table2.phone1 in ('123456'))
OR (table2.phone2 IS NOT NULL AND table2.phone2 in ('1234456')))
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No';
我在 table2.email、table2.phone1、table2.phone2、table2.created_at 上有一个索引。这些都是单索引而不是复合索引。据我所知, (table2.email, table2.phone1, table2.phone2) 上的复合索引将不起作用,因为条件是 OR 条件。
我在 (table2.id, table2.created_at, table2.status, table2.is_test) 上创建了一个复合索引,但我在解释查询中得到了相同的结果。
说明查询如下所示
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE table2 range PRIMARY, created_at 8 293 Using where
created_at,
email,
phone1,
phone2,
com_index
1 SIMPLE table1 eq_ref PRIMARY PRIMARY 4 id 1 Using index
这里com_index是我创建的复合索引。我怎样才能创建一个索引来加速这个查询。从解释结果来看,为查询选择的键是 created_at。有没有办法为 table 2 创建复合索引?
请帮我。提前致谢。
编辑:
解释此查询的生产:
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
| 1 | SIMPLE | l0_ | range | PRIMARY,created_at,email,day_phone,eve_phone | PRIMARY | 4 | NULL | 942156 | Using where |
| 1 | SIMPLE | m1_ | eq_ref | PRIMARY | PRIMARY | 4 | lead_platform.l0_.id | 1 | Using index |
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
通常,MySQL 不能在 3 个可能的列(电子邮件、电话 1、电话 2)上使用索引。
我怀疑您的其他条件不是很具体,不会在您的生产数据库中产生很好的结果(意思是,大多数项目不是 "Test" 等)。
使用跨多列的 OR
语句优化查询很棘手。
这 article 表明使用 UNION
将这样的查询拆分为多个查询会更快。在您的情况下,这将是 3 个查询的组合,没有 OR 语句。
这样 MySQL 可以执行索引合并 - 使用 email
、phone1
和 phone2
上的索引。
测试它,让我知道它在实际数据上是否更快。
SELECT COUNT(DISTINCT(t.id)) FROM (
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.email = 'test@gmail.com'
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No'
UNION ALL
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.phone1 IS NOT NULL AND table2.phone1 in ('123456')
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No'
UNION ALL
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.phone2 IS NOT NULL AND table2.phone2 in ('1234456')
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No') AS t
不要跨栏展开一系列内容。在这种情况下,有一个 table 的 phone 个数字,并带有返回 table1
的链接。对于给定的 table1 类型的人,table 会有 0、1、2 甚至更多的数字。那么就不需要 OR
或 UNION
了。相反,需要 JOIN
。
您有 status
和 is_test
;这些似乎是多余的。有很多标志是没有帮助的;这些可以合并吗?
我不明白按 email OR phone
进行查找。似乎您将拥有一个或另一个价值,而不是两者。在那种情况下,构建 WHERE
子句(和 JOIN
),那么就不会有 OR
了。
毕竟,在新的 table 中有 INDEX(phone)
,在 table1
中有 INDEX(email)
。由于电子邮件和 phone 可能非常有选择性,我不会将 status
等添加到索引中。
我有一个如下所示的查询:
select count(test.id) from table1
inner join table2 on table1.id = table2.id
where (table2.email = 'test@gmail.com'
OR (table2.phone1 IS NOT NULL AND table2.phone1 in ('123456'))
OR (table2.phone2 IS NOT NULL AND table2.phone2 in ('1234456')))
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No';
我在 table2.email、table2.phone1、table2.phone2、table2.created_at 上有一个索引。这些都是单索引而不是复合索引。据我所知, (table2.email, table2.phone1, table2.phone2) 上的复合索引将不起作用,因为条件是 OR 条件。 我在 (table2.id, table2.created_at, table2.status, table2.is_test) 上创建了一个复合索引,但我在解释查询中得到了相同的结果。 说明查询如下所示
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE table2 range PRIMARY, created_at 8 293 Using where
created_at,
email,
phone1,
phone2,
com_index
1 SIMPLE table1 eq_ref PRIMARY PRIMARY 4 id 1 Using index
这里com_index是我创建的复合索引。我怎样才能创建一个索引来加速这个查询。从解释结果来看,为查询选择的键是 created_at。有没有办法为 table 2 创建复合索引? 请帮我。提前致谢。
编辑: 解释此查询的生产:
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
| 1 | SIMPLE | l0_ | range | PRIMARY,created_at,email,day_phone,eve_phone | PRIMARY | 4 | NULL | 942156 | Using where |
| 1 | SIMPLE | m1_ | eq_ref | PRIMARY | PRIMARY | 4 | lead_platform.l0_.id | 1 | Using index |
+----+-------------+-------+--------+----------------------------------------------+---------+---------+----------------------+--------+-------------+
通常,MySQL 不能在 3 个可能的列(电子邮件、电话 1、电话 2)上使用索引。
我怀疑您的其他条件不是很具体,不会在您的生产数据库中产生很好的结果(意思是,大多数项目不是 "Test" 等)。
使用跨多列的 OR
语句优化查询很棘手。
这 article 表明使用 UNION
将这样的查询拆分为多个查询会更快。在您的情况下,这将是 3 个查询的组合,没有 OR 语句。
这样 MySQL 可以执行索引合并 - 使用 email
、phone1
和 phone2
上的索引。
测试它,让我知道它在实际数据上是否更快。
SELECT COUNT(DISTINCT(t.id)) FROM (
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.email = 'test@gmail.com'
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No'
UNION ALL
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.phone1 IS NOT NULL AND table2.phone1 in ('123456')
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No'
UNION ALL
SELECT test.id FROM table1
INNER JOIN table2 on table1.id = table2.id
WHERE table2.phone2 IS NOT NULL AND table2.phone2 in ('1234456')
AND table2.id <> 1234
AND table2.created_at >= '2015-10-10'
AND table2.status NOT IN ('test')
AND table2.is_test = 'No') AS t
不要跨栏展开一系列内容。在这种情况下,有一个 table 的 phone 个数字,并带有返回 table1
的链接。对于给定的 table1 类型的人,table 会有 0、1、2 甚至更多的数字。那么就不需要 OR
或 UNION
了。相反,需要 JOIN
。
您有 status
和 is_test
;这些似乎是多余的。有很多标志是没有帮助的;这些可以合并吗?
我不明白按 email OR phone
进行查找。似乎您将拥有一个或另一个价值,而不是两者。在那种情况下,构建 WHERE
子句(和 JOIN
),那么就不会有 OR
了。
毕竟,在新的 table 中有 INDEX(phone)
,在 table1
中有 INDEX(email)
。由于电子邮件和 phone 可能非常有选择性,我不会将 status
等添加到索引中。