在查询中使用 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 STATUS
或SELECT
行 FROM information_schema.TABLES WHERE ...
会给你一个估计.
如果你想看看有没有,说“至少100行”,那么这可能是实用的:
SELECT 1 FROM ... WHERE ... ORDER BY ... LIMIT 99,1
看看你能不能回来。但是,这可能 也可能不会 有效,具体取决于索引以及 WHERE
和 ORDER 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);
如何在 运行 查询之前使用 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 STATUS
或SELECT
行 FROM information_schema.TABLES WHERE ...
会给你一个估计.
如果你想看看有没有,说“至少100行”,那么这可能是实用的:
SELECT 1 FROM ... WHERE ... ORDER BY ... LIMIT 99,1
看看你能不能回来。但是,这可能 也可能不会 有效,具体取决于索引以及 WHERE
和 ORDER 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);