繁体   English   中英

合并多个选择而没有并集或全部并集

[英]Combine multiple Selects without Union or Union All

我有一个庞大的查询,该查询曾经对很多表(每个表有成千上万行)执行UNION ALL,然后在返回之前输出到临时表。

旧表格:

 SELECT *
FROM   (SELECT `a` AS `Human readable A`,
               `b` AS `Human readable B`,
               `c` AS `Human readable C`,
        FROM   `table1`
        UNION ALL
        SELECT
               `a` AS `Human readable A`,
               `b` AS `Human readable B`,
               `c` AS `Human readable C`,
        FROM   `table2`
        UNION ALL
        SELECT
              `a` AS `Human readable A`,
              `b` AS `Human readable B`,
              `c` AS `Human readable C`,

        FROM `table3`
) AS temp_table 

该查询几乎杀死了数据库(查询需要20分钟到61分钟之间的任何时间),在这段时间内CPU完全耗尽。

我发现为每个表运行一个单独的SELECT语句最多只花了几秒钟 ,并决定将它们合并到位于不同物理服务器上的应用程序级别,这是一个额外的好处(下面的伪代码)。

    $result1 =  SELECT
                      `a` AS `Human readable A`,
                      `b` AS `Human readable B`,
                      `c` AS `Human readable C`,

                FROM `table1`

    $result2 =  SELECT
                      `a` AS `Human readable A`,
                      `b` AS `Human readable B`,
                      `c` AS `Human readable C`,

                FROM `table2`

    $result3 =  SELECT
                      `a` AS `Human readable A`,
                      `b` AS `Human readable B`,
                      `c` AS `Human readable C`,

                FROM `table3`

$result4 = merge($result1, $result2, $result3)

但是,我觉得这有点不安全,因为查询可能会更新这些单独的选择查询之间的数据。 有没有一种方法可以改善我的选择语句查询集,使其仅作为一个事务处理 (无需写操作),因此所有数据将被共享的读取锁锁定并返回。

附加信息

我预计原始表单会花费更长的时间,因为它花费大量的CPU时间来重新创建/排序组合表中的索引,而我不需要这样做(我只需要将结果附加在一起)。

  • 所有表的结构完全相同
  • 请注意,每个表大约有34 a AS Human readable A a AS。由于数据与不同的项目相关,因此数据被分为不同的表。
  • 此特定查询中有20个联合(21个表)。
  • 使用InnoDB表获取数据。 我知道这在CPU上比MyIsam更为密集,但是在了解了MyIsam的各种缺点之后,我不愿意切换存储引擎。
  • 没有WHERE子句(通过拆分成表,数据已经“预先分组”)

考虑到您的约束,最好的调用是在发出每个连续的SELECT之前显式地锁定表:

SET autocommit=0; -- optional, but this is where and how you must start the transaction if you need one
LOCK TABLES t1 READ, t2 READ, t3 READ;
SELECT a FROM t1;
SELECT a FROM t2;
SELECT a FROM t3;
UNLOCK TABLES; -- beware: implicit COMMIT

除非有某种法律要求将这些数据保存在多个表中,否则您确实应该坚持验证所有这些表合并到一个表中。

我以为我会通过代码示例提供两种可能的解决方案及其各种好处。 解决方案之一是从RandomSeed的答案中“窃取”:

if ($READING_ONLY_INNODB_TABLES)
{
    /**
     * - Since these tables are innodb, we can make use of its 'REPEATABLE READ'
     * isolation level
     * - Locking the entire tables directly is slightly faster, but this method 
     * allows us to have a consistent view of the database without implementing
     * ANY locks (which would block other processes). 
     * It may be easier to think of them as locking as this results in the same 
     * result in terms of consistency (innodb even handles phantom reads at this level)
     * - MyIsam does not support REPEATABLE READ, hence this cannot be used for it
     */
   $query = 
       'START TRANSACTION WITH CONSISTENT SNAPSHOT;'. # --auto sets "SET autocommit=0"
       $queries_string . # --This is the series of selects
       'COMMIT;';
}
else
{
    /**
     * This is a lower resource intensive, 'generic' way (works with MyISAM) that will wait until it can read lock
     * all the tables before reading. This way we should 'force' a 
     * 'repeatable read'/consitent view.
     */
    $query = 
        'SET autocommit=0;'. # starts the transaction
        'LOCK TABLES ' . $lock_tables_string . ';' . # Automatically commits anything before this
        $queries_string . # This is the series of selects from the tables we just locked
        'UNLOCK TABLES;'; # commits a transaction if any tables currently have been locked with LOCK TABLES    
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM