2016-06-25 7 views
1

내가 stackoverflow에 새로운 소식을 올지라도 제 생각에는 오랜 질문에 사과한다. 소매점에 대한 대규모 데이터베이스가 있습니다. 우리는 제품의 개폐 잔고를 얻기위한 보고서를 준비하고 제품에는 배치 번호가 있으며 모든 배치에는 여러 위치에 할당 된 고유 일련 번호가 있습니다. 문제는 각 창고 위치에 대해 선택한 제품의 각 배치의 기초 잔액을 확보하는 것입니다. 보고서 필터에 위치가 지정되지 않은 경우 보고서는 각 위치의 선택한 제품 및 모든 배치에 대한 결과를 제공해야합니다.MySQL/PHP, ForEach 루프 느림

PHP/PDO를 사용하여 모든 위치를 가져 와서 첫 번째 루프에서 선택한 제품에 대한 모든 배치를 가져오고 배치 루프에서 먼저 여는 균형을 얻습니다. (날짜 필터 이전의 수량 합계), 그 다음 해당 위치에서 해당 제품 배치의 수령액을 계산 한 다음 (판매일 및 종료일 필터에서받은 수량 합계) 판매 된 수량 또는 수량을 다른 창고로 이동합니다 선택한 시작일 및 종료일 필터 내에서 다른 창고로 이동).

저는 이제 모든 값을 가지고 있기 때문에 종가 잔액을 계산할 수 있습니다. 이것은 정상적으로 실행되지만 쿼리가 20 개의 창고에 대해 실행되고 단일 제품에 대해 거의 20 개 이상의 배치가있을 경우 보고서 처리 시간이 오래 걸리고 MySQL CPU 사용이 최대 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! 당신의 질문에 대한 건설적인 비판은 코드 예제가 꽤 길기 때문에 자원 봉사자가 그것을 모방하려고하지 않을 수도 있습니다. 또한 쿼리 성능 질문은 일반적으로 코드 표식 코드가 아닌 데이터 테이블과 인덱스 구조 및 실제 쿼리를 표시 할 때 최상의 결과를 얻습니다.마지막으로, 자신의 이름보다는 숫자로 된 사용자 이름이 사람들로 하여금 시간을 투자하지 못하게합니다. 다시 환영합니다! –

답변

2

당신은이 보고서 프로그램에서 많은 것을하고 있다는 것을 알고 있습니다. 특히 단일 결과 행 쿼리를 많이 만들고 있습니다. 이는 종종 각 항목에 대해 행을 반환하는 쿼리를 적게 작성하는 것과 비교할 때 성능에 좋지 않은 것으로 간주됩니다.

한 가지 가능한 해결책 : 밤에 보고서 프로그램을 실행하고 나쁜 성능을 땀을 내지 마십시오. 너는 일을 끝낼거야. 이런 종류의 밤새 일괄 처리는 실제 상황에서 상당히 일반적입니다.

다른 해결책 : 데이터베이스를 데이터로 처리하여 값이 아닌 테이블을 반환합니다. 여러 개의 결과를 반환하도록 쿼리를 리 팩터합니다. 결국 쿼리가 훨씬 줄어들어 DBMS의 쿼리 플래너를 활용하여 많은 정보를 한 번에 가져올 수 있습니다. 예를 들어, 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

시간을내어 질문을 들여 주셔서 감사합니다. 밤에 보고서를 실행하는 것은 내가 제시 한 다른 제안을 시도 할 때까지는 지금은 좋은 생각입니다. –