Mysql 在查询中使用或时索引

Mysql 在查询中使用或时索引,mysql,sql,Mysql,Sql,当我有这样一个查询时,创建索引的最佳方法是什么 ... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ... 我知道一个查询只能使用一个索引,所以我无法创建两个索引,一个用于user\u 1,一个用于user\u 2 此类型查询的解决方案是否也可用于此查询 WHERE ((user_1 = '$user_id' AND user_2 = '$friend_id') OR (user_1 = '$friend_id' AND user_2

当我有这样一个查询时,创建索引的最佳方法是什么

... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ...
我知道一个查询只能使用一个索引,所以我无法创建两个索引,一个用于
user\u 1
,一个用于
user\u 2

此类型查询的解决方案是否也可用于此查询

WHERE ((user_1 = '$user_id' AND user_2 = '$friend_id') OR (user_1 = '$friend_id' AND user_2 = '$user_id'))
我知道一个查询只能使用一个索引

这是不正确的。在适当的情况下,MySQL将在查询中常规使用多个索引。(例如,连接多个表的查询几乎总是在涉及的每个表上使用至少一个索引。)

对于您的第一个查询。如果两列都已编制索引,则解释输出将给出以下行的解释:

Using union(index_on_user_1,index_on_user_2); Using where

第二个示例中显示的查询包含在
(user\u 1,user\u 2)
上的索引中。如果您计划定期运行这些查询,请创建该索引。

MySQL在
条件下很难运行。理论上,@Dashwuff提到了索引合并优化,但实际上,它并没有在您认为应该的时候启动。除此之外,它提供的性能不如单个索引

大多数人用于解决此问题的解决方案是拆分查询:

SELECT ... WHERE user_1 = ?
UNION
SELECT ... WHERE user_2 = ?
这样,每个查询将能够使用自己的索引选择,而不依赖于不可靠的索引合并功能

您的第二个查询可以更简单地进行优化。这只是一个元组比较。可以这样写:

WHERE (user_1, user_2) IN (('$user_id', '$friend_id'), ('$friend_id', '$user_id'))
在MySQL的旧版本中,元组比较不会使用索引,但由于5.7.3,它会使用索引(请参阅)


注意:不要直接在SQL表达式中插入应用程序代码变量。请改用查询参数。

这两种情况不同

在第一种情况下,需要在两列中搜索相同的值。如果有两列索引(u1,u2),则可以在列u1处使用,因为它不能在列u2处使用。如果u1和u2有两个单独的索引,则可能会同时使用这两个索引。选择来自基于预期返回的行数的统计信息。如果返回的行预期很少,则将选择索引查找(如果适当的索引可用)。如果数字较高,则最好是扫描表或索引

在第二种情况下,需要再次检查两列,但在每个搜索中有两个子搜索,其中第二个子搜索将基于第一个子搜索的结果,这是由于AND条件。在这里,它更重要,两个索引u1和u2将有所帮助,因为选择首先搜索的任何字段都将有一个索引。使用索引的选择与我上面描述的一样

但是,在任何一种情况下,每或将强制执行一次或一组搜索。因此,建议的使用union中断的解决方案不会造成更多的阻碍,因为无论使用OR(s)选择1次还是使用union选择x次,无论索引选择和搜索类型(seek或scan),都会对表进行x次搜索。因此,由于union中的每个select都有自己的执行计划部分,因此更有可能使用(单列)索引,并最终从OR周围的所有部分获取所有行结果集。如果不想将大型select语句复制到多个联合,则可以获取主键值,然后选择这些值,或使用视图确保语句的大部分位于一个位置

最后,如果排除union选项,有一种方法可以欺骗优化器使用单个索引。创建双索引u1,u2(或u2,u1-基数较高的列优先)并修改语句,以便所有或部分使用所有列:

... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ...
将转换为:

... WHERE ((user_1 = '$user_id' and user_2=user_2) OR (user_1=user_1 and user_2 = '$user_id')) ...

这样,将始终使用双索引(u1,u2)。请注意,如果列可为Null,则此操作不起作用,使用isnull或coalesce绕过此操作可能会导致未选择索引。但是,它将在ansi NULL关闭的情况下工作。

用户_2上的条件不会在
(用户_1,用户_2)
上使用索引,因为用户_2不是索引的最左边的列。就像你不能只在电话簿上查名字一样。@BillKarwin仔细阅读了第二个问题。它是
(user\u 1,user\u 2)
上两个相等条件的并集。