【问题标题】:CakePHP - Building a Complex QueryCakePHP - 构建一个复杂的查询
【发布时间】:2011-11-23 11:48:21
【问题描述】:

我想使用 CakePHP 构建以下查询。我该怎么办?

        SELECT
            `Artist`.`id`,
            CONCAT_WS(' ', `Person`.`first_name`, `Person`.`last_name`, `Person`.`post_nominal_letters`) AS `name`,
            `Portfolio`.`count`
        FROM
            `people` as `Person`,
            `artists` as `Artist`
        LEFT OUTER JOIN
            (SELECT
                `Product`.`artist_id`,
                 COUNT(DISTINCT `Product`.`id`) AS `count`
            FROM
                `product_availabilities` AS `ProductAvailability`,
                `products` AS `Product`
            LEFT OUTER JOIN
                `order_details` AS `OrderDetail`
            ON
                `Product`.`id` = `OrderDetail`.`product_id`
            LEFT OUTER JOIN
                `orders` AS `Order`
            ON
                `Order`.`id` = `OrderDetail`.`order_id`
            WHERE
                `ProductAvailability`.`id` = `Product`.`product_availability_id`
            AND
                `Product`.`online` = true
            AND
                (`ProductAvailability`.`name` = 'For sale')
                OR
                    ((`ProductAvailability`.`name` = 'Sold') AND (DATEDIFF(now(),`Order`.`order_date`) <= 30))
            GROUP BY
                `Product`.`artist_id`)
        AS
            `Portfolio`
        ON
            `Artist`.`id` = `Portfolio`.`artist_id`
        WHERE
            `Artist`.`person_id` = `Person`.`id`
        AND
            `Artist`.`online` = true
        GROUP BY
            `Artist`.`id`
        ORDER BY
            `Person`.`last_name`, `Person`.`first_name`;

【问题讨论】:

    标签: php sql cakephp


    【解决方案1】:

    我认为模型是 Artist 并且它与 有一个 belongsTo 关系,那么你可以用这种方式使用 CakePHP ORM 和 hasMany 和 Portfolio

    首先你必须破坏艺术家和作品集之间的关系

    $this-&gt;Artist-&gt;unbindModel(array('hasMany'=&gt;array('Portfolio')));

    然后建立关系

    $this->Artist->bindModel(array('hasOne'=>array('Portfolio')));
    

    最后你必须创建其他关系

    $this->Artist->bindModel(array(
    'belongsTo'=>array(
    'Product'=>array(
     'clasName'=>'Product',
     'foreignKey'=> false,
     'conditions'=>'Product.id = Artist.product_id'
     ),
     'ProductAvaibility'=>array(
     'clasName'=>'ProductAvaibility',
     'foreignKey'=> false,
     'conditions'=>'ProductAvaibility.id = Product.product_avaibility_id'
     ),
     'OrderDetail'=>array(
     'clasName'=>'OrderDetail',
     'foreignKey'=> false,
     'conditions'=>'Product.id = OrderDetail.product_id'
     ),
     'Order'=>array(
     'clasName'=>'Order',
     'foreignKey'=> false,
     'conditions'=>'Order.id = OrderDetail.order_id'
     ),
    )
    ));
    

    现在,当关系完成后,你可以做你的发现

    $this->Artist->find('all', array(
    'conditions'=>array(
    'Artist.online'=>true
    ),
    'group'=>array(
    'Artist.id'
    ),
    'order'=>array(
    'Person.last_name', 
    'Person.first_name', 
    )
    ))
    

    希望对你有用

    【讨论】:

      【解决方案2】:

      你应该把它放在你的模型中。如果你还没有阅读,我建议你阅读skinny controllers and fat models

      class YourModel extends AppModel {
      
          public function getArtistsAndPortfolioCounts() {
              return $this->query("SELECT ... ");
          }
      
      }
      

      所以在你的控制器中:

      class ArtistsControllre extends AppController {
          public function yourAction() {
              debug($this->YourModel->getArtistsAndPortfolioCounts());
          }
      }
      

      【讨论】:

      • Model::query() 方法是最好的方法吗?
      • 我想是的,因为这样你可以缓存并且它返回的结果与 $this->find() 一样接近。
      • 能不能用子查询来做,这样对代码维护不是更好吗?
      【解决方案3】:

      不要尝试使用 ORM 构建该查询。慢镜头会是一场灾难。

      相反,您应该尽可能“接近金属”地执行该请求。我不熟悉 CakePHP 的 API(因为我倾向于像黑死病一样避免它),但是您应该能够创建一个与 ActiveRecord 无关的Model,并且很可能可以访问任何类似于 PDO 包装器的东西。使用它来执行查询并将结果映射到变量。


      也就是说,您可能想要检查您的查询(您似乎有一些强迫性的引用病)。您可以做的一项改进是停止在表中使用id 列。

      整个设置可以真正受益于在 Artists 表中创建另一列:portfolio_size。每次应该更改时都可以更新。是的,它会使你的表去规范化,但它也会使这个查询变得微不足道和快速,在其他地方花费很少的成本。


      对于列的名称,如果表Artists有一个id,则保留在列artist_id中,如果Products有一个外键引用Artists.artist_id,那么也将其命名为artist_id。相同的东西应该在你的数据库中具有相同的名称。这还可以让您在 SQL USING 语句中使用。这是一个小例子:

      SELECT 
         Users.name
         Users.email
      FROM Users
      LEFT JOIN GroupUsers USING (user_id)
      LEFT JOIN Groups USING (group_id)
      WHERE Groups.name = 'wheel'
      

      我假设这个查询不需要解释,因为它是用户和组之间简单的多对多关系。

      【讨论】:

        【解决方案4】:

        您可以使用query 方法,但是当find 或任何其他便捷方法都不够用时,该方法被视为最后的手段。

        但也可以在 Cake find 方法 see the cookbook 中手动指定连接。我不完全确定,但我相信嵌套连接也受支持。 CONCAT_WS 可以通过相关字段在 find 方法的 field 属性中调用。

        【讨论】:

        • CakePHP 2.0 有虚拟字段,您可以在其中指定任何 SQL 作为该字段,包括子查询和 CONCAT_WS
        • 如果你想使用“虚拟字段”,它们也出现在 Cake 1.3 中:book.cakephp.org/view/1608/Virtual-fields
        • 你肯定不能使用 virtualFields 来获取每个 Artist 的 Portfolio.count 吗?
        • 确实存在限制和可能的解决方法:book.cakephp.org/view/1608/…
        • 据我所知,您无法指定使用子查询的连接,如问题中我的 SQL 语句所示...
        猜你喜欢
        • 1970-01-01
        • 2017-07-06
        • 1970-01-01
        • 2012-04-12
        • 1970-01-01
        • 1970-01-01
        • 2012-10-21
        • 2012-12-03
        • 1970-01-01
        相关资源
        最近更新 更多