因此,为了简化生活,我为CodeIgniter创建了一个子查询库。
将其放在CodeIgniter Wiki上,但我从未真正看过它。因此,您能告诉我是否有什么我需要改进的地方,或者我真的不应该做的事情?
P.S.如果需要,请随意使用。
P.P.S.
join_range
是用于解决此问题的辅助方法。P.P.P.S.最新版本可在此处找到。
class Subquery{
var $CI;
var $db;
var $statement;
var $join_type;
var $join_on;
function __construct(){
$this->CI =& get_instance();
$this->db = array();
$this->statement = array();
$this->join_type = array();
$this->join_on = array();
}
/**
* start_subquery - Creates a new database object to be used for the subquery
*
* @param $statement - SQL statement to put subquery into (select, from, join, etc.)
* @param $join_type - JOIN type (only for join statements)
* @param $join_on - JOIN ON clause (only for join statements)
*
* @return A new database object to use for subqueries
*/
function start_subquery($statement, $join_type='', $join_on=1){
$db = $this->CI->load->database('', true);
$this->db[] = $db;
$this->statement[] = $statement;
if(strtolower($statement) == 'join'){
$this->join_type[] = $join_type;
$this->join_on[] = $join_on;
}
return $db;
}
/**
* end_subquery - Closes the database object and writes the subquery
*
* @param $alias - Alias to use in query
*
* @return none
*/
function end_subquery($alias=''){
$db = array_pop($this->db);
$sql = "({$db->_compile_select()})";
$alias = $alias!='' ? "AS $alias" : $alias;
$statement = array_pop($this->statement);
$database = (count($this->db) == 0)
? $this->CI->db: $this->db[count($this->db)-1];
if(strtolower($statement) == 'join'){
$join_type = array_pop($this->join_type);
$join_on = array_pop($this->join_on);
$database->$statement("$sql $alias", $join_on, $join_type);
}
else{
$database->$statement("$sql $alias");
}
}
/**
* join_range - Helper function to CROSS JOIN a list of numbers
*
* @param $start - Range start
* @param $end - Range end
* @param $alias - Alias for number list
* @param $table_name - JOINed tables need an alias(Optional)
*/
function join_range($start, $end, $alias, $table_name='q'){
$range = array();
foreach(range($start, $end) AS $r){
$range[] = "SELECT $r AS $alias";
}
$range[0] = substr($range[0], 7);
$range = implode(' UNION ALL ', $range);
$sub = $this->start_subquery('join', 'inner');
$sub->select($range, false);
$this->end_subquery($table_name);
}
}
示例用法
此查询:
SELECT `word`, (SELECT `number` FROM (`numbers`) WHERE `numberID` = 2) AS number
FROM (`words`) WHERE `wordID` = 3
将变为:
$this->db->select('word')->from('words')->where('wordID', 3);
$sub = $this->subquery->start_subquery('select');
$sub->select('number')->from('numbers')->where('numberID', 2);
$this->subquery->end_subquery('number');
#1 楼
我个人认为您对事情的处理方式错误,可以轻松地将查询字符串传递到select
方法中,并将第二个参数设置为true来绕过反引号。因此输出会将子查询字符串放置在主查询中查询选择。
我会按照以下方式进行操作:
class MyModel extends Model
{
public function getRows()
{
//Create a subquery and render it to a stirng
$sub = $this->db->select('number')->from('numbers')->where('numberID', 2)->_compile_select();
//Clear the data from the CI Arrays
$this->db->_reset_select();
//Build the main query passing in the sub-query and disabling backticks
$this->db->select("word,(" . $sub . ")", false)->where('wordID', 3);
//Get the results
$result = $this->get("words");
}
}
@ _compile_select()
@ _reset_select( )
首先,请允许我声明上面的代码可能无法完全正常运行,因为我没有测试机器atm,但是我知道这是可能的,并且您不需要所有指定了额外的逻辑。
对我来说似乎很简单,而无需创建新的
$db
。我还建议您将上面的逻辑封装到一个类中,以便您可以传递对象的周围环境并简化其工作。上面是一个POC
概念:
class InnerQuery extends CI_DB_active_record
{
public function __construct()
{
}
public function __call($method,$params = array())
{
//Remove methods that modify the database
switch(strtolower($method))
{
case 'get':
case 'count_all_results':
case 'get_where':
trigger_error("Cannot use {$method} in InnerQuery");
break;
}
return $this;
}
public function compile()
{
return "(" . $this->_compile_select() . ")";
}
public function __tostring()
{
return $this->compile();
}
}
好,因此上述类在控制器中扩展了与
$this->db
相同的对象,因此您可以使用所有方法来构建查询suc h as $this->InnerQuery->select("item as item_key")->from("inner_table")->where("foo","zed");
您应该禁用更改数据库或运行任何查询的父方法,因为它仅用于构建选择字符串。
因此,您应该能够执行以下操作:
$this->db->select("word")->where('wordID', 3);
$this->db->select($this->InnerQuery,false);
将使用DB类来构建查询,并且可以将其传递到外部选择中,并且
__tostring
将返回带有括号的(SELECT ...)
并将其传递给主查询。评论
\ $ \ begingroup \ $
我不想在主数据库对象上调用_compile_select()和_reset_select()。这意味着我必须在其余查询之前声明所有子查询,而我不想这样做。另外,该库的重点是对此进行抽象。
\ $ \ endgroup \ $
–火箭危险品
2011年1月27日15:05
\ $ \ begingroup \ $
但是我也说过,我也建议您将上述逻辑封装到一个类中,以便您可以传递对象的周围环境并简化生活。这将解决该问题
\ $ \ endgroup \ $
– RobertPitt
2011年1月27日15:08
\ $ \ begingroup \ $
我最初尝试扩展活动记录类,但是失败了。我想我做错了。我真的很喜欢您的方法,当我有时间更新库时,我可能会这样做。
\ $ \ endgroup \ $
–火箭危险品
2011年1月28日下午5:09
\ $ \ begingroup \ $
没问题,对于上述所有混乱,深表歉意,希望您能一起获得更稳定的课堂:)
\ $ \ endgroup \ $
– RobertPitt
2011年1月29日在12:03
#2 楼
我在这里可能会遗漏某些东西,但是对我来说,似乎您有一个将预先构建的查询传递到的类?我想拥有一个子查询的构建方式与顶级查询相同?
评论
\ $ \ begingroup \ $
您本身并没有传递预构建的查询。 start_subquery返回您(对)CodeIgniter的数据库对象(的引用)。然后,您可以在该对象上调用活动查询方法。 end_subquery从db对象获取查询,将其包装在()中,然后将其添加到主查询中。
\ $ \ endgroup \ $
–火箭危险品
2011年1月20日,0:28
\ $ \ begingroup \ $
我喜欢这种方法,比自己编写要好得多。现在我有时间仔细阅读它,我不确定您可以做很多不同的事情。 A +代码,您是否考虑过将代码作为CI问题跟踪器中的增强功能?
\ $ \ endgroup \ $
–冰雹
2011年1月20日,9:13
\ $ \ begingroup \ $
我将其发布在CodeIgniter Wiki上。
\ $ \ endgroup \ $
–火箭危险品
2011年1月20日下午14:28
#3 楼
使用此功能
$this->db->where('`store_id` NOT IN (SELECT `store_id` FROM `user_store`)', NULL, FALSE);
检查此
评论
\ $ \ begingroup \ $
嗯,谢谢,但是我已经知道了。这个问题是从2011年1月开始的,我实际上是在2011年7月在代码中添加了对WHERE的支持。请在此处检查代码的最新版本:github.com/NTICompass/CodeIgniter-Subqueries
\ $ \ endgroup \ $
–火箭危险品
2012年4月11日在13:24
\ $ \ begingroup \ $
“使用此”答案不是“代码评论”。代码审查工作不仅要通知研究人员有效的方法,还应告知其合理性的原因。
\ $ \ endgroup \ $
– mickmackusa
20 Mar 9 '20 at 20:36
评论
为什么$ db是数组?@Time Machine:$ db是一个数组,因为每次调用start_subquery时,它都会创建一个新的数据库对象。这允许子查询中的子查询。
能给我们一个例子吗?
@RobertPitt:我在问题中添加了一个示例。
如果有人好奇,可以在这里获取最新版本:github.com/NTICompass/CodeIgniter-Subqueries