使用 cakePHP 对单个字段进行 SQL Sum 不适用于 paginate()

Posted

技术标签:

【中文标题】使用 cakePHP 对单个字段进行 SQL Sum 不适用于 paginate()【英文标题】:SQL Sum on single field with cakePHP don't work with paginate() 【发布时间】:2014-04-29 15:46:25 【问题描述】:

我需要从“orcamentos”表中获取“valor”字段的总和。 我正在使用它并且它正在工作,但我知道这不是正确的方法:

//function index() from OrcamentosController.php
$orcamentoSubprojetosTotal = $this->Orcamento->query(
    "SELECT
        SUM(Orcamento.valor) AS TotalOrcamentoSuprojetos
    FROM
        orcamentos AS Orcamento
    WHERE
        Orcamento.subprojeto_id IS NOT NULL;"
    );
$this->set(compact('orcamentoSubprojetosTotal'));

我发现了这个问题cakephp sum() on single field(以及其他sum() function in cakephp query、using virtual fields to sum values in cakephp),但现在我将这一行添加到我的控制器中:

$this->Orcamento->virtualFields['total'] = 'SUM(Orcamento.valor)';

paginate() 停止工作并只显示一个条目,如下所示:

第 1 页,共 1 页,共显示 2 条记录中的 1 条记录,从记录 1 开始,到 2 结束

这是我的 index() 函数:

public function index($tipoOrcamento = null) 
$this->Orcamento->recursive = 0;

/*
$orcamentoSubprojetosTotal = $this->Orcamento->query(
    "SELECT
        SUM(Orcamento.valor) AS TotalOrcamentoSuprojetos
    FROM
        orcamentos AS Orcamento
    WHERE
        Orcamento.subprojeto_id IS NOT NULL;"
);
$this->set(compact('orcamentoSubprojetosTotal'));
*/


$this->set(compact('tipoOrcamento'));
if($tipoOrcamento == 'subtitulo')
    $this->set('orcamentos', $this->Paginator->paginate('Orcamento', array('Orcamento.subtitulo_id IS NOT NULL')));
elseif($tipoOrcamento == 'subprojeto')
    $this->set('orcamentos', $this->Paginator->paginate('Orcamento', array('Orcamento.subprojeto_id IS NOT NULL')));
else
    $this->set('orcamentos', $this->Paginator->paginate('Orcamento'));



我可以使用 query() 或者有人可以帮助我处理虚拟字段吗?

谢谢。

【问题讨论】:

其实我只需要一种访问总和的方法,我不需要虚拟字段。我用 cakePHP 做的方式好吗? 我的问题真的是执行 SUM 查询的正确方法是什么,很抱歉给您带来麻烦。 【参考方案1】:

不要使用虚拟字段

virtual field 用于以下用途:

public $virtualFields = array(
    'name' => 'CONCAT(User.first_name, " ", User.last_name)'
);

如果从不属于/单行的东西中使用虚拟字段 - 它不会做你所期望的事情,这可以从 paginate 生成的查找调用的次要影响中看出。

使用字段

改为使用field 方法并传入表达式:

$integer = $Model->field(
    'SUM(valor)', 
     array('NOT' => array('subprojeto_id' => null))
);

哪个会执行:

SELECT SUM(valor) from x where NOT (subprojecto_id IS NULL);

这也将返回一个标量值,而如问题所示调用查询将返回一个嵌套数组。

【讨论】:

非常感谢!这就是我一直在寻找的,抱歉没有正确询问。我需要使用 $this->Model->field 或者我收到错误“调用非对象上的成员函数字段()”,但它有效!只是为了理解,使用 field() 相当于我对原始查询所做的事情,但是使用 cakephp 必须提供的内容,对吗? 如果你需要的话,使用查询没有什么问题——它是相同的sql,有效地使用字段只是更少的编写代码和更方便,因为它意味着你不需要做$actualValue = $returned[0][0]; 是的,field() 很容易使用。我仍然有一个问题:array('NOT' => array('subprojeto_id' => null)) 工作完美,但如果我尝试array('NOT' => array('subtitulo_id' => null)),它会给我错误:错误:SQLSTATE[23000]:完整性约束违规:1052 列 'subtitulo_id' in where 子句不明确。在我的表中,subtitulo 条目的 subprojeto 字段将为 null,而 subprojeto 条目的 subtitulo 字段将为 null。它正在处理查询,为什么它不能与 field() 一起使用? 因为您在多个表中都有该字段 - 并且通过 this 进行该模型的递归值为 0 - 这意味着 hasOne 和 belongsTo 关联在查询中加入。在您的模型/应用程序模型中将递归设置为 -1(一个好的做法)或使用条件数组中的模型别名 (array('Orcamento.subprojeto_id' => null))。查看您正在生成的 sql 将是一个好主意 - 如果不是原因,您至少可以理解问题。 好的,成功了。我试图通过查看SQL查询来找到问题,但是我现在正在学习SQL和php,所以我不明白很多东西。感谢您的帮助!【参考方案2】:

当然,当您使用 SUM 时,您会得到一条记录

你可以做两件事:

    find() 调用之前创建virtualField,并在查询之后取消设置。 在您的分页器设置中使用'fields' => arra(...),并在您不想求和时仅列出您需要检索的字段而不是虚拟字段

【讨论】:

我认为通过声明一个虚拟字段,我只是在字段列表中添加了一个字段,因此分页器可以像以前一样工作,但它会多一个字段。我只想有一种访问总和的方法,它不需要是虚拟字段。我用的方式好吗? 是的,使用虚拟字段是正确的方法。当您想要获取 SUM 并在 find() 调用之后取消设置 virtualField 时,只需使用 find() ,这样它就不会影响分页。否则分页查询将包含模型的所有字段(甚至是虚拟字段)

以上是关于使用 cakePHP 对单个字段进行 SQL Sum 不适用于 paginate()的主要内容,如果未能解决你的问题,请参考以下文章

CakePHP 2.2.4 无法使用 HAVING 子句和计算字段对结果进行分页 - Haversine 公式

CakePHP:向表单添加字段(动态)

CakePHP:从单个模型中检索多个记录以以一种形式进行编辑

Cakephp:选择登录后要保存的数据

对这个复杂的 SQL 使用 cakephp 的 find 方法

Cakephp保存没有id的数据