从嵌套在循环中的MySQL查询结果中提取平均值的更快方法

时间:2019-04-02 18:49:47

标签: php mysql arrays json mysqli

致力于构建一个php文件以创建一个JSON,以便在Web应用程序中使用,以跟踪特定运输路线的运输利润。目标是拥有一组数据,我可以将这些数据插入与公司合作的每个客户的下拉列表中,以跟踪每个客户的运输路线的平均收入和利润。从本质上讲,我需要查询过去两年中每个客户的清单,卡车的组合目的地/原点/类型,然后显示利润,收入和符合较早条件的发货数量的平均值。

我正在使用PHP 7,phpMyAdmin。

$currentDate = date('Y-m-d');
$twoYearsAgo = Date('Y-m-01', strtotime($currentDate . " -2 years + 1 month"));
$customers = "SELECT DISTINCT customer_name FROM `wo_main_alldata` WHERE ship_date BETWEEN '$twoYearsAgo' AND '$currentDate'";
$customerResult = mysqli_query($conn, $customers);
$customerList= array();
while ($row = mysqli_fetch_array($customerResult)) {
    $customerList[] = $row[0];
}
$lanesArray = array();
foreach ($customerList as $customerName){

    $laneData = "SELECT DISTINCT type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state
    FROM wo_main_alldata
    WHERE customer_name = '$customerName' 
    AND pickup_city != ''";
    $lanesResult = mysqli_query($conn, $laneData);
    while ($row2 = mysqli_fetch_array($lanesResult)){
        $equipment = $row2[0];
        $pu_city = $row2[1];
        $pu_state = $row2[2];
        $dest_city = $row2[3];
        $dest_state = $row2[4];
        $laneAverages = "SELECT AVG(proj_revenue), AVG(proj_gross_profit), COUNT(pro_num) FROM wo_main_alldata WHERE type_of_shipment = '$equipment' AND pickup_city = '$pu_city' AND pickup_state = '$pu_state' AND consignee_city = '$dest_city' AND consignee_state = '$dest_state'";
        $lanesAverageResult = mysqli_query($conn, $laneAverages);
        while ($row3 = mysqli_fetch_array($lanesAverageResult)){

        }
        mysqli_free_result($lanesAverageResult);
    }
}

我还没有实现将数据推送到数组以进行输出的详细信息,但是就目前而言,由于在过去的两个客户中有2000多个运输路线,该文件可能需要10分钟才能完成处理年。

有什么方法可以更快地制作这种瓶坯?

编辑:我知道我需要使用准备好的语句,因为现在所有这些操作都是在本地完成的,而我只是在尝试优化运行时间。

编辑2:解决方案!

SELECT customer_name, type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state, AVG( proj_revenue ) , AVG( proj_gross_profit ) , COUNT( pro_num ) 
FROM wo_main_alldata
WHERE ship_date
BETWEEN  '$twoYearsAgo'
AND  '$currentDate'
AND pickup_city !=  ''
GROUP BY customer_name, type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state

2 个答案:

答案 0 :(得分:4)

我真的很犹豫发布答案,因为此处的SQL导致您在PHP中所做的各种额外工作。没有数据库模式和一些示例数据,我感觉我只是在盲目地这样做。例如,DISTINCT这些东西在这里做什么,也许它们是必需的,也许它们是多余的……我无从得知。

这就是这里


这些都是相同的表,因此再次查询相同的数据是没有意义的,例如以这两个查询为例

$customers = "SELECT DISTINCT customer_name FROM `wo_main_alldata` WHERE ship_date BETWEEN '$twoYearsAgo' AND '$currentDate'";

$laneData = "SELECT DISTINCT type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state
FROM wo_main_alldata
WHERE customer_name = '$customerName' 
AND pickup_city != ''";

您可以将它们与以下内容组合:

$laneData = "SELECT DISTINCT
    customer_name,  -- From the first query
    type_of_shipment,
    pickup_city,
    pickup_state,
    consignee_city,
    consignee_state
FROM 
    wo_main_alldata
WHERE 
    ship_date BETWEEN '$twoYearsAgo' AND '$currentDate' 
    AND
    pickup_city != ''
";

第一个查询提取所有“不同”的客户名称,然后循环遍历并使用该名称在同一表中查找下一组数据。

此查找将使您在拳头中使用的独特字符无效,但是(是)您也可以在此处使用它。它之所以无效,是因为第二个查询说:“给我所有记录,其中customer_name =某物”,因此,如果该名称重复存在,则会在第二个查询中找到所有这些记录。当它们分开时,控制foreach ($customerList as $customerName){循环可能很重要。但是,我们不再需要该循环。

当我们将它们组合在一起时,我们将customer_name添加到第二个选择中,同时还添加了WHERE位。然后,我们就可以消除将这些customer_name = '$customerName'捆绑在一起的条件因为当它变成customer_name=customer_name时就不再需要它了,它只是“此行”。

现在看起来还不干净。它还消除了所有这些代码:

$customers = "SELECT DISTINCT customer_name FROM `wo_main_alldata` WHERE ship_date BETWEEN '$twoYearsAgo' AND '$currentDate'";
$customerResult = mysqli_query($conn, $customers);
$customerList= array();
while ($row = mysqli_fetch_array($customerResult)) {
    $customerList[] = $row[0];
}
$lanesArray = array();
foreach ($customerList as $customerName){

    $laneData = "SELECT DISTINCT type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state
    FROM wo_main_alldata
    WHERE customer_name = '$customerName' 
    AND pickup_city != ''";

两个都从同一个表中选择数据,第二个(在循环中)只是​​从第一个查询中获取用户名,然后在同一表中再次查找它。

对于最后一个查询,您(再次)只是回引用相同的数据

 $equipment = $row2[0]; //from previous query on same table
 WHERE type_of_shipment = '$equipment' 

where条件中的所有内容直接来自另一个查询的结果,因此您可以消除它。剩下的就是这个:

SELECT
   AVG(f.proj_revenue),
   AVG(f.proj_gross_profit),
   COUNT(f.pro_num)
FROM (
  SELECT DISTINCT
    customer_name,
    type_of_shipment,
    pickup_city,
    pickup_state,
    consignee_city,
    consignee_state
  FROM 
    wo_main_alldata
  WHERE 
    ship_date BETWEEN '$twoYearsAgo' AND '$currentDate' -- From the first query
    AND
    pickup_city != ''
) as f

我无法真正测试一下,因此您可能需要进行一些调整,尽管有点逻辑,但我还是有点感觉。我很确定顶级查询中的列也必须在内部子查询中。特别是f.proj_revenuef.proj_gross_profitf.pro_num。可能您会得到类似Unknown column 'f.proj_gross_profit' in 'field list'

的信息

有几种方法可以通过再次加入表格来解决此问题。

SELECT
   AVG(m.proj_revenue),
   AVG(m.proj_gross_profit),
   COUNT(m.pro_num)
FROM 
    wo_main_alldata AS m
JOIN
    (
      SELECT DISTINCT
        id,  //<--- id is an issue
        customer_name, 
        type_of_shipment,
        pickup_city,
        pickup_state,
        consignee_city,
        consignee_state
      FROM 
        wo_main_alldata
      JOIN
      WHERE 
        ship_date BETWEEN '$twoYearsAgo' AND '$currentDate'
        AND
        pickup_city != ''
    ) as f
ON f.id = m.id

我真的不确定最好的解决方法,因为我不知道什么必须与众不同。这确实使它复杂化,因为如果您按上述方式放置ID,则可能因每行唯一而使Distinct呼叫中毒。您也许可以在一个查询中完成所有操作:

SELECT DISTINCT
    customer_name, 
    type_of_shipment,
    pickup_city,
    pickup_state,
    consignee_city,
    consignee_state,
    AVG(m.proj_revenue),
    AVG(m.proj_gross_profit),
    COUNT(m.pro_num)
  FROM 
    wo_main_alldata
  WHERE 
    ship_date BETWEEN '$twoYearsAgo' AND '$currentDate'
    AND
    pickup_city != ''

但是我说的太抽象了。不要害怕采用PHPmyAdmin(或您用来管理数据库的任何东西)并在那里直接进行查询。这样一来,您就可以在任何编码之外使用它,并以您想要的方式获得它。

在任何情况下,如果您发现自己来回访问DB以获得相同的数据机会,则可以在一个稍微复杂一些的查询中进行。如果您不太了解SQL,但在PHP上可以执行简单查询并在PHP中进行处理,这是很诱人的。

乍一看,这似乎是“简单”的方法,但是您可以让数据库完成的每一项工作都可以用PHP节省2到3项工作。您的代码将更小,更轻,更简单,更易于阅读。例如(假设您可以按照建议将它们组合),您的代码将变为:

$lanesAverageResult = mysqli_query($conn, $laneAverages); //our new query
while ($row3 = mysqli_fetch_array($lanesAverageResult)){

}

因此,我们只删除了25+行PHP,而使用了稍微复杂一些的查询。

PS 对不起,这很久了。

希望有帮助!

答案 1 :(得分:2)

耗时很长的主要原因之一是因为它对数据库进行了大量单独的调用。据我了解,在某些情况下,每个客户要进行2000多个单独的SQL查询。您将要考虑使用子查询和/或联接将其压缩。

https://www.guru99.com/sub-queries.html

修改 使用子查询来压缩获取每个客户的运输路线的示例将是这样的。 (未经测试的查询,但它给出了有关如何实现子查询的粗略思路)

SELECT AVG(final.proj_revenue), AVG(final.proj_gross_profit), COUNT(final.pro_num) FROM 
    (SELECT proj_revenue, proj_gross_profit, pro_num FROM
        (SELECT DISTINCT 
            type_of_shipment, pickup_city, pickup_state, consignee_city, consignee_state
            FROM wo_main_alldata WHERE customer_name = '$customername' AND pickup_city != ''
        ) as subquery
            WHERE type_of_shipment = subquery.type_of_shipment 
            AND pickup_city = subquery.pickup_city 
            AND pickup_state = subquery.pickup_state 
            AND consignee_city = subquery.consignee_city 
            AND consignee_state = subquery.consignee_state
    ) as final