在没有Union或Union All的情况下组合多个Selects

时间:2013-11-08 08:52:28

标签: mysql

我有一个大量的查询,用于在很多表(每个表有数千行)上执行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)

但是,我觉得这有些不安全,因为查询可能会更新这些单独的选择查询之间的数据。 有没有办法改进我的select语句查询集,这样它只被视为一个事务(不需要写入)所以所有数据都将被锁定并使用共享读锁并返回。

其他信息

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

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

2 个答案:

答案 0 :(得分:0)

考虑到你的约束,最好的调用是在发出每个连续的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

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

答案 1 :(得分:0)

我想我会通过代码示例提供两种可能的解决方案及其各种好处。其中一个解决方案是从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    
}