我在工作中使用CodeIgniter,我们的一个模型文件中包含很多子查询。我最初不得不手动编写每个子查询,并想知道是否可以使用活动记录。
因此,为了简化生活,我为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'); 


评论

为什么$ db是数组?

@Time Machine:$ db是一个数组,因为每次调用start_subquery时,它都会创建一个新的数据库对象。这允许子查询中的子查询。

能给我们一个例子吗?

@RobertPitt:我在问题中添加了一个示例。

如果有人好奇,可以在这里获取最新版本:github.com/NTICompass/CodeIgniter-Subqueries

#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