在MySQL中处理数千条记录

时间:2019-03-18 18:04:09

标签: php mysql

有关该问题的一些背景知识:

我工作的公司提供与Booking.com/Airbnb等类似的服务。物业所有者在网站上列出了他们的物业,并获得了预订。

在月底,我必须生成发票并将其发送到这些属性。为了生成这些发票,我使用了最早可追溯至2018年4月1日的记录。对于特定的房地产,我搜索其相关预订,然后遍历结果集以计算我们的佣金。

随着公司的发展,物业和预订的数量也增加了。现在有超过100家酒店,其中一些酒店有2000多笔预订。要生成一张发票,需要花费相当长的时间,大部分时间要超过一分钟。

我想了解处理此类大型结果集的最佳做法,因为我觉得当前的实现效率很低。

我唯一想做的就是计算每个属性的所有应付金额并将其写入一个单独的表,然后在每次预订时仅修改该金额。然后,我可以只使用该表中的金额来获取一个月底的欠款。但是我不认为这是正确的,因为通常情况下最好的做法是不将派生属性保留在数据库中。

我使用PHP服务器端,Laravel 5.4和MySQL 5.7是DBMS。

任何帮助将不胜感激。谢谢!

PS:我也尝试过使用PHP集合中可用的chunk方法,但是它们没有显着减少时间。

编辑:我错过了一个非常重要的部分。调试时,我可以清楚地看到,大部分时间都在计算/处理成千上万条记录上。不是查询本身。即使网络状况不佳,记录也会在不到10秒的时间内获取,而大多数情况下是不到5秒。我会显示代码,但会在我的工作机上显示它,目前我无法访问它。这是我在家里思考的东西。基本上,代码是这样的:

$properties = Property::where('status', 'Active');
foreach($properties as $property) { // 100+ times
     $bookings = Booking::where('status', 'Approved')->where('checked_in', 1)->where('propId', $property->id)->get();
     $commission = 0.0; // commissions owed to us
     $commissionOta = 0.0; // any commissions owed to OTA's
     foreach($bookings as $booking) { // 2000+ times
          $commission += $booking->commission;
          $commissionOta += $booking->commissionOta;
     }
     $totalReceived = PaymentReceived::where('propId', $property->id)->sum('amount'); // any amounts paid by the property owner
     $property->payable = ($commission + $commissionOta) - $totalReceived;
}

2 个答案:

答案 0 :(得分:2)

不清楚您要什么。 但总的来说,我强烈建议您避免在循环中进行查询:

所有您的代码片段:

$properties = Property::where('status', 'Active');
foreach($properties as $property) { // 100+ times
     $bookings = Booking::where('status', 'Approved')->where('checked_in', 1)->where('propId', $property->id)->get();
     $commission = 0.0; // commissions owed to us
     $commissionOta = 0.0; // any commissions owed to OTA's
     foreach($bookings as $booking) { // 2000+ times
          $commission += $booking->commission;
          $commissionOta += $booking->commissionOta;
     }
     $totalReceived = PaymentReceived::where('propId', $property->id)->sum('amount'); // any amounts paid by the property owner
     $property->payable = ($commission + $commissionOta) - $totalReceived;
}

似乎只等于此查询:

SELECT p.id,
       SUM(b.commission) s_comm,
       SUM(b.commissionOta) s_commOta,
       SUM(b.commission) + SUM(b.commissionOta) - pr.amount payable
FROM Property p
INNER JOIN Booking b
ON b.propOd = p.id
  AND b.status = 'Approved'
  AND checked_in = 1 
LEFT JOIN (
    SELECT pr.propId, SUM(pr.amount) amount
    FROM PaymentReceived pr
    GROUP BY pr.propId
) pr
ON pr.propId = p.id
WHERE p.status = 'Active'
GROUP BY p.id

尝试运行此查询。检查结果集和性能,并尝试使您的代码适应于使用一个查询而不是循环数据库请求。

对于Laravel,它应该类似于:

$result = DB::select('SELECT p.id,
       SUM(b.commission) s_comm,
       SUM(b.commissionOta) s_commOta,
       SUM(b.commission) + SUM(b.commissionOta) - pr.amount payable
FROM Property p
INNER JOIN Booking b
ON b.propOd = p.id
  AND b.status = \'Approved\'
  AND checked_in = 1 
LEFT JOIN (
    SELECT pr.propId, SUM(pr.amount) amount
    FROM PaymentReceived pr
    GROUP BY pr.propId
) pr
ON pr.propId = p.id
WHERE p.status = \'Active\'
GROUP BY p.id');

foreach($result as $r){
    echo 'Property id: '.$r->id.' Commission: '.$r->s_comm. ' Commission Ota: '.$r->commissionOta .' Payable: '.$r->payable ;
}

您应该尝试实施的另一种想法是削减/关闭期间。现在,如果我理解正确,那么您将在每个月或其他某个时期的最开始重新计算所有数据。但是通常您应该存储上一期间的一些平衡结果,并仅根据上一期间的数据重新计算差异。

答案 1 :(得分:0)

好吧,我对“大数据”有一些经验(嗯,不是那么大,但又不小-我正在电信领域发展,并且我有一个呼叫详细信息表,经常超过20万条记录)。我可以告诉你,通过正确的索引搜索非常快。我只是在未索引列上有116803行的表中进行了测试,并得到了结果:

Showing rows 0 - 24 (1608 total, Query took 0.0016 seconds)

现在,在同一表上的索引列上:

Showing rows 0 - 0 (1 total, Query took 0.0008 seconds.)

此外,如果使用联接(左,右,内部等),则将它们放在索引列上。这样,sql不必为每一行搜索整个联接表。

因此,如果您有下一个查询:

SELECT * FROM table_a INNER JOIN table_b ON column_from_a = column_from_b

如果table_b很大,请确保索引column_from_b

另一件事(但稍微复杂一点)是复制。您可以拥有一个SQL群集,比如说3台服务器。您可以将主要应用程序设置为使用其中2个,并保留第3个用于某些密集操作的应用程序。

但是最好的解决方案是查看如何为特定类型的操作提供定制解决方案。没有可以在任何地方实现的通用方法。