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

9w11ddsr  于 2022-12-07  发布在  其他
关注(0)|答案(4)|浏览(112)

如何在运行查询之前使用get_compiled_select或count_all_results而不将表名添加两次?当我在这两个命令之后使用$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()中使用表名,那么我会得到一个没有使用表的错误。我如何让它只设置一次表名呢?

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);
 }
yvfmudvl

yvfmudvl1#

要计算一个查询 * 将 * 返回多少行,基本上必须执行所有的工作。也就是说,获得计数,然后执行查询是不切实际的;您还不如直接执行查询。
如果您的目标是通过获取一些行加上总数来“分页”,那么这实际上是两个独立的操作(可以组合起来看起来像一个操作)。
如果目标是 * 估计 * 行数,则SHOW TABLE STATUSSELECT Rows FROM information_schema.TABLES WHERE ...会为您提供 * 估计 *。
如果你想看看是否有,比如说“至少100行”,那么这可能是实用的:

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

然后看看是否返回了一行。但是,这可能 * 也可能 * 不是有效的,这取决于索引以及WHEREORDER BY。(向我们展示查询,我可以详细说明。)
使用OFFSET进行分块是非常低效的。如果没有可用的索引,那么它实际上是在为每个块执行整个查询。如果有可用的索引,那么块的速度会越来越慢。下面讨论为什么OFFSET不适合“分页”,以及一个有效的解决方案:Pagination。它讲述了如何“记住你离开的地方“作为一种有效的分块技术。每个分块取100到1000行。

x6h2sr28

x6h2sr282#

您代码中的缺陷在于,它的目的是在同一个查询中选择一些记录的子集及其总数。这在MySQL中是不可能的,因此您无法生成这样的查询,因此,您会得到上面提到的错误。问题是,如果您执行

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

则您将获得最多2000条记录,因此,如果匹配条件的记录总数大于限制,则您将无法从上面的示例中准确获得计数,因此,在这种情况下,您需要

select count(1) from t where ...

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

egdjgwm8

egdjgwm83#

$this-〉数据库-〉所有结果计数()

使用count_all_results()计算返回的结果数
计算返回结果的数量是很有用的--如果一段代码中至少有一行被传递了零行,通常会出现错误。如果不处理零结果的可能性,应用程序可能会变得不可预测的不稳定,并可能向恶意用户给予有关应用程序架构的提示。确保正确处理零结果是我们在这里要重点关注的。
允许您确定特定活动记录查询中的行数。查询将接受查询生成器限制器,如where()或_where()、like()或_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);

获取编译的选择()

codeigniter v3.0中引入了方法$this->db->get_compiled_select();,它编译活动记录查询而不实际执行它。但这不是一个全新的方法。在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;

**注意:-**当您使用Query Builder Caching功能并且不重置您的查询时,对get_compiled_select()的两次调用将导致该高速缓存合并两次。这反过来会导致,如果您正在缓存select()-,则会选择同一字段两次。

8i9zcol2

8i9zcol24#

Rick James让我走上了正确的道路。最后我不得不使用分页和嵌套查询对结果进行分块。在2000条记录中,即使有一条记录使用LIMIT也会超时。这是我最终得到的代码,它使用get_compiled_select('tblProgram '),然后获取因为我没有使用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);

相关问题