使用 codeigniter 的活动记录模式进行 UNION 查询

问题描述 投票:0回答:11

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

php mysql codeigniter activerecord union
11个回答
38
投票

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

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

31
投票

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

此链接将对您有很大帮助:活动记录子查询

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

// #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();

23
投票

这是我曾经用过的一种快速但肮脏的方法

// 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);

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

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


8
投票

您可以使用以下方法获取模型中的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 变量中,而不实际执行它。

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


3
投票

通过修改 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`)

2
投票

我找到了这个库,它非常适合我以 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 的开发版本,因为这是一个生产应用程序。)


1
投票

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

$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);

0
投票

试试这个

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

0
投票

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");

0
投票

我看到有人问如何通过查询构建器方法将 ORDER BY 与 UNION 结合使用。

只需记住封装您的子查询,以便您的 LIMIT 和 ORDER BY 逻辑按预期执行。

public function demoCompiledUnion(int $param1, int $param2): array
{
    $lows = $this->db
        ->select('pk, address')
        ->from('unit1')
        ->where('pk <', $param1)
        ->limit(5)
        ->get_compiled_select();

    $highs = $this->db
        ->select('pk, address')
        ->from('unit2')
        ->where('pk >', $param2)
        ->limit(5)
        ->get_compiled_select();

    return $this->db  // don't need select() if you want every column
        ->from("($lows) UNION ($highs)")
        ->order_by(2, 'DESC')  // order by address DESC
        ->get()
        ->result();  // return payload as an array of objects
}

我打印了

$this->db->last_query()
并添加了一些选项卡来显示此呈现的 SQL(引用字符可能会根据您的数据库驱动程序/方言而有所不同):

SELECT *
FROM (
    SELECT "pk", "address"
    FROM "unit1"
    WHERE "pk" < 10000
    LIMIT 5
) UNION (
    SELECT "pk", "address"
    FROM "stl_unit"
    WHERE "stl_unit_pk" > 15000
    LIMIT 5
)
ORDER BY 2 DESC

结果集与预期完全一致 - 10 行按地址列值降序排序。

因为此查询构建的所有潜在易受攻击的部分都是通过活动记录方法完成的,所以它与 CodeIgniter 允许的一样安全。


-1
投票

这是我创建的解决方案:

$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);
© www.soinside.com 2019 - 2024. All rights reserved.