如何在 MySQL 中重用一个巨大的查询

How to reuse a huge query in MySQL

我有一个非常大的查询,它从许多 table 中获取数据并按所有非计算列对它们进行分组。而且我需要多次重用此查询,但要使用其他分组和过滤。在 MSSQL 中,我为此目的使用 CTE 或临时 table,但 MySQL 不支持 CTE,我不能在同一查询中多次引用临时 table(这是MySQL 的可悲和不合逻辑的限制)。

SELECT 
    t1.VideoId, 
    t1.RegionId, 
    t1.CountryId, 
    t1.PerCountryCount,
    t2.PerRegionCount
FROM (
    SELECT 
        VideoId, 
        RegionId, 
        CountryId, 
        SUM(PlayCount) PerCountryCount
    FROM TrackedData
    GROUP BY VideoId, RegionId, CountryId
) t1
INNER JOIN (
    SELECT 
        VideoId, 
        RegionId, 
        SUM(PlayCount) PerRegionCount
    FROM TrackedData
    GROUP BY VideoId, RegionId
) t2

此示例已简化但显示了问题所在。 t1 查询有更详细的数据,我想在 t2 查询中重用它,因为在实际项目中很难在 t1 查询中获取数据(百万行、许多过滤器和分组等)。我想重用这个查询的第二个原因是查询长度。我不想重复只更改了大约 60 行代码的查询。

MySQL 中的解决方法是创建多个临时 table。您可以使用临时 table 作为来源来填充另一个。这是我们使用的方法。 (是的,使用单个临时 table 会更有效,但是 MySQL 不允许多次引用同一个临时 table。)


另一种方法是创建一个常规 table 而不是临时 table。这有很多缺点。一个大问题是被放弃的 tables 不会自动删除。如果你走这条路,你无疑会想要:

  • 为这些 "temporary" 工作使用单独的数据库 tables
  • 一个定期安排的定期清理例程,以丢弃旧的和废弃的 tables
  • table 的严格命名约定,table名称使用特殊前缀,后跟日期时间字符串和会话 ID,然后是 "name" table.

清理例程可以根据年龄(基于日期时间字符串,但要注意更改时区设置的客户端的含义)和SHOW PROCESSLIST 中不再存在会话。 (我在那里使用了 "and" 这个词……我们想要删除满足其中任何一个条件的 table。)

创建这些工作的代码 tables 应该删除这些 tables,但即使这样,也会有异常终止而留下 tables。


(就个人而言,我会避免使用第二种方法,而只是使用多个临时 tables。)

作为使用多个临时 tables 的演示:

  CREATE TEMPORARY TABLE _t1_ AS
  SELECT VideoId
       , RegionId
       , CountryId
       , SUM(PlayCount) AS PerCountryCount
    FROM TrackedData
   GROUP BY VideoId, RegionId, CountryId
  ;
  CREATE INDEX _t1_IX1
    ON _t1_ (VideoId, RegionId, PerCountryCount);

  CREATE TEMPORARY TABLE _t2_ AS
  SELECT VideoId
       , RegionId
       , SUM(PerCountryCount) AS PerRegionCount 
    FROM _t1_
   GROUP BY VideoId, RegionId
  ;
  CREATE INDEX _t2_IX1
    ON _t2_ (VideoId, RegionId, PerRegionCount)
  ;

请注意,在 t1 上定义 suitable 索引将提高填充 t2.

的性能

您可以使用视图的概念重用查询:

https://dev.mysql.com/doc/refman/8.0/en/view-algorithms.html

简单的例子,实际上big_ass_select_query可能有一堆连接

DROP VIEW IF EXISTS big_ass_select_query;

CREATE VIEW big_ass_select_query AS
SELECT col1 from table1;

将其用作普通查询

SELECT * FROM big_ass_select_query WHERE col1 > 1;