2016-06-25 4 views
1

Я новичок в stackoverflow, примите мои извинения за длинный вопрос. У меня есть большая база данных розничной торговли. Мы хотели подготовить отчет, чтобы получить баланс открытия и закрытия продуктов, продукция имеет номер партии, и каждая партия имеет уникальный серийный номер, который присваивается нескольким местоположениям. Задача состоит в том, чтобы получить начальные балансы каждой партии выбранного продукта для каждого местоположения склада. Если местоположение не указано в фильтре отчетов, отчет должен давать результат для выбранного продукта и всех его партий.MySQL/PHP, ForEach Loops slow

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

Теперь я могу рассчитать баланс закрытия, поскольку у меня есть все значения. Это работает нормально, но когда запрос выполняется для 20 складов, а для одного продукта почти 20+ партий, отчет занимает много времени, а использование процессоров MySQL достигает 200%.

Я попытался оптимизировать запросы, мои таблицы были правильно проиндексированы. В таблицах много данных. миллионы записей. Мне нужно посоветовать, как улучшить мой код или методы, что я делаю неправильно и как сделать отчет быстрее.

Я использую заголовок для моего приложения, а следующий код.

foreach($locations as $loc){ 
    //if batch is selected put it in array 
    if($query_array['batch_no'] != ''){ 
     $batches[] = $query_array['batch_no']; 
    }else{ 
    // or get all batches from the inventory table for the location 
     $this->db->select('distinct(batch_no)'); 
     $this->db->from('product_details'); 
     $this->db->where('product_id',$query_array ['product']); 
     $this->db->where('warehouse_id',$loc); 
     $query = $this->db->get(); 
     foreach($query->result() as $row){ 
      $batches[] = $row->batch_no; 
     } 
     $query->free_result(); 
    } 

    foreach($batches as $batch){ 

     //GIN IN Opening Balance 
     $this->db->select('IFNULL(SUM(gi.qty),0) as gin_in',FALSE); 
     $this->db->from('gin as g'); 
     $this->db->join('gin_items as gi','gi.gin_id=g.id'); 
     $this->db->where('gi.product_id',$query_array ['product']); 
     $this->db->where('DATE(g.creation_date) < ',$query_array ['date_from']); 
     $this->db->where('g.to_location_id', $loc); 
     $this->db->where_in('gi.batch_no',$batch); 
     $this->db->where_in('gi.status',2); 
     $this->db->where('g.status',3); 
     $query = $this->db->get(); 

     if($query->num_rows()==1) 
     { 
      $var1=$query->row()->gin_in; 
      $query->free_result(); 
     } 

     // SUM (Return Invoices for a specific product, for this particular store, 
     // before this date range) -> $var2 
     $this->db->select('IFNULL(SUM(ii.qty),0) as sale_return_in',FALSE); 
     $this->db->from('return_sales_invoice rs'); 
     $this->db->join('return_invoice_items as ii','ii.invoice_id=rs.id'); 
     $this->db->where('ii.medicine_id',$query_array ['product']); 
     $this->db->where_in('ii.batch_no',$batch); 
     $this->db->where_in('rs.location_id',$loc); 
     $this->db->where('rs.dated < ',$query_array ['date_from']); 

     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $var2=$query->row()->sale_return_in; 
      $query->free_result(); 
     } 


     //SUM (Sales Invoices of a specific product, from this particular store, 
     // before this date range) -> $var3 
     $this->db->select('IFNULL(SUM(ii.qty),0) as sale_out',FALSE); 
     $this->db->from('sales_invoice si'); 
     $this->db->join('invoice_items as ii','ii.invoice_id=si.id'); 

     $this->db->where('ii.medicine_id',$query_array ['product']); 
     $this->db->where_in('ii.batch_no',$batch); 
     $this->db->where_in('si.location_id',$loc); 
     $this->db->where('si.dated < ',$query_array ['date_from']); 

     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $var3=$query->row()->sale_out; 
      $query->free_result(); 
     } 

     // SUM (GIN of a specific product, from this particular store, 
     // before this date range) -> $var4 
     // if from location then minis stock 
     $this->db->select('IFNULL(SUM(gi.qty),0) as gin_out',FALSE); 
     $this->db->from('gin as g'); 
     $this->db->join('gin_items as gi','gi.gin_id=g.id'); 
     $this->db->where('DATE(g.creation_date) < ',$query_array ['date_from']); 
     $this->db->where('gi.product_id',$query_array ['product']); 
     $this->db->where_in('gi.batch_no',$batch); 
     $this->db->where_in('g.from_location_id',$loc); 
     $this->db->where('gi.status',2); 
     $this->db->where('g.status',3); 
     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $var4=$query->row()->gin_out; 
      $query->free_result(); 
     } 
     $op_bal = ($var1 + $var2) - ($var3 + $var4); 

     //--------------------------------------------------------------------------------- 

     $where_from = "DATE(g.creation_date) >='" . $query_array ['date_from'] . "'"; 
     $where_to = "DATE(g.creation_date) <='" . $query_array ['date_to'] . "'"; 

     $rs_where_from = "DATE(rs.creation_date) >='" . $query_array ['date_from'] . "'"; 
     $rs_where_to = "DATE(rs.creation_date) <='" . $query_array ['date_to'] . "'"; 

     $si_where_from = "DATE(si.creation_date) >='" . $query_array ['date_from'] . "'"; 
     $si_where_to = "DATE(si.creation_date) <='" . $query_array ['date_to'] . "'"; 

     //GIN IN Opening Balance 
     $this->db->select('IFNULL(SUM(gi.qty),0) as gin_in',FALSE); 
     $this->db->from('gin as g'); 
     $this->db->join('gin_items as gi','gi.gin_id=g.id'); 
     $this->db->where($where_from); 
     $this->db->where($where_to); 
     $this->db->where('gi.product_id',$query_array ['product']); 
     $this->db->where_in('gi.batch_no',$batch); 
     $this->db->where_in('g.to_location_id', $loc); 
     $this->db->where('g.status',3); 
     $this->db->where('gi.status',2); 
     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $g_stock_in=$query->row()->gin_in; 
      $query->free_result(); 
     } 

     // SUM (Return Invoices for a specific product, for this particular store, 
     // before this date range) -> $var2 
     $this->db->select('IFNULL(SUM(ii.qty),0) as sale_return_in',FALSE); 
     $this->db->from('return_sales_invoice rs'); 
     $this->db->join('return_invoice_items as ii','ii.invoice_id=rs.id'); 
     $this->db->where('ii.medicine_id',$query_array ['product']); 
     $this->db->where($rs_where_from); 
     $this->db->where($rs_where_to);     
     $this->db->where_in('ii.batch_no',$batch); 
     $this->db->where_in('rs.location_id',$loc); 
     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $s_stock_in=$query->row()->sale_return_in; 
      $query->free_result(); 
     } 


     //SUM (Sales Invoices of a specific product, from this particular store, 
     // before this date range) -> $var3 
     $this->db->select('IFNULL(SUM(ii.qty),0) as sale_out',FALSE); 
     $this->db->from('sales_invoice si'); 
     $this->db->join('invoice_items as ii','ii.invoice_id=si.id'); 
     $this->db->where('ii.medicine_id',$query_array ['product']); 
     $this->db->where_in('ii.batch_no',$batch); 
     $this->db->where_in('si.location_id',$loc); 
     $this->db->where($si_where_from); 
     $this->db->where($si_where_to); 

     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $s_stock_out=$query->row()->sale_out; 
      $query->free_result(); 
     } 

     // SUM (GIN of a specific product, from this particular store, 
     // before this date range) -> $var4 
     // if from location then minis stock 
     $this->db->select('IFNULL(SUM(gi.qty),0) as gin_out',FALSE); 
     $this->db->from('gin as g'); 
     $this->db->join('gin_items as gi','gi.gin_id=g.id'); 
     $this->db->where($where_from); 
     $this->db->where($where_to); 
     $this->db->where('gi.product_id',$query_array ['product']); 
     $this->db->where_in('gi.batch_no',$batch); 
     $this->db->where_in('g.from_location_id',$loc); 
     $this->db->where('g.status',3); 
     $this->db->where('gi.status',2); 
     $query = $this->db->get(); 

     if($query->num_rows()==1){ 
      $g_stock_out=$query->row()->gin_out; 
      $query->free_result(); 
     } 
     $qty_in=$g_stock_in+$s_stock_in; 
     $qty_out=$g_stock_out+$s_stock_out; 

     $productName=$this->getProductName($query_array ['product']); 
     $locationName=$this->getLocationName($loc); 

     $data [] = array (
      'location' => $locationName, 
      'product' => $productName, 
      'batchno' => $batch , 
      'op_bal' => $res['op_bal'] , 
      'qty_in' => $qty_in, 
      'qty_out' => $qty_out, 
      'cl_bal' => ($res['op_bal'] + $qty_in) - $qty_out 
     );  

    } 
} 
return $data; 
+1

Добро пожаловать в SO! Быстрая конструктивная критика вашего вопроса: пример кода довольно длинный и может препятствовать волонтерам пытаться его разгадать. Кроме того, вопросы эффективности запросов обычно получают наилучшие результаты, если вы показываете структуру данных и структуры индексов, а также фактические запросы, а не код Ignit Code.Наконец, числовое имя пользователя, а не ваше собственное имя еще больше препятствует людям инвестировать время в вас: многие такие пользователи никогда не возвращаются. Опять же, добро пожаловать! –

ответ

2

Вы признаете, что в этой программе отчетов вы много чего делаете. В частности, вы делаете много запросов с одним результатом. Это часто считается вредным для производительности, по сравнению с написанием меньшего количества запросов, которые возвращают строку для каждого элемента.

Одно из возможных решений вашей проблемы: Запустите программу отчетов в ночное время и не потейте паршивые характеристики. Вы получите работу. Такой вид ночного пакетного запуска довольно распространен в реальных ситуациях.

Другое решение: обрабатывать базу данных как машину, чтобы возвращать таблицы, а не только значения. Обновите свои запросы, чтобы вернуть несколько результатов. У вас будет гораздо меньше запросов, и вы сможете воспользоваться планировщиком запросов СУБД, чтобы сразу получить много информации. Например, вместо того, чтобы зацикливаться на месте в php, заставьте MySQL сделать это.

Это даст вам список партий, складов и продуктов, по одному ряду.

SELECT DISTINCT batch_no, warehouse_id, product 
     FROM product_details 
    ORDER BY batch_no, warehouse_id, product 

Далее, я думаю, этот запрос будет дать вам один из ваших необходимых результатов, но и для всех партий, складов и продуктов. «Я думаю», потому что вы не раскрыли свою схему или бизнес-правила. Этот запрос связан с объединением первых двух запросов в вашей программе.

SELECT SUM(gi.qty) gin_in, p.batch_no, p.warehouse_id, p.product 
    FROM product_details p 
    JOIN gin g  ON g.to_location_id = p.warehouse_id 
    JOIN gin_items gi ON gi.gin_id = g.id AND gi.product_id = p.product 
WHERE DATE(g.creation_date) < $date_from 
    AND g.status = 3 
GROUP BY p.batch_no, p.warehouse_id, p.product 

Вы получаете идею: сделать ваши запросы возвращают много строк, а не выполнение запроса лишь Gazillion раз для возврата в одной строке каждый раз.

+0

Спасибо, что нашли время, чтобы изучить вопрос. Запуск отчета ночью - хорошая идея, пока я не попробую другие предложения, которые вы дали. –

Смежные вопросы