这个问题可能已经在其他地方得到了解答,但过去几天我一直在寻找答案,却找不到适合我的问题/我理解的答案......
我使用的是CakePHP 3.8.5,目前正在处理一个在select中包含子查询的查询。我得到了3个表Locations
、Computers
和Printers
。Locations
和Computers
处于belongsToMany
关系中,以及Locations
和Printers
。
因此,我尝试获取以下查询,就数据结果而言,该查询运行良好:
$computersQuery = $this->Computers->find();
$computersQuery->select([$computersQuery->func()->count('*')])
->where(function (QueryExpression $exp) {
return $exp
->notEq('Computers.Unwanted_Software', '')
->equalFields('Computers.Area_ID', 'Locations.Area_ID');
});
$printersQuery = $this->Printers->find();
$printersQuery->select($printersQuery->func()->count('*'))
->where(function (QueryExpression $exp) {
return $exp
->eq('Printers.WHQL', 0)
->equalFields('Printers.Area_ID', 'Locations.Area_ID');
});
$dataQuery = $this->Locations->find();
$dataQuery->select(['Locations.Area_ID',
'Unwanted_Software' => $computersQuery,
'Printers_Not_WHQL_Compatible' => $printersQuery])
->group('Locations.Area_ID');
所以我尝试在我的控制器中分页$dataQuery
。在我的模型中,我可以点击所有三个列标题,但只有Area_ID
列会被排序。两个子查询列不会排序。即使我没有得到错误。查看SQL日志显示,它甚至从来没有尝试order by
这两个列...
任何想法如何解决这个/工作围绕这个是高度赞赏!
如果你需要更多关于我的代码的信息,请在下面留下评论。
编辑1:
正如user @ndm所指出的,我必须将计算后的字段放入分页数组的sortWhitelist
选项中,这样做效果很好,因为我可以按列标题排序:
$this->paginate = [
'limit' => '100',
'sortWhitelist' => [
'Locations.Area_ID',
'Unwanted_Software',
'Printers_Not_WHQL_Compatible'
],
'order' => ['Locations.Area_ID' => 'ASC']
]
但是接下来出现了下一个问题。尝试按Printers_Not_WHQL_Compatible
列排序。生成的SQL代码有一个小问题(顺便说一句,我使用的是SQL Server 2008):
SELECT *
FROM (SELECT Locations.Area_ID AS [Locations__Area_ID],
(SELECT (COUNT(*)) FROM computers Computers WHERE (...)) AS [Unwanted_Software],
(SELECT (COUNT(*)) FROM printers Printers WHERE (...)) AS [Printers_Not_WHQL_Compatible],
(ROW_NUMBER() OVER (ORDER BY SELECT (COUNT(*)) FROM printers Printers WHERE (...) asc, Locations.Area_ID ASC)) AS [_cake_page_rownum_] FROM locations_Views Locations GROUP BY Locations.Area_ID ) _cake_paging_
WHERE _cake_paging_._cake_page_rownum_ <= 100
这代表产生的SQL程式码。问题是在de Order By
陈述式中,我的子查询前后没有括号。它应该看起来像这样,以便SQL Server可以读取它:
... ORDER BY ( SELECT (COUNT(*)) FROM printers Printers WHERE (...) ) asc, ...
有什么办法解决这个问题吗?
编辑2:
因此,无论是用pull-request还是通过newExpr()
函数进行修复,@ndm都能很好地工作。至少对于Order By
中子查询的括号来说是这样。
不幸的是,已经遇到了下一个问题。生成的SQL(这对两个解决方案都有意义!)有点“刷新”Order By
中整个查询的参数输入,这意味着它将Where
子句的筛选器参数放在:c0
中。您可以在下面的查询中看到这一点:
SELECT *
FROM (SELECT Locations.Area_ID AS [Locations__Area_ID],
(SELECT (COUNT(*))
FROM computers Computers
WHERE (Computers.Unwanted_Software != :c0
AND Computers.Area_ID = (Locations.Area_ID)
)
) AS [Unwanted_Software],
(SELECT (COUNT(*))
FROM printers Printers
WHERE (Printers.WHQL = :c1
AND Printers.Area_ID = (Locations.Area_ID))
) AS [Printers_Not_WHQL_Compatible],
(ROW_NUMBER() OVER (ORDER BY
(SELECT (COUNT(*))
FROM printers Printers
WHERE (Printers.WHQL = :c0
AND Printers.Area_ID = (Locations.Area_ID))) asc,
Locations.Area_ID ASC)
) AS [_cake_page_rownum_]
FROM locations_Views Locations
GROUP BY Locations.Area_ID ) _cake_paging_
WHERE _cake_paging_._cake_page_rownum_ <= 100
我不认为这是预期的结果。我个人可能可以通过避免直接传递参数来解决这个问题(我不必处理具体的外部用户输入)。我仍然认为这应该被检查。
感谢@ndm的帮助!绝对是我的一张赞成票。
1条答案
按热度按时间wlzqhblo1#
正如注解中所链接的,默认情况下,分页器只允许对主表中存在的列、其他表(联接)的列或计算列must be explicitly allowed via the
sortWhiteList
option进行排序。生成的窗口函数SQL中缺少的括号看起来像是一个错误,SQL Server查询转换器不检查
order
子句中定义的表达式是否为查询,这将需要将生成的SQL用括号括起来。我已经为
3.9
和4.1
推出了一个可能的修复程序:如果您现在无法升级,可能的解决方法是将您的子查询 Package 在其他运算式中,在编译时,这些运算式应该会将内部查询运算式 Package 在括号中: