IN子句中的MySQL多列
Posted
技术标签:
【中文标题】IN子句中的MySQL多列【英文标题】:MySQL multiple columns in IN clause 【发布时间】:2017-11-26 03:13:27 【问题描述】:我有一个数据库,其中有四列对应于地理坐标 x,y 的开始和结束位置。列是:
x0 y0 x1 y1我有这四列的索引,序列为 x0, y0, x1, y1。
我有一个包含大约一百个地理对组合的列表。我将如何有效地查询这些数据?
我想按照this SO answer 的建议做这样的事情,但它只适用于 Oracle 数据库,不适用于 mysql:
SELECT * FROM my_table WHERE (x0, y0, x1, y1) IN ((4, 3, 5, 6), ... ,(9, 3, 2, 1));
我在想也许可以用索引做点什么?什么是最好的方法(即:最快的查询)?感谢您的帮助!
注意事项:
我无法更改数据库的架构 我有大约 100'000'000 行编辑:
原样的代码实际上是可以工作的,但是它非常慢并且没有利用索引(因为我们有一个旧版本的 MySQL v5.6.27
)。
【问题讨论】:
这在 MySQL 中应该可以正常工作,你试过了吗?我看到的第一条评论说你链接的问题是 5 年前的。 您知道,您可以在 MySQL 中执行此操作。查看我的测试:sqlfiddle.com/#!9/7b5c1/1 【参考方案1】:为了有效利用索引,可以重写IN谓词
(x0, y0, x1, y1) IN ((4, 3, 5, 6),(9, 3, 2, 1))
像这样:
( ( x0 = 4 AND y0 = 3 AND x1 = 5 AND y1 = 6 )
OR ( x0 = 9 AND y0 = 3 AND x1 = 2 AND y1 = 1 )
)
【讨论】:
您的解决方案要快得多。使用我们的 MySQL 版本,单个查询(100'000'000 行,列表中的 10 个元素)使用您的解决方案需要 3.14 秒,而使用IN
语法需要 1427 秒。
@GordonLinoff 建议的查询模式可能更快,将单独的 SELECT 语句的结果与 UNION ALL
集合运算符连接起来。对此的解释可能会显示带有“ref”和“const”的查询,而不是“range”。该模式肯定会使用索引。不能保证它会更快,但值得测试。
仅通过一次性测试,GordonLinoff 的解决方案比您的解决方案稍慢(3.96 对 3.14 秒)。到目前为止,这不是一个严格的测试,但至少,这两个选项都使用了索引。
不是in
相当于有索引的or
或in
对于mysql 的情况甚至更好,因为在mysql 中有对in
的优化??
@lily:使用 EXPLAIN 查看执行计划的差异,是否正在使用索引。是的,这两种形式在语义上是等效的 ( foo = 1 or foo = 2 )
和 foo in (1,2)
。在这个特定示例中,MySQL 5.6 优化器使用元组比较(a,b,c) IN ((1,1,1),(2,2,2))
对表单使用效率较低的执行计划。从理论上讲,是的,优化器应该能够将其扩展为this answer中所示的形式,然后提出使用索引的执行计划。注意:在 MySQL 5.6 中观察到的行为,Bill Karwin 指出这已在 5.7 中修复【参考方案2】:
我不明白你的意思。以下查询是有效的 MySQL 语法:
SELECT *
FROM my_table
WHERE (x0, y0, x1, y1) IN ((4, 3, 5, 6), ... ,(9, 3, 2, 1));
我希望 MySQL 使用您描述的复合索引。但是,如果没有,您可以这样做:
SELECT *
FROM my_table
WHERE x0 = 4 AND y0 = 3 AND x1 = 5 AND y1 = 6
UNION ALL
. . .
SELECT *
FROM my_table
WHERE x0 = 9 AND y0 = 3 AND x1 = 2 AND y1 = 1
WHERE
子句中的相等比较将利用索引。
【讨论】:
确实,它是一种有效的语法,但执行起来需要很长时间。看来我们使用的 MySQL 版本没有利用索引。【参考方案3】:MySQL 允许像您展示的那样进行行构造函数比较,但优化器直到 MySQL 5.7 才知道如何使用索引来提高性能。
https://dev.mysql.com/doc/refman/5.7/en/row-constructor-optimization.html【讨论】:
我明白了,我们有一个旧版本的 MySQL,它让一切变得非常缓慢。该代码实际上可以工作,但速度很慢。 @spencer 回答已修复。【参考方案4】:您可以将concatenate 四个值放入一个字符串中并像这样检查它们:
SELECT *
FROM my_table
WHERE CONCAT_WS(',', x0, y0, x1, y1) IN ('4,3,5,6', ..., '9,3,2,1');
【讨论】:
MySQL 需要为表中的 每一 行评估 CONCAT_WS 函数。这可能会使用索引,但它会完全扫描索引,所有 100,000,000 行。【参考方案5】:您正在做的方式是在我机器上的 mysql 版本中给出正确的结果。我正在使用v5.5.55
。也许您正在使用旧的。请检查一下。
如果您仍想在自己的版本中解决此问题,或者上述解决方案不起作用,请阅读下一个解决方案。
我仍然不清楚这里所有列的数据类型和范围。所以我假设数据类型是整数,范围在 0 到 9 之间。如果是这种情况,您可以按照下面给出的方式轻松执行此操作。
select * from s1 where x0+10*x1+100*y1+1000*y2 in (4356,..., 9321);
【讨论】:
使用这种方法,MySQL 将无法对索引(x0,x1,y1,y2)
使用范围扫描操作。 MySQL 将在 where 子句中为表中 100,000,000 行中的每一行评估该表达式。以上是关于IN子句中的MySQL多列的主要内容,如果未能解决你的问题,请参考以下文章
使用 sqlalchemy 查询使用多列 where in 子句