如何跨多个表检索大型数据集并避免循环查询

时间:2013-06-17 23:10:01

标签: mysql

首先对不起,如果问题已经得到解答,我在这里搜索了谷歌并且无法找到我的答案。这个问题可能还没有被问到,但它隐藏在所有的&#34之下;只需使用LEFT JOIN"并且"将它存储在数组中"答案。

我需要在多个表中加载大量数据(然后将其插入另一个数据库引擎,但这并不重要,我需要优化我的SELECT)。

表格布局如下所示:

表A带有a_id字段 表B带有a_id和b_id字段 表C带有b_id和c_id字段 ......(另外3-4级这样)。

我目前以这种方式访问​​数据(伪代码):

query1 = SELECT ... FROM TableA WHERE something=$something

foreach query1 as result1:

    query2 = SELECT ... FROM TableB WHERE b_id=result1.a_id

    foreach query2 as result2:
        query3 = SELECT ... FROM TableC WHERE bc_id=result2.b_id

            foreach query3 as result3:
                // Another few levels of this, see the millions of SELECTs coming?

到目前为止我找到的唯一解决方案是:

  1. 使用慢速方式并发送多个查询(当前解决方案,完成我的小测试集需要很长时间)
  2. 使用大量的LEFT JOIN将所有数据放在一个查询中。涉及传输大量数据数千次,因此客户端的一些奇特逻辑将其再次拆分为适当的表,因为每行将包含其父表的内容。 (我使用OOP,每个表映射到一个对象,每个对象包含其他对象)。
  3. 将表A中的每个对象存储在一个数组中,然后加载所有表B,存储到数组中,继续在表C上。适用于小集,但是我的数量很大,不适合ram完全没有。
  4. 有没有办法避免在这样的循环中每秒进行10k次查询?

    (我正在使用PHP,从MySQL转换为MongoDB,如果有帮助的话,这样可以更好地处理嵌套对象)

    编辑:似乎对我尝试做什么以及为什么会产生一些混淆。我将尝试更好地解释:我需要批量转换为新结构。新结构非常有效,甚至不用担心。我从头开始重新构建一个非常古老的网站,并选择MongoDB作为我的存储引擎,因为我们有很多这样的嵌套数据,对我来说效果很好。切换回MySQL对我来说甚至都不是一个选择,新的结构和代码已经很好地建立起来了,我已经在这方面工作了大约一年了。 我不是在寻找优化当前架构的方法,我不能。数据就是这样,我需要读取整个数据库。一旦。然后我就完成了它。

    我需要做的一切,是从旧网站导入数据,处理并转换它,以便我可以将它插入我们的新网站。 MySQL来了:旧网站是一个非常普通的PHP / MySQL网站。我们有很多桌子(实际上约有70个)。我们没有很多用户,但每个用户都有大量数据跨越7个表。

    我目前正在做什么,就是我循环每个用户(1个查询)。对于这些用户中的每一个(70k),我加载表A,每个用户包含10-80行。然后我在A的每个循环上查询表B(所以,10-80乘70k),其中每个A包含1-16行。表C中包含每行的1-4行。我们现在在4 * 80 * 70k查询要做。然后我为每个C. E设置D,1-32行,每个D为1-16行.F每个E为1-16行。表F有几百万行。

    问题

    • 我最终对MySQL服务器进行了数千次(如果不是数百万次)查询,其中生产数据库甚至不在我的本地机器上,而是在5-10ms之外。即使在0.01毫秒,我也只有几个小时的网络延迟。我创建了一个本地副本,因此我的受限测试集运行速度非常快,但是下载几GB这样的数据还需要很长时间。

    • 我可以将成员表保存在RAM和表A中,这样我就可以一次性下载每个数据库,而不是进行数千次查询,但是一旦在表B中进行,那么跟踪这将是一个真正的混乱在内存中,特别是因为我使用PHP(至少是命令行),它使用的内存比C ++程序要多得多,因为我可能有严格的RAM控制。所以这个解决方案也不起作用。

    • 我可以将所有表连接在一起,但如果它适用于2-3个表,那么对7个表执行此操作将导致额外的巨大带宽损失,从服务器传输相同数据数百万次而无需使用(同时也使代码真的很复杂,以适当的顺序将它们拆分)。

    问题:有没有办法不经常查询数据库?比如,告诉MySQL服务器有一个程序或者我需要这个顺序的所有这些数据集,所以我不必每行重新查询一次,所以数据库只是不断地为我吐出数据?当前的问题只是我做了很多查询,脚本和数据库几乎都处于空闲状态,因为一个人总是在等待另一个。查询本身实际上是非常快速的,对索引的int字段进行基本准备的SELECT查询。

    这是我过去常常使用MySQL的问题,直到现在才真正给我带来麻烦。在当前状态下,如果不是几天完成,脚本需要几个小时。它不是那么糟糕,但如果我能做得更好,我会很高兴知道。如果没有,那么好吧,我只是等待它完成,至少它将运行最多3-4次(2-3次测试运行,让用户检查他们的数据是否正确转换,修复错误,再试一次,以及最后一次错误修正的运行。)

    提前致谢!

2 个答案:

答案 0 :(得分:0)

假设您的7个表由ID链接,请执行以下操作

第一次查询

'SELECT * FROM table_a WHERE a_id IN (12,233,4545,67676,898999)'
// store the result in $result_of_first_query

然后执行foreach并在逗号分隔变量(csv)中选择要在下一个查询中使用的ID

foreach($result_of_first_query as $a_row_from_first_table)
{
    $csv_for_second_query = $csv_for_second_query.$a_row_from_first_table['b_id'].",";
}

$csv_for_second_query = trim($csv_for_second_query,", "); // problem is we will have a lot of duplicate entries
$temp_arr = array(); // so lets remove the duplicates
$temp_arr = explode(",",$csv_for_second_query);  // explode values in array
$temp_arr = array_unique($temp_arr);  // remove duplicates
$csv_for_second_query = implode(",",$temp_arr);  // create csv string again. ready!

现在为你的第二个表,只需1个查询就可以获得JOIN所需的所有值(不是通过mysql,我们将用php执行此操作)

第二次查询

'SELECT * FROM table_b where a_id IN ('.$csv_for_second_query.')'
// store the result in $result_of_second_query;

然后我们只需要以编程方式连接两个数组。

$result_a_and_b = array(); // we will store the joined result of every row here

// lets scan every row from first table
foreach($result_of_first_query as $inc=> $a_row_from_first_table)
{
    // assign every row from frist table to result_a_and_b 
    $result_a_and_b[$inc]['a']=$a_row_from_first_table;

    $inc_b=0; // counter for the joins that will happen by data from second table

    // for every row from first table we will scan every row from second table
    // so we need this nested foreach
    foreach($result_of_second_query as $a_row_from_second_table)
    {
        // are data need to join? if yes then do so! :)
        if($a_row_from_first_table['a_id']==$a_row_from_second_table['a_id'])
        {
            $result_a_and_b[$inc]['b'][$inc_b]=$a_row_from_second_table; // "join" in our "own" way :)
            ++$inc_b; // needed for the next join
        }
    }
}

现在我们有这个格式的数组$ result_a_and_b:

$result_a_and_b[INDEX]['a']
$result_a_and_b[INDEX]['b'][INDEX]

所以对于2个查询,我们得到一个类似于TABLE_A_ROWS_NUMBER + 1的结果(一个是第一个表的初始查询)

像这样继续做你想要的多个级别。

  1. 使用链接表
  2. 的ID查询数据库
  3. 以CSV字符串
  4. 获取ID
  5. 使用WHERE id IN(11,22,33,44,55,.....)进行下一次查询
  6. 以编程方式加入
  7. 提示:您可以使用unset()释放临时变量的内存。

    我相信我在你的问题中回答“有没有办法不经常查询数据库?”

    注意:代码没有测试错别字,也许我错过了逗号或两个 - 或者可能不是

    我相信你可以得到点:)希望它有所帮助!

答案 1 :(得分:0)

谢谢大家的回答。我得出结论,我实际上无法以其他方式做到这一点。

我自己的解决方案是在localhost上设置副本数据库(如果快照足够,则只是副本)。这样,它可以减少网络延迟,并允许脚本和数据库达到100%的CPU使用率,而且如果不完全重新组织我的脚本,它似乎是最快的。

当然,这仅适用于一次性脚本。处理这个问题的正确方法是我现在得到的两个答案的混合:在线程中使用多个无缓冲连接,并逐个处理(从表A加载50行,在ram中存储,加载与表A相关的所有数据)表B,存储在RAM中,然后处理所有这些并继续从表A)。

无论如何,谢谢你的答案!