Как можно избежать запросов MySQL в блоке FOREACH, когда SUM используется по определенным критериям? - PullRequest
0 голосов
/ 24 февраля 2019

По сути, то, что у меня есть , работает , но не является лучшей практикой.

Вот мой текущий вывод, который я и хочу, но мне интересно, могу ли я запросить этоданные с одним (хотя и сложным) запросом вместо 5-10 отдельных запросов.(3-7 в выражении foreach, которое я хотел бы исключить.)

Example

Если бы я мог получить все данные в одном запросе,поместите его в массив, а затем извлеките его в таблицу соответственно, это будет более эффективно.

Но я не очень разбираюсь в MySQL и не знаю, как запрашивать несколько сумм конкретных данных, для которых требуется конкретный WHERE.критерии ...

Вот пример моих таблиц: (реальные таблицы более сложные, но я думаю, что это упрощено, так что это понятно.)

+-------------------------------------+
|              crop_plans             |
+-------------------------------------+
| ID | advisor_id | acres | crop_year |
+----+------------+-------+-----------+
| 1  | 3          | 1000  | 2018      |
+----+------------+-------+-----------+
| 2  | 1          | 1500  | 2019      |
+----+------------+-------+-----------+
| 3  | 2          | 300   | 2017      |
+----+------------+-------+-----------+
| 4  | 2          | 600   | 2018      |
+----+------------+-------+-----------+
| 5  | 3          | 800   | 2017      |
+----+------------+-------+-----------+

+---------------------+
|        users        |
+---------------------+
| user_id | full_name |
+---------+-----------+
| 1       | Dude A    |
+---------+-----------+
| 2       | Dude B    |
+---------+-----------+
| 3       | Dude C    |
+---------+-----------+

+----------------------+
|       advisors       |
+----------------------+
| advisor_id | user_id |
+------------+---------+
| 1          | 1       |
+------------+---------+
| 2          | 2       |
+------------+---------+
| 3          | 3       |
+------------+---------+

Вот мои запросы иподумав, а затем код:

Сначала я вытащил получить имя советника (и ID):

SELECT ad.advisor_id AS this_advisor_id, ua.full_name AS full_name
FROM advisors AS ad 
JOIN users AS ua ON ad.user_id = ua.user_id 
WHERE advisor_id > 1;

(да, пропуская первый)

Затем для каждого из этих результатов я запрашиваю СУММУ акров за данный год, где советник является советником в этой «строке»:

SELECT * 
FROM 
    (
        SELECT SUM(acres) AS current_year_acres 
        FROM crop_plans
        WHERE crop_year = ? AND advisor_id = ?
    ) a CROSS JOIN  (
        SELECT SUM(acres) AS last_year_acres 
        FROM crop_plans 
        WHERE crop_year = ? AND advisor_id = ?
    ) b CROSS JOIN  (
        SELECT SUM(acres) AS year_before_acres
        FROM crop_plans 
        WHERE crop_year = ? AND advisor_id = ?
    ) c;

И, наконец, я запрашиваюитоги: (не зависит от советника)

SELECT * 
FROM 
    (
        SELECT SUM(acres) AS current_year_acres 
        FROM crop_plans
        WHERE crop_year = ?
    ) a CROSS JOIN  (
        SELECT SUM(acres) AS last_year_acres 
        FROM crop_plans 
        WHERE crop_year = ?
    ) b CROSS JOIN (
        SELECT SUM(acres) AS year_before_acres 
        FROM crop_plans 
        WHERE crop_year = ?
    ) c

И с этими результатами я отображаюих с PHP и т. д., чтобы это выглядело так, как вы видите на скриншоте выше.

Вот полный (рабочий, но не идеальный) код:

<?php
echo '<table class="table table-striped table-hover dashboard"><thead><tr>';
echo '<th>Advisor</th><th>'.$current_year.'</th><th>'.$last_year.'</th><th>'.$year_before.'</th>'; // Header
echo '</tr></thead><tbody>';

$sql_retreive = $con->prepare("SELECT ad.advisor_id AS this_advisor_id, ua.full_name AS full_name FROM advisors AS ad JOIN users AS ua ON ad.user_id = ua.user_id WHERE advisor_id > 1;");
$sql_retreive->execute();
$result = $sql_retreive->get_result(); 
if($result->num_rows >0 ){
    while($row=$result->fetch_assoc()){
   extract($row); // assigns respective values
   echo '<tr>'; // start row
   echo '<td><!--'.$this_advisor_id.'-->'.$full_name.'</td>'; // First column

            // query database for each advisor
            $sql_retreive_in = $con->prepare("SELECT * FROM (SELECT SUM(acres) AS current_year_acres FROM crop_plans WHERE crop_year = ? AND advisor_id = ?) a CROSS JOIN  (SELECT SUM(acres) AS last_year_acres FROM crop_plans WHERE crop_year = ? AND advisor_id = ?) b CROSS JOIN  (SELECT SUM(acres) AS year_before_acres FROM crop_plans WHERE crop_year = ? AND advisor_id = ?) c;");
            $bind_process_in = $sql_retreive_in->bind_param('sisisi',$current_year,$this_advisor_id,$last_year,$this_advisor_id,$year_before,$this_advisor_id); 
            $sql_retreive_in->execute();
            $result_in = $sql_retreive_in->get_result(); 
            if($result_in->num_rows >0 ){
                while($row_in=$result_in->fetch_assoc()){
                extract($row_in); // assigns respective values
                echo '<td>'.format($current_year_acres,'lunit').'</td><td>'.format($last_year_acres,'lunit').'</td><td>'.format($year_before_acres,'lunit').'</td>'; // Remaining columns
                    } // end of inside while
                } // end of inside num_rows >0

        echo '</tr>'; // end row
        } // end of while
    } // end of num_rows >0

// Final totals line
echo '<tr>'; // start row
echo '<td> Total</td>'; // First column

    $sql_retreive = $con->prepare("SELECT * FROM (SELECT SUM(acres) AS current_year_acres FROM crop_plans WHERE crop_year = ?) a CROSS JOIN  (SELECT SUM(acres) AS last_year_acres FROM crop_plans WHERE crop_year = ?) b CROSS JOIN  (SELECT SUM(acres) AS year_before_acres FROM crop_plans WHERE crop_year = ?) c");
    $bind_process = $sql_retreive->bind_param('sss',$current_year,$last_year,$year_before); 
    $sql_retreive->execute();
    $result = $sql_retreive->get_result(); 
    if($result->num_rows >0 ){
        while($row=$result->fetch_assoc()){
       extract($row); // assigns respective values
       echo '<td>'.format($current_year_acres,'lunit').'</td><td>'.format($last_year_acres,'lunit').'</td><td>'.format($year_before_acres,'lunit').'</td>'; // Remaining columns
            } // end of while
        } // end of num_rows >0

    echo '</tr>'; // end row
    // echo "<tr><td colspan='100%'></td></tr>"; 
    echo "</tbody></table>";
    ?>

Я быочень благодарен за любые хорошие предложения о том, как я могу исключить запрос в операторе foreach (предварительно собрав все эти значения с помощью запроса и поместив их в массив.)

1 Ответ

0 голосов
/ 24 февраля 2019

Вы должны быть в состоянии сделать это в одном запросе, используя условное агрегирование :

SELECT 
    ua.full_name AS Advisor,
    SUM(CASE WHEN cp.year = 2017 THEN cp.acres ELSE 0 END) AS Total_Acres_2017,
    SUM(CASE WHEN cp.year = 2018 THEN cp.acres ELSE 0 END) AS Total_Acres_2018,
    SUM(CASE WHEN cp.year = 2019 THEN cp.acres ELSE 0 END) AS Total_Acres_2019
FROM advisors AS ad 
INNER JOIN users AS ua ON ad.user_id = ua.user_id 
INNER JOIN crop_plans AS cp WHERE cp.advisor_id = ad.advisor_id
WHERE ad.advisor_id > 1
GROUP BY ad.advisor_id, ua.full_name
UNION ALL
SELECT 
    'Total', 
    SUM(CASE WHEN cp.year = 2017 THEN cp.acres ELSE 0 END),
    SUM(CASE WHEN cp.year = 2018 THEN cp.acres ELSE 0 END),
    SUM(CASE WHEN cp.year = 2019 THEN cp.acres ELSE 0 END)
FROM crop_plans AS cp 
WHERE cp.advisor_id > 1

Первый запрос вычисляет общее количество акров для каждого консультанта и года, используя условное агрегированиеповернуть набор результатов.Обратите внимание, что это работает только для фиксированного набора столбцов (если вам нужно изменить окно года, запрос должен быть изменен).

Первый набор результатов UNION редактируется вторым запросом, который вычисляет итоги по годам, в основном, используя ту же технику.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...