MySQL:如何在其他查询中有效地重用查询结果?

时间:2018-04-06 14:40:20

标签: mysql mariadb

我运行完全相同的查询四次,两次作为子查询,每次都收集不同的信息。将第一个查询的结果传递给其他三个查询的最佳方法是什么,因此它不必再运行三次?

平均而言,它返回大约2,000行,但可以是0(在这种情况下我跳过其他三个)到所有的任何地方。主表有近300,000行,每天增长约800,行永远不会被删除,数千行全天更新,多次更新。

我查看了查询缓存,但它看起来并不是一个光明的未来:
自MySQL 5.6 / MariaDB 10.1.7起默认禁用 自MySQL 5.7.20起折旧 在MySQL 8.0中删除

我考虑过将GROUP_CONCAT与IN结合使用,但不知何故,我怀疑使用更大的查询会很好(如果有的话)。

这是我用来格式化其他脚本结果的库,所以原始查询几乎可以是任何东西。通常,它位于索引列上,但使用存储的函数可能非常复杂,需要几分钟。它总是涉及主表,但也可以连接到其他表(但仅用于过滤主表的结果)。

我在CentOS 7上使用Perl 5.16和MariaDB 10.1.32(很快将升级到10.2)。我使用的是prepare_cached和占位符。这个库运行的用户对几个存储函数的表只有SELECT和EXECUTE的访问权限,但是如果需要我可以改变它。

我尽可能地将下面的内容最小化,并尽可能多地使用metasyntactic变量(尖括号内)以试图使逻辑清晰。 id是16个字节,是主表的主键(下面标有a)。

我接受三个参数作为输入。 <tables>始终包含a,并且可能包含a join b on a.id=b.id之类的加入。 <where>可能很简单,如e=3或非常复杂。我还为占位符获取了一系列数据,但我已将其排除在下面,因为它不会影响逻辑。

<search> = FROM <tables> WHERE (<where>)

<foo> = k < NOW() - INTERVAL 3 HOUR
<bar> = j IS NOT NULL OR <foo>
<baz> = j IS NULL AND k > NOW() - INTERVAL 3 HOUR
so <baz> is !<bar>.  Every row should match one or the other

<where> often includes 1 or more of foo/bar/baz

SELECT a.id, b, c, d, <foo> x <search> ORDER BY e, id

SELECT COUNT(*) <search> AND <baz>
I really only need to know if any of the above rows match <baz>

SELECT c, COUNT(*) t, SUM(<bar>) o FROM a WHERE c IN (SELECT c <search> GROUP BY c) GROUP BY c

SELECT d, COUNT(*) t, SUM(<bar>) o FROM a WHERE d IN (SELECT d <search> GROUP BY d) GROUP BY d

最后两个从原始查询中的行获取所有唯一cd的列表,然后计算总行数(而不仅仅是原始查询中的行)的匹配数{ {1}}或c以及其中有多少匹配d。这些结果被转储到哈希中,所以当我遍历原始查询中的行时,我可以查找这些计数。我认为运行这两个查询一次比为每行运行两个较小的查询更有效。

谢谢。

编辑添加解决方案:

临时表就是答案,就像Raymond建议的那样。在我的查询上使用EXPLAIN表示MariaDB已经为每个使用临时表,并在每个完成时删除它。

内部联接仅返回两个表中存在的行。因此,通过创建一个与我的第一个SELECT匹配的临时ID表,然后将其连接到其他SELECT的主表,我只获取我想要的数据,而不必将所有数据复制到临时表。

&#34;要创建临时表,您必须具有CREATE TEMPORARY TABLES特权。会话创建临时表后,服务器不会对表执行进一步的权限检查。创建会话可以对表执行任何操作,例如DROP TABLE,INSERT,UPDATE或SELECT。&#34; - https://dev.mysql.com/doc/refman/5.7/en/create-temporary-table.html

我还发现默认情况下GROUP BY排序,如果你不需要通过告诉它不要对数据进行排序,你可以获得更好的性能。

<bar>

1 个答案:

答案 0 :(得分:1)

我能想到的最好的方法是使用TEMPORARY表。

p.sím使用有效的MySQL SQL代码与主题启动器相同的伪代码

CREATE TEMPORARY TABLE <name> AS ( SELECT FROM <tables> WHERE (<where>) )

<foo> = k < NOW() - INTERVAL 3 HOUR
<bar> = j IS NOT NULL OR <foo>
<baz> = j IS NULL AND k > NOW() - INTERVAL 3 HOUR
so <baz> is !<bar>.  Every row should match one or the other

<where> often includes 1 or more of foo/bar/baz

SELECT a.id, b, c, d, <foo> x FROM <name> ORDER BY e, id

SELECT COUNT(*) FROM <name> WHERE <baz>

SELECT c, COUNT(*) t, SUM(<bar>) o FROM a WHERE c IN (SELECT c FROM <name> GROUP BY c) GROUP BY c

SELECT d, COUNT(*) t, SUM(<bar>) o FROM a WHERE d IN (SELECT d FROM <name> GROUP BY d) GROUP BY d