在查询中使用 count_all_results 或 get_compiled_select 和 $this->db->get('table') 列出 table 两次?

Using count_all_results or get_compiled_select and $this->db->get('table') lists table twice in query?

如何在 运行 查询之前使用 get_compiled_select 或 count_all_results 而不添加两次 table 名称?当我在其中任何一个之后使用 $this->db->get('tblName') 时,我得到错误:

Not unique table/alias: 'tblProgram'

SELECT * FROM (`tblProgram`, `tblProgram`) JOIN `tblPlots` ON `tblPlots`.`programID`=`tblProgram`.`pkProgramID` JOIN `tblTrees` ON `tblTrees`.`treePlotID`=`tblPlots`.`id` ORDER BY `tblTrees`.`id` ASC LIMIT 2000

如果我没有在 count_all_results 或 $this->db->get() 中使用 table 名称,那么我会得到一个错误,指出没有使用 table .我怎样才能让它只设置一次 table 名称?

public function get_download_tree_data($options=array(), $rand=""){

//join tables and order by tree id
   $this->db->reset_query();
   $this->db->join('tblPlots','tblPlots.programID=tblProgram.pkProgramID');
   $this->db->join('tblTrees','tblTrees.treePlotID=tblPlots.id');
   $this->db->order_by('tblTrees.id', 'ASC');

//get number of results to return
   $allResults=$this->db->count_all_results('tblProgram', false);

//chunk data and write to CSV to avoid reaching memory limit
   $offset=0;
   $chunk=2000;
   $treePath=$this->config->item('temp_path')."$rand/trees.csv";
   $tree_handle=fopen($treePath,'a');
   while (($offset<$allResults)) {
      $this->db->limit($chunk, $offset);  
      $result=$this->db->get('tblProgram')->result_array();
      foreach ($result as $row) {
           fputcsv($tree_handle, $row);
      }    
      $offset=$offset+$chunk;
    }
                    
    fclose($tree_handle);
    return array('resultCount'=>$allResults);
 }

您的代码中的缺陷在于它旨在 select 某些记录的子集及其在同一查询中的总计数。这在 MySQL 中是不可能的,因此您无法生成这样的查询,因此,您会收到上述错误。问题是如果你做一个

select ... from t where ... limit 0, 2000

然后你得到最多2000条记录,所以,如果符合条件的总记录数大于限制,那么你不会从上面准确地得到计数,所以,在这种情况下你需要一个

select count(1) from t where ...

这意味着您需要构建您的实际查询(您的 count_all_results 调用下方的代码),查看结果数是否达到限制。如果结果数没有达到限制,那么你不需要执行单独的查询来获取计数,因为你可以计算 $offset * $chunk + $recordCount。但是,如果您获得尽可能多的记录,那么您将需要构建另一个查询,而不需要 order_by 调用,因为计数独立于您的排序和获取计数。

要计算查询 返回多少行,基本上必须执行所有工作。也就是说,获取计数,然后执行查询是不切实际的;你也可以直接查询。

如果您的目标是通过获取一些行加上总计数来“分页”,那实际上是两个独立的操作(可以组合起来看起来像一个操作。)

如果目标是估计行数,那么SHOW TABLE STATUSSELECT FROM information_schema.TABLES WHERE ...会给你一个估计.

如果你想看看有没有,说“至少100行”,那么这可能是实用的:

SELECT 1 FROM ... WHERE ... ORDER BY ... LIMIT 99,1

看看你能不能回来。但是,这可能 也可能不会 有效,具体取决于索引以及 WHEREORDER BY。 (向我们展示查询,我可以详细说明。)

使用 OFFSET 进行分块非常低效。如果没有可用的索引,那么它实际上对每个块执行整个查询。如果有可用的索引,块就会越来越慢。这里讨论了为什么 OFFSET 不利于“分页”,以及一个有效的解决方法: Pagination 。它讨论了如何“记住你离开的地方”作为一种有效的分块技术。每个块获取 100 到 1000 行。

$this->db->count_all_results()

统计返回结果的数量count_all_results()

计算返回结果的数量很有用——如果将至少有一行的代码部分传递给零行,通常会出现错误。如果不处理零结果的可能性,应用程序可能会变得不可预测地不稳定,并可能向恶意用户提供有关应用程序架构的提示。确保正确处理零结果是我们在这里要关注的重点。

允许您确定特定 Active Record 查询中的行数。查询将接受查询生成器限制器,例如 where()、or_where()、like()、or_like() 等。示例:

echo $this->db->count_all_results('my_table');  // Produces an integer, like 25
$this->db->like('title', 'match');
$this->db->from('my_table');
echo $this->db->count_all_results(); // Produces an integer, like 17

但是,此方法还会重置您可能已传递给 select() 的任何字段值。如果需要保留它们,可以将 FALSE 作为第二个参数传递:

echo $this->db->count_all_results('my_table', FALSE);

get_compiled_select()

方法$this->db->get_compiled_select();在codeigniter v3.0中引入,编译活动记录查询而不实际执行它。但这并不是一种全新的方法。在 CI 的旧版本中,它类似于 $this->db->_compile_select();,但该方法在更高版本中已被保护,因此无法回调。

// Note that the second parameter of the get_compiled_select method is FALSE
$sql = $this->db->select(array('field1','field2'))
                                ->where('field3',5)
                                ->get_compiled_select('mytable', FALSE);

// ...
// Do something crazy with the SQL code... like add it to a cron script for
// later execution or something...
// ...

$data = $this->db->get()->result_array();

// Would execute and return an array of results of the following query:
// SELECT field1, field1 from mytable where field3 = 5;

注意:- 在您使用查询生成器缓存功能且不重置查询时两次调用 get_compiled_select() 将导致缓存被合并两次。反过来,如果你缓存 select() - select 相同的字段两次。

Rick James 让我走上了正确的道路。我最终不得不使用分页和嵌套查询对结果进行分块。即使对 2000 条记录中的 1 块使用 LIMIT 也会超时。这是我最终得到的代码,它使用 get_compiled_select('tblProgram') 然后 get('tblTrees O1')。由于我没有使用 FALSE 作为 get_compiled_select 的第二个参数,所以在 get() 为 运行.

之前清除了查询
        //grab the data in chunks, write it to CSV chunk by chunk
        $offset=0;
        $chunk=2000;    
        $i=10;  //counter for the progress bar
        
        $this->db->limit($chunk);
        $this->db->select('tblTrees.id');
        
        //nesting the limited query and then joining the other field later improved performance significantly
        $query1=' ('.$this->db->get_compiled_select('tblProgram').') AS O2';
        $this->db->join($query1, 'O1.id=O2.id');
       
        $result=$this->db->get('tblTrees O1')->result_array();
        $allResults=count($result);
        $putHeaders=0;
        
        $treePath=$this->config->item('temp_path')."$rand/trees.csv";
        $tree_handle=fopen($treePath,'a');  
        
        //while select limit returns the limit
        while (count($result)===$chunk) {
            $highestID=max(array_column($result, 'id'));
            
            //update progres bar with estimate
            if ($i<90) {
              $this->set_runStatus($qcRunId, $status = "processing", $progress = $i);
              $i=$i+1;
            }

            //only get the fields the first time
            foreach ($result as $row) {
                if ($offset===0 && $putHeaders===0){
                    fputcsv($tree_handle, array_keys($row));
                    $putHeaders=1;
                }
                fputcsv($tree_handle, $row);
            }  
            
            //get the next chunk
            $offset=$offset+$chunk;
            $this->db->reset_query();
            $this->make_query($options);
            $this->db->order_by('tblTrees.id', 'ASC');
            $this->db->where('tblTrees.id >', $highestID);
            $this->db->limit($chunk);
            $this->db->select('tblTrees.id');
            $query1=' ('.$this->db->get_compiled_select('tblProgram').') AS O2';
            $this->db->join($query1, 'O1.id=O2.id');
                   
            $result=$this->db->get('tblTrees O1')->result_array();
            
            $allResults=$allResults+count($result);
        }
            //write out last chunk 
            foreach ($result as $row) {
                fputcsv($tree_handle, $row);
            }          
            fclose($tree_handle);

            return array('resultCount'=>$allResults);