我有一张桌子line_item { id: int, price: decimal, quantity: int, [other:...] }
。这张桌子很大,约。 2800万行。现在我想获得前1000行order by f(price, quantity, [other...])
,f
是一个任意函数。最好的方法是什么?
我想到了2个解决方案:
order by
和limit
。这种方式可能很慢,因为我认为MySQL计算每行f
的结果然后对它们进行排序。f
的结果。这种方式不利于可伸缩性,因为我可能想在不同的上下文中使用多个函数f
(f1
,f2
...)。我真的希望有第三种解决方案比他们更好。
答案 0 :(得分:0)
我正考虑另一种选择:
创建一个只包含id和f列的临时表。
创建第二个临时表(temp_table2)并在其中插入以下结果:
SELECT TOP 1000 id, f
FROM temp_table
ORDER BY f
这应该比你提到的其他2个选项运行得更快,因为在这里你只需要使用2列。
最后,您可以通过将此seoncd临时表连接到原始表来选择最终结果。
SELECT line_item.* --or just the columns you need
FROM temp_table2
INNER JOIN line_item
ON temp_table2.id = line_item.id
您还可以尝试执行您提到的第一个选项,如果使用我建议的临时表,请查看是否有任何显着的性能改进。 在许多情况下使用临时表可以缩短执行时间,但不是所有时间都是如此 - 所以最好是尝试两者并查看哪些更好。
答案 1 :(得分:0)
(对不起,这是一个否定的答案,但这就是生活。)
如果您接受“最佳解决方案”的速度只是您所经历的速度的两倍,那么请接受@ Zsuzsa's。
我在这里告诉你,如果不对f(...)做些什么就无法进行优化。原因如下:
优化器没有看到WHERE子句,但看到带有表达式的ORDER BY。因此,它意识到评估查询的唯一方法是执行“表扫描”(即,读取所有行),评估每行的函数,将结果保存在tmp中table(有28M行),对tmp表进行排序,并提供1000行。
可以将该函数的任何复制到WHERE子句中以过滤掉某些行吗?如果是这样,tmp表可能会更小。或者,如果你幸运的话,也许可以设计一些INDEX,这样就不必进行全表扫描。
您要修改所有行吗?或者这是一种“只写”表?也就是说,一旦写入,一行是否永远不会改变?在此基础上,可以为所有“旧”行预先计算f()吗?如果是这样,将它存储在某处并添加索引 - 噗!即时结果。
f()的常见部分是某个日期范围的测试吗? (大表通常有某种日期。大表上的查询经常询问“最近”的项目。)如果是这样,可以从f()中取出。然后我们可以考虑按日期对表进行PARAGETION。这样,即使f中没有其他任何东西可以优化,“分区修剪”也可以限制要处理的行数。
请显示创建表并讨论这里的一些想法是否可行。