【问题标题】:UNION query with codeigniter's active record pattern使用 codeigniter 的活动记录模式的 UNION 查询
【发布时间】:2010-01-11 08:37:03
【问题描述】:

如何使用 PHP CodeIgniter 框架的活动记录查询格式进行 UNION 查询?

【问题讨论】:

    标签: mysql sql codeigniter activerecord union


    【解决方案1】:

    CodeIgniter 的 ActiveRecord 不支持 UNION,因此您只需编写查询并使用 ActiveRecord 的查询方法即可。

    $this->db->query('SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2');
    

    【讨论】:

    • 解释一下,CodeIgniter 的 ActiveRecord 只支持与其支持的所有 SQL 类型兼容的 SQL 特性(或以自己的方式实现)。 ActiveRecord 的想法是将数据库类型抽象为独立于数据库,让人们从 MySQL 迁移到 MSSQL 或其他任何没有重大问题的东西。如果他们试图添加一致,它将与其他数据库类型发生冲突。
    • 独立于数据库,让人们从 MySQL 迁移到 MSSQL 或其他没有大问题的东西
    • 列举一个不支持 UNION 的流行 RDBMS?其他 ORM(± ActiveRecord 语义),例如 SQLAlchemy,为所有数据库后端(包括 SQLite)的 UNION 和 JOIN(各种)提供出色的支持。对于不直接支持它的后端(例如 SQLite),ORM 通过在幕后做更多的工作,同时仍然保持可移植性。在这种特殊情况下,通过手动执行查询,除了 ActiveRecord 系统本身的更高级功能(例如表过滤)之外,您将失去所有外表的可移植性。
    • 这不是纯粹的活动记录查询。我有同样的要求。我得到了解决方案并给出了答案。
    • 如何在这个查询中添加 order by?
    【解决方案2】:

    通过使用 last_query() 进行联合,可能会影响应用程序的性能。因为对于单个联合,它需要执行 3 个查询。即对于“n”联合“n+1”查询。对 1-2 查询联合影响不大。但是,如果将许多查询或具有大数据的表联合起来,就会出现问题。

    此链接对您有很大帮助:active record subqueries

    我们可以将活动记录与手动查询结合起来。 示例:

    // #1 SubQueries no.1 -------------------------------------------
    
    $this->db->select('title, content, date');
    $this->db->from('mytable');
    $query = $this->db->get();
    $subQuery1 = $this->db->_compile_select();
    
    $this->db->_reset_select();
    
    // #2 SubQueries no.2 -------------------------------------------
    
    $this->db->select('title, content, date');
    $this->db->from('mytable2');
    $query = $this->db->get();
    $subQuery2 = $this->db->_compile_select();
    
    $this->db->_reset_select();
    
    // #3 Union with Simple Manual Queries --------------------------
    
    $this->db->query("select * from ($subQuery1 UNION $subQuery2) as unionTable");
    
    // #3 (alternative) Union with another Active Record ------------
    
    $this->db->from("($subQuery1 UNION $subQuery2)");
    $this->db->get();
    

    【讨论】:

    • => $query = $this->db->get(); 有什么用? $query 似乎没有在代码中的任何地方使用。
    • 太棒了!好主意。这是一个真正的联盟。另一个在数据库上运行 2 个查询。我有时想知道开发人员是否关心数据库性能。
    【解决方案3】:

    这是我曾经用过的又快又脏的方法

    // Query #1
    
    $this->db->select('title, content, date');
    $this->db->from('mytable1');
    $query1 = $this->db->get()->result();
    
    // Query #2
    
    $this->db->select('title, content, date');
    $this->db->from('mytable2');
    $query2 = $this->db->get()->result();
    
    // Merge both query results
    
    $query = array_merge($query1, $query2);
    

    不是我最好的作品,但它解决了我的问题。

    注意:我不需要订购结果。

    【讨论】:

    • 如果您的数据正在通过一些异步过程发生变化,那是个坏主意,数据可能会在两个查询之间添加/删除/更改。
    • 更好的方法是使用像@Somnath Muluk 在你上面的帖子中所做的子查询。正如 user9645 所写,在一个单独的进程中进行两个查询是个坏主意。
    【解决方案4】:

    您可以使用以下方法获取模型中的SQL语句:

    $this->db->select('DISTINCT(user_id)');
    $this->db->from('users_master');
    $this->db->where('role_id', '1');
    
    $subquery = $this->db->_compile_select();
    $this->db->_reset_select();
    

    这样,SQL 语句将位于 $subquery 变量中,而无需实际执行。

    你很久以前就问过这个问题,所以也许你已经得到了答案。如果没有,这个过程可能会奏效。

    【讨论】:

    • _compile_select() 是最新版本的 CodeIgniter 中的受保护方法(下划线表示它最初是作为内部方法使用的)。请参阅我的解决方法:stackoverflow.com/a/14270008/560114
    【解决方案5】:

    通过修改 somnath huluks 的答案,我将以下变量和函数添加到 DB_Active_rec 类中,如下所示:

    class DB_Active_records extends CI_DB_Driver
    {
    
       ....
    
       var $unions;
    
       ....
    
        public function union_push($table = '')
        {
            if ($table != '')
            {
                $this->_track_aliases($table);
                $this->from($table);
            }
    
            $sql = $this->_compile_select();
    
            array_push($this->unions, $sql);
            $this->_reset_select();
        }
    
        public function union_flush()
        {
            $this->unions = array();
        }
    
        public function union()
        {
            $sql = '('.implode(') union (', $this->unions).')';
            $result = $this->query($sql);
            $this->union_flush();
            return $result;
        }
    
        public function union_all()
        {
            $sql = '('.implode(') union all (', $this->unions).')';
            $result = $this->query($sql);
            $this->union_flush();
            return $result;
        }
    }
    

    因此,您可以虚拟地使用不依赖于 db_driver 的联合。

    要在此方法中使用 union,您只需进行常规活动记录查询,但调用 union_push 而不是 get。

    注意:您必须确保您的查询具有匹配的列,例如常规联合

    示例:

        $this->db->select('l.tpid, l.lesson, l.lesson_type, l.content, l.file');
        $this->db->where(array('l.requirement' => 0));
        $this->db->union_push('lessons l');
        $this->db->select('l.tpid, l.lesson, l.lesson_type, l.content, l.file');
        $this->db->from('lessons l');
        $this->db->join('scores s', 'l.requirement = s.lid');
        $this->db->union_push();
        $query = $this->db->union_all();
        return $query->result_array();
    

    会产生:

    (SELECT `l`.`tpid`, `l`.`lesson`, `l`.`lesson_type`, `l`.`content`, `l`.`file`
    FROM `lessons` l
    WHERE `l`.`requirement`=0)
    union all 
    (SELECT `l`.`tpid`, `l`.`lesson`, `l`.`lesson_type`, `l`.`content`, `l`.`file`
    FROM `lessons` l
    JOIN `scores` s ON `l`.`requirement`=`s`.`lid`)
    

    【讨论】:

      【解决方案6】:

      我找到了这个库,它非常适合我以 ActiveRecord 样式添加 UNION:

      https://github.com/NTICompass/CodeIgniter-Subqueries

      但我必须首先从 CodeIgniter 的 dev 分支中获取 get_compiled_select() 方法(可在此处获得:https://github.com/EllisLab/CodeIgniter/blob/develop/system/database/DB_query_builder.php -- DB_query_builder 将替换 DB_active_rec)。据推测,此方法将在 CodeIgniter 的未来生产版本中提供。

      一旦我将该方法添加到系统/数据库中的 DB_active_rec.php 中,它就像一个魅力。 (我不想使用 CodeIgniter 的开发版,因为这是一个生产应用程序。)

      【讨论】:

      • 我在这里进退两难,需要澄清一下。在DB_active_rec.php 中添加get_compiled_select 方法是否会使库正常工作?我们是否必须使用添加的方法来组合结果?在您提到的用于执行 UNION 的库的 GitHub 存储库中,我没有看到有关此方法的提及。它会执行 UNION ALL 吗?
      • @RocketHazmat:请参阅上面的评论并澄清我的疑问。根据您显示的示例,我无法运行我的 UNION ALL 查询。
      • @SilentAssassin:添加get_compiled_select 将使库正常工作。你不需要自己调用它,它是由库内部调用的。确实可以UNION ALL。要使用UNIONs,请执行以下操作:$sub1 = $this->subquery->start_union(); $sub1->select('a')->from('b')->where('c', 'd'); $sub2 = $this->subquery->start_union(); $sub2->select('a2')->from('b2')->where('c2', 'd2'); $this->subquery->end_union(); $query = $this->db->get();
      • @RocketHazmat 看到这个link。这就是我想要做的。我知道ORDER BY 正在制造问题。 start_union 之前怎么写? $sub1 在此之前不会被初始化,所以我必须使用 db 对象来编写它?
      • @SilentAssassin:UNION 中的个人SELECTs 不能拥有ORDER BY。对于完整的结果集,您只能有一个 ORDER BY。我所说的“在start_union 之前”的意思是在sub1 = $this->subquery->start_union(); 之前写$this->db->order_by(),因为如果在之后写,可能会出现语法错误。我正在努力。
      【解决方案7】:

      这是我正在使用的解决方案:

      $union_queries = array();
      $tables = array('table1','table2'); //As much as you need
      foreach($tables as $table){
          $this->db->select(" {$table}.row1, 
                              {$table}.row2,
                              {$table}.row3");
          $this->db->from($table);
          //I have additional join too (removed from this example)
          $this->db->where('row4',1);
          $union_queries[] = $this->db->get_compiled_select();
      }
      $union_query = join(' UNION ALL ',$union_queries); // I use UNION ALL
      $union_query .= " ORDER BY row1 DESC LIMIT 0,10";
      $query = $this->db->query($union_query);
      

      【讨论】:

        【解决方案8】:

        试试这个

        function get_merged_result($ids){                   
            $this->db->select("column");
            $this->db->distinct();
            $this->db->from("table_name");
            $this->db->where_in("id",$model_ids);
            $this->db->get(); 
            $query1 = $this->db->last_query();
        
            $this->db->select("column2 as column");
            $this->db->distinct();
            $this->db->from("table_name");
            $this->db->where_in("id",$model_ids);
        
            $this->db->get(); 
            $query2 =  $this->db->last_query();
            $query = $this->db->query($query1." UNION ".$query2);
        
            return $query->result();
        }
        

        【讨论】:

        • 如果您的数据正在通过一些异步过程发生变化,那是个坏主意,数据可能会在两个查询之间添加/删除/更改。
        【解决方案9】:

        bwisn 的答案比所有答案都好,并且会起作用,但性能不佳,因为它会首先执行子查询。 get_compiled_select 不运行查询;它只是编译它以供以后运行,所以速度更快 试试这个

        $this->db->select('title, content, date');
        $this->db->where('condition',value);
        $query1= get_compiled_select("table1",FALSE);
        $this->db->reset_query();
        
        $this->db->select('title, content, date');
        $this->db->where('condition',value);
        $query2= get_compiled_select("table2",FALSE);
        $this->db->reset_query();
        
        $query = $this->db->query("$query1 UNION $query2");
        

        【讨论】:

          【解决方案10】:

          这是我创建的解决方案:

          $query1 = $this->db->get('Example_Table1');
          $join1 = $this->db->last_query();
          $query2 = $this->db->get('Example_Table2');
          $join2 = $this->db->last_query();
          $union_query = $this->db->query($join1.' UNION '.$join2.' ORDER BY column1,column2);
          

          【讨论】:

          • 这并不是真正的“服务器”效率,因为您在 UNION 之前执行了 2 个不必要的查询。
          • 我得到了有效的解决方案并给出了答案。
          猜你喜欢
          • 2012-11-15
          • 2015-05-10
          • 2016-03-10
          • 1970-01-01
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多