问题 MySQL Union全部在Yii的默认范围内


我正在使用Yii 1.1.16,并且想知道我的联盟2型号将如何使用默认范围?

model 1 = abc
model 2 = abc2

基本上我想做一个简单的联盟

SELECT * FROM `abc`
UNION ALL
SELECT * FROM `abc2`

没有真正使用默认范围,所以对概念有点新意。 两个表都具有完全相同的列号和列名。

我试过这个,但失败了。

在我的 abc 模型

public function defaultScope() {
        return array(
            'alias' => 't2',
            'select'=>array('t.*, t2.*'),
            'union'=>array('SELECT * FROM `abc2`')
        );
    }

更新:  刚刚意识到 http://www.yiiframework.com/doc/api/1.1/CDbCriteria,没有 union 对于 CDbCriteria

public function defaultScope() {
        return array(
            //'alias' => 't',
            'select'=>array('*'),
            'join'=>'UNION ALL SELECT * FROM `abc2`'
        );
    }

上面的代码给了我一个错误

CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column 't.make_code' in 'where clause'. The SQL statement executed was: SELECT `t`.`class_code`, `t`.`make_code`, `t`.`model_code` FROM `abc` `t` UNION ALL SELECT * FROM `abc2` WHERE `t`.`make_code`=:yp0 LIMIT 1. Bound with :yp0='11' 

在两个表中,我都有专栏 make_code

我需要查询看起来像这样

SELECT * FROM 
(
   SELECT * FROM `abc` 
      UNION ALL 
   SELECT * FROM `abc2`
) AS t 

该怎么办?


12463
2018-01-15 03:42


起源

是的,有点发现这个。但还是想弄清楚 http://www.yiiframework.com/wiki/486/defaultscope/ - user2636556
请解释一下你有哪些实体?为什么要为2个数据库表使用1个AR模型? - Aleksei Akireikin
@AlexAkr coz 1表有新产品的信息和1使用过。我知道我可以将它们合并到1个表中并将它们标记为新的和使用的。 (这会更简单)不幸的是,这两个表经常更新,它们会在CD中提供给我们几周。我想按原样更新mysql - user2636556
嗨@ user2636556,如果你告诉我在答案中我缺少什么,我可以解决它或完成帮助你这个,我真的认为这个问题的答案在这里,问候 - DiegoCoderPlus
@DiegoCoderPlus没有完全得到你想要做的事情:(我只是想要一种方式,如果我这样做 Abc::model()->findAll() 我得到两个表联合在一起的结果 - user2636556


答案:


你可以使用Yii 查询生成器;它是您需要构建非标准查询的首选解决方案,并且本机支持 UNION

$data = Yii::app()->db->createCommand()
  ->select('*')
  ->from('abc')
  ->union('SELECT * FROM `abc2`')
  ->where('make_code=:make_code', [':make_code'=>42])
  ->queryAll();

或者,您可以使用,而不是直接查询 ->getText() 或手动编写SQL然后使用 CSqlDataProvider 制作可配置的数据源。


3
2018-01-17 21:01



所以没有办法可以使用它 defaultScope()?每次我打电话给模特 abc 我必须有一个自定义函数,它会联合并调用它吗? - user2636556
@ user2636556:没办法。 defaultScope 被喂入 CDbCriteria,您可以在文档中看到该类没有支持联合选择的规定。 - Jon


您好,我希望我能帮助您, 现在,yii 1.x的cdbcriteria尚未为工会做好准备...所以我想更多关于其他解决方案或wa。

所以,解决这个问题的简单方法是从您需要的查询创建一个视图:

CREATE VIEW unionAbc AS
SELECT * FROM `abc` 
UNION ALL 
SELECT * FROM `abc2`

然后从那个视图中你可以构建一个新的模型并且不费力地调用它。

我发现的另一个解决方案是合并模型查询结果,我从php理论的角度告诉你,我的意思是当你在yii中生成一个模型查询它返回一个数组或对象时,你需要使用yii helper中存在的模型中的数组转换器然后合并两个数组,这样就会产生一个包含两个数组的新数组

未经测试的第三个解决方案是:

$dataProvider =  new CArrayDataProvider('User');
$dataProvider->setData($modelabc->findAll());
$dataProvider->setData($modelabc2->findAll());

问候。


2
2018-01-16 20:02



所以任何时候我都有更新 abc 要么 abc2 表我必须去phpmyadmin并创建视图 unionAbc ? - user2636556
完全没有,视图只创建一次,然后它按照你在创建中引入的查询行为...... - DiegoCoderPlus


我建议KISS解决方案:
是否有可能从联合选择创建视图并使用它?
然后从此视图创建Active Record。
 当然,对于UI中的创建/删除数据,您必须在两个表中单独创建模型。
 
更新
您可以在数据库中创建视图: CREATE VIEW abc_union AS SELECT * FROM abc UNION ALL SELECT * FROM abc2 
然后使用Gii为此视图生成模型。


2
2018-01-19 11:01



任何示例代码?不太明白 - user2636556
更新我的回答...... - Amini


首先,如果DB中有2个相同的表,可能最好将它们合并为一个。 Yii基于简单规则的ActiveRecord实现:一个表 - >一个活动记录模型。

我建议你使用存储库模式这个解决方案。我不认为那用法 defaultScope() 是必不可少的。

1.创建一个接口,以确保两个产品模型都实现一些常用方法。

interface Product
{
    public function findAllByMakeCode($makeCode);

    // other common methods here
}

2.使用您需要的常用方法实现或默认范围创建特征。

trait ProductTrait
{
    function findAllByMakeCode($makeCode)
    {
        return $this->model()->findAllByAttributes(['make_code' => $makeCode]);
    }

    function defaultScope()
    {
        // your default scope condition
        // for example field is_deleted equals to 0
        return ['condition' => 'is_deleted=0'];
    }
    // other common methods implementation
}

3.为两个表创建单独的AR模型。

// used product entity
class ProductUsed extends CActiveRecord implements Product
{
    use ProductTrait;

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return 'product_used';
    }
}

// new product entity
class ProductNew extends CActiveRecord implements Product
{
    use ProductTrait;

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return 'product_new';
    }
}

4.创建一个Repository类。

class ProductRepository
{   
    public function findAllByMakeCode($makeCode)
    {
        return array_merge(
            ProductUsed::model()->findAllByMakeCode($makeCode),
            ProductNew::model()->findAllByMakeCode($makeCode)
        );
    }

    public function findAll($condition = '', array $params = [])
    {
        return array_merge(
            ProductUsed::model()->findAll($condition = '', array $params = []),
            ProductNew::model()->findAll($condition = '', array $params = [])
        );
    }
}

5.使用它。

$repository = new ProductRepository();

// all products
$products = $repository->findAll();

// products with 42 make code
$products42 = $repository->findAllByMakeCode(42);

也许它看起来有点复杂,但它是值得的。


1
2018-01-18 10:46



该 $makeCode 是一个条件,我后来添加“当需要”不是的一部分 defaultScope() 它还能完成吗? - user2636556
当然,您可以在ProductTrait中为两个模型添加默认范围。答案已更新。顺便说一句,默认范围的主要目的是按某种条件过滤表行(例如 is_deleted 旗帜不稳定)不是为了 union, join等 - Aleksei Akireikin


你(们)能做到 联盟 喜欢这个;

$firtQuery = Yii::app()->db->createCommand()
    ->select("*")
    ->from('abc')
    //->where() you can add WHERE clause in here
    ->getText();

$result = Yii::app()->db->createCommand()
    ->select("*")
    ->from('abc2')
    //->where() you can add WHERE clause in here
    ->union($firtQuery)
    ->queryRow();

1
2018-01-24 04:10



我如何使它成为模型的默认查询?所以任何时候 $a=new Abc; 被称为,它已经是一个联盟? - user2636556