5

我有这 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
            )
        )
    )
);

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

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['...']...
    }
}

我的问题是,查询 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;
}

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

解决方案2

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

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

4

4 回答 4

6

提出的两种解决方案都可能不是最优的,但解决方案 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;

如果在此查询操作期间,用户将商品交易给另一个用户,会发生什么情况?使用这个查询,我们可以看到我们开始查询时的游戏状态:该项目存在一次,在我们运行查询之前拥有它的用户的库存中。

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

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

  1. 该项目可以显示在第一个用户的库存中(扫描用户 B -> 扫描用户 A -> 已交易项目或扫描用户 B -> 扫描用户 A -> 已交易项目)。
  2. 该项目可以显示在第二个用户的库存中(项目交易 -> 扫描用户 A -> 扫描用户 B 或项目交易 -> 扫描用户 B -> 扫描用户 A)。
  3. 该项目可以显示在两个库存中(扫描用户 A -> 交易项目 -> 扫描用户 B)。
  4. 该物品可能不会显示在用户的任何一个库存中(扫描用户 B -> 交易的物品 -> 扫描用户 A)。

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

如果您打算在周二午夜向物品 ID 为 1000000 的人赠送 5,000 美元,我希望您手头有 10,000 美元。如果您的程序依赖于在拍摄快照时唯一的项目是唯一的,那么您可能会在这种查询中引发异常。

锁定是好的,因为它增加了可预测性并保护了结果的完整性

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

哦,最后,使用准备好的语句!

永远不应该有一个看起来像这样的语句:

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

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

于 2013-08-16T13:24:11.577 回答
2

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

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

于 2013-08-16T13:13:04.573 回答
2

如果您想在应用程序代码中对数据进行算法评估(我认为这是正确的做法),则根本不应该使用 SQL。SQL 被设计成一种人类可读的方式来从关系数据库中请求计算获得的数据,这意味着,如果你只是使用它来存储数据,并在代码中进行计算,那么无论如何你都做错了。

在这种情况下,您应该更喜欢使用不同的存储/检索可能性,例如键值存储(那里有持久存储,并且较新版本的 MySQL 也为 InnoDB 公开了键值接口,但它仍然使用关系用于键值存储的数据库,也就是该工作的错误工具)。

如果您仍然想使用您的解决方案:

基准。

我经常发现发出多个查询可能比单个查询更快,因为 MySQL 必须解析更少的查询,优化器要做的工作更少,而且 MySQL 优化器通常会失败(这就是 STRAIGHT 之类的原因存在 JOIN 和索引提示)。即使它没有失败,多个查询可能仍然更快,具体取决于底层存储引擎以及尝试一次访问数据的线程数(锁定粒度 -这仅适用于混合更新查询- MyISAM 和InnoDB 默认为 SELECT 查询锁定整个表)。再说一次,如果您不使用事务,您甚至可能使用这两种解决方案获得不同的结果,因为如果您使用多个查询而不是单个查询,数据可能会在查询之间发生变化。

简而言之:您的问题比您发布/要求的内容以及通用答案可以提供的内容更多。

关于您的解决方案:如果您的环境 a) 数据更改很常见和/或 b) 您有许多并发运行的线程(请求)访问和更新您的表(锁定粒度在拆分时更好),我更喜欢第一个解决方案查询,以及查询的可缓存性);如果您的数据库在不同的网络上,例如网络延迟是一个问题,那么您可能会更好地使用第一个解决方案(但我认识的大多数人在同一台服务器上都有 MySQL,使用套接字连接,而本地套接字连接通常不会' t有很多延迟)。

情况也可能会根据 for 循环实际执行的频率而改变。

再次:基准


要考虑的另一件事是内存效率和算法效率。后一种情况在这两种情况下都约为 O(n),但根据您用于连接的数据类型,两种情况中的任何一种情况都可能更糟。例如,如果你使用字符串来加入(你真的不应该,但你没有说),在更多依赖于 php 的解决方案中的性能还取决于 php 哈希映射算法(php 中的数组实际上是哈希映射)和 a冲突,而 mysql 字符串索引通常是固定长度的,因此,根据您的数据,可能不适用。

对于内存效率,多查询版本肯定更好,因为在两种解决方案中无论如何你都有 php 数组(这在内存方面效率非常低!),但连接可能会根据几种情况使用临时表(通常它应该't,但在某些情况下确实如此 - 您可以使用 EXPLAIN 来检查您的查询)

于 2013-08-16T13:48:14.333 回答
0

在某些情况下,您应该使用 limit 以获得最佳性能

如果你想显示 1000 行和一些单个查询(主数据)

你应该跑 1000 次,限制在 10-100 之间

然后在查询中使用 WHERE IN 通过单个查询获取主数据的外键。然后计算您的唯一数据以限制主数据。

例子

选择productID, date from transaction_product limit 100

获取所有productID并使其唯一

然后从 master_product WHERE IN (1,2 3 4) 限制 4 中选择价格(从唯一的总数中计数)

foreach(transaction) master_poduct[productID]

于 2017-03-03T18:57:51.720 回答