当我有这样的查询时,创建索引的最佳方法是什么?
... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ...
我知道查询中只能使用一个索引,因此我无法创建两个索引,一个用于user_1
,一个用于user_2
。
也可以将此类查询的解决方案用于该查询吗?
WHERE ((user_1 = '$user_id' AND user_2 = '$friend_id') OR (user_1 = '$friend_id' AND user_2 = '$user_id'))
答案 0 :(得分:4)
MySQL在OR
条件下很难。理论上,@ duskwuff提到了索引合并优化,但实际上,它在您认为应该使用时不会起作用。此外,它不能像单个索引那样提供性能。
大多数人用来解决此问题的解决方案是拆分查询:
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开始,它将使用索引(请参阅https://dev.mysql.com/doc/refman/5.7/en/row-constructor-optimization.html)。
P.S .:请勿将应用程序代码变量直接插值到您的SQL表达式中。请改用查询参数。
答案 1 :(得分:1)
我知道查询中只能使用一个索引...
这是不正确的。在适当的情况下,MySQL将在查询中常规使用多个索引。 (例如,查询联接多个表的查询几乎总是在每个涉及的表上使用至少一个索引。)
对于第一个查询,为MySQL will use an index merge union optimization。如果两列都已建立索引,则EXPLAIN输出将给出以下解释:
Using union(index_on_user_1,index_on_user_2); Using where
第二个示例中显示的查询由(user_1, user_2)
上的索引覆盖。如果您计划例行运行这些查询,请创建该索引。
答案 2 :(得分:0)
这两种情况不同。
在第一种情况下,都需要在两列中搜索相同的值。如果您有两列索引(u1,u2),那么它可能会在u1列使用,因为它不能在u2列使用。如果您有两个分别用于u1和u2的索引,则可能会同时使用它们。该选择来自基于预期返回多少行的统计信息。如果期望返回的行,那么在适当的索引可用的情况下,将很少选择索引搜索。如果该数目很大,则最好使用表或索引进行扫描。
在第二种情况下,也需要再次检查两列,但是由于AND条件,在每次搜索中都有两个子搜索,其中第二个子搜索将基于第一个子搜索的结果。这里更重要,两个索引u1和u2会有所帮助,因为任何选择首先搜索的字段都将具有索引。使用索引的选择就像我上面所述。
但是,无论哪种情况,每个OR都会强制执行1次或一组搜索。因此,提出的使用并集中断的解决方案不会更多地受到阻碍,因为无论使用OR(一个或多个)进行选择还是使用并集进行x选择,并且无论索引选择和搜索类型(搜索还是扫描),都将对该表进行x次搜索。结果,由于联合中的每个选择都有其自己的执行计划部分,因此更有可能使用(单列)索引,并最终从OR周围的所有部分获得所有行结果集。如果您不想将一个大型的select语句复制到多个并集,则可以获取主键值,然后选择这些键或使用视图确保该语句的大部分位于一个位置。
最后,如果排除并集选项,则可以通过一种方法诱使优化器使用单个索引。创建一个双索引u1,u2(或u2,u1-具有更高基数的列首先出现)并修改您的语句,以便所有OR部分使用所有列:
... 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)。请注意,如果列为可为空,并且使用nullull或合并绕过该列可能会导致未选择索引,则此方法将起作用。但是它将与ansi null一起使用。