CakePHP - 构建一个复杂的查询
Posted
技术标签:
【中文标题】CakePHP - 构建一个复杂的查询【英文标题】:CakePHP - Building a Complex Query 【发布时间】: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`;
【问题讨论】:
【参考方案1】:我认为模型是 Artist 并且它与 有一个 belongsTo 关系,那么你可以用这种方式使用 CakePHP ORM 和 hasMany 和 Portfolio
首先你必须破坏艺术家和作品集之间的关系
$this->Artist->unbindModel(array('hasMany'=>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 语句所示...以上是关于CakePHP - 构建一个复杂的查询的主要内容,如果未能解决你的问题,请参考以下文章