我应该使用JOIN函数还是在循环结构中运行多个查询?

iSa*_*iSa 5 php mysql optimization

我有这2个mysql表:TableA和TableB

TableA
*ColumnAId
*ColumnA1
*ColumnA2
TableB
*ColumnBId
*ColumnAId
*ColumnB1
*ColumnB2

在PHP中,我想拥有这种多维数组格式

$array = array(
    array(
        'ColumnAId' => value,
        'ColumnA1' => value,
        'ColumnA2' => value,
        'TableB' => array(
            array(
                'ColumnBId' => value,
                'ColumnAId' => value,
                'ColumnB1' => value,
                'ColumnB2' => value
            )
        )
    )
);
Run Code Online (Sandbox Code Playgroud)

这样我就可以用这种方式循环它

foreach($array as $i => $TableA) {
    echo 'ColumnAId' . $TableA['ColumnAId'];
    echo 'ColumnA1' . $TableA['ColumnA1'];
    echo 'ColumnA2' . $TableA['ColumnA2'];
    echo 'TableB\'s';
    foreach($value['TableB'] as $j => $TableB) {
        echo $TableB['...']...
        echo $TableB['...']...
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,查询MySQL数据库的最佳方法或正确方法是什么,以便我可以实现这一目标?

解决方案1 ---我正在使用的那个

$array = array();
$rs = mysqli_query("SELECT * FROM TableA", $con);
while ($row = mysqli_fetch_assoc($rs)) {
    $rs2 = mysqli_query("SELECT * FROM Table2 WHERE ColumnAId=" . $row['ColumnAId'], $con);
    // $array = result in array
    $row['TableB'] = $array2;
}
Run Code Online (Sandbox Code Playgroud)

我怀疑我的代码导致它总是查询数据库.

溶液2

$rs = mysqli_query("SELECT * FROM TableA JOIN TableB ON TableA.ColumnAId=TableB.ColumnAId");
while ($row = mysqli_fet...) {
    // Code
}
Run Code Online (Sandbox Code Playgroud)

第二个解决方案只查询一次,但是如果我在TableA中有数千行,而TableB中有数千行,每个TableB.ColumnAId(1 TableA.ColumnAId = 1000 TableB.ColumnAId),那么这个解决方案2需要花费很多时间而不是解决方案1?

Gli*_*ire 5

所提出的两种解决方案都不是最佳的,但解决方案1是不可预测的,因此固有的问题!

在处理大型数据库时,您学到的第一件事就是"执行查询的最佳方式"通常取决于数据库中的因素(称为元数据):

  • 有多少行.
  • 你要查询多少个表.
  • 每行的大小.

因此,不太可能有针对您的问题的银弹解决方案.您的数据库与我的数据库不同,如果您需要最佳性能,则需要对不同的优化进行基准测试.

您可能会发现在数据库中应用和构建正确的索引(以及理解MySQL中索引的本机实现)会为您做更多的事情.

有些查询的黄金规则应该很少被破坏:

  • 不要在循环结构中执行它们.由于经常是诱人的,创建连接,执行查询和获得响应的开销很高.
  • 避免SELECT *,除非必要.选择更多列将显着增加SQL操作的开销.
  • 了解你的指数.使用该EXPLAIN功能可以查看正在使用的索引,优化查询以使用可用的查询并创建新查询.

因此,在这两个中,我会选择第二个查询(SELECT *仅替换您想要的列),如果您有时间进行优化,可能有更好的方法来构建查询.

然而,速度应该成为你在这个唯一的考虑因素,有很大的理由不使用建议之一:

可预测性:为什么读锁是一件好事

其中一个答案表明,将表锁定很长一段时间是件坏事,因此多查询解决方案是好的.

我认为这不可能离真相更远.实际上,我认为在许多情况下,运行单个锁定SELECT查询的可预测性是运行该查询的一个更大的参数,而不是优化和速度优势.

首先,当我们SELECT在MyISAM或InnoDB数据库(MySQL的默认系统)上运行(只读)查询时,会发生的事情是表是读锁定的.这可以防止任何WRITE操作发生在表上,直到读取锁定被放弃(我们的SELECT查询完成或失败).其他SELECT查询不受影响,因此如果您正在运行多线程应用程序,它们将继续工作.

这种延迟是件好事.为什么,你可能会问?关系数据完整性.

让我们举一个例子:我们正在运行一个操作来获取当前在游戏中一群用户的清单中的项目列表,所以我们这样做:

SELECT * FROM `users` JOIN `items` ON `users`.`id`=`items`.`inventory_id` WHERE `users`.`logged_in` = 1;
Run Code Online (Sandbox Code Playgroud)

如果在此查询操作期间用户将项目交易给另一个用户,会发生什么?使用此查询,我们可以看到当我们启动查询时的游戏状态:项目存在一次,在我们运行查询之前拥有它的用户的清单中.

但是,如果我们在循环中运行它会发生什么?

根据用户在我们阅读他的详细信息之前或之后进行交易,以及我们阅读两个玩家的库存的顺序,有四种可能性:

  1. 该项目可以显示在第一个用户的库存中(扫描用户B - >扫描用户A - >项目交易或扫描用户B - >扫描用户A - >交易项目).
  2. 该项目可以显示在第二个用户的库存中(交易项目 - >扫描用户A - >扫描用户B或交易项目 - >扫描用户B - >扫描用户A).
  3. 该项目可以在两个清单中显示(扫描用户A - >交易项目 - >扫描用户B).
  4. 该项目既不会显示在用户的库存中(扫描用户B - >交易项目 - >扫描用户A).

这意味着我们无法预测查询结果或确保关系完整性.

如果你计划在星期二午夜向物品ID为1000000的人提供5000美元,我希望你手头有10万美元.如果您的程序依赖于在拍摄快照时唯一的唯一项目,则可能会使用此类查询引发异常.

锁定很好,因为它可以提高可预测性并保护结果的完整性.

注意:您可以强制循环锁定事务,但它仍然会更慢.

哦,最后,使用准备好的声明!

永远不应该有一个如下所示的声明:

mysqli_query("SELECT * FROM Table2 WHERE ColumnAId=" . $row['ColumnAId'], $con);
Run Code Online (Sandbox Code Playgroud)

mysqli准备语句的支持.阅读它们并使用它们,它们将帮助您避免数据库发生可怕的事情.


Alm*_* Do 2

肯定是第二种方式。嵌套查询是一件丑陋的事情,因为每次嵌套查询都会获得所有查询开销(执行、网络等),而单个查询JOIN查询将执行一次 - 即所有开销只会执行一次。

简单的规则是一般情况下不要循环使用查询。可能会有例外,如果一个查询太复杂,那么由于性能原因应该进行拆分,但在某种情况下只能通过基准和度量来显示。