在 CakePHP 3 中对子查询列进行分页排序
Paginate sort on subquery column in CakePHP 3
这可能已经在其他地方得到了回答,但过去几天我一直在寻找答案,但找不到适合我的答案 problem/that 我明白了...
我正在使用 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
进行分页。在我的模型中,我可以单击所有三列 headers 但只有 Area_ID
列会被排序。两个子查询列不会排序。即使我没有收到错误。
查看 SQL-log 表明它甚至从未尝试 order by
这两列...
非常感谢任何解决此问题的想法 this/work!
如果您需要有关我的代码的更多信息,请在下面发表评论。
编辑 1:
正如用户@ndm 指出的那样,我必须将计算字段放入分页数组的 sortWhitelist
选项中。
这样做效果很好,因为我能够按 headers:
列进行排序
$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 服务器可以读取它:
... ORDER BY ( SELECT (COUNT(*)) FROM printers Printers WHERE (...) ) asc, ...
有什么解决办法吗?
编辑 2:
所以 @ndm 使用 pull-request 或通过 newExpr()
函数进行修复的答案都很好。至少关于 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 的大力帮助!绝对是我的赞成票。
如评论中所链接,默认情况下,分页器仅允许对存在于主 table 中的列、其他 table 的列(连接)或计算列 .
生成的 window 函数中缺少括号 SQL 看起来像一个错误,the SQL Server query translator 不检查 order
中定义的表达式是否子句是一个查询,需要将生成的 SQL 括在括号中。
我已经推送了 3.9
和 4.1
的可能修复:
https://github.com/cakephp/cakephp/pull/15165
https://github.com/cakephp/cakephp/pull/15164
如果您现在无法升级,可能的解决方法是将您的子查询包裹在额外的表达式中,编译时应将内部查询表达式包裹在括号中:
$dataQuery
->select([
'Locations.Area_ID',
'Unwanted_Software' => $dataQuery->newExpr($computersQuery),
'Printers_Not_WHQL_Compatible' => $dataQuery->newExpr($printersQuery)
])
->group('Locations.Area_ID');
这可能已经在其他地方得到了回答,但过去几天我一直在寻找答案,但找不到适合我的答案 problem/that 我明白了...
我正在使用 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
进行分页。在我的模型中,我可以单击所有三列 headers 但只有 Area_ID
列会被排序。两个子查询列不会排序。即使我没有收到错误。
查看 SQL-log 表明它甚至从未尝试 order by
这两列...
非常感谢任何解决此问题的想法 this/work!
如果您需要有关我的代码的更多信息,请在下面发表评论。
编辑 1:
正如用户@ndm 指出的那样,我必须将计算字段放入分页数组的 sortWhitelist
选项中。
这样做效果很好,因为我能够按 headers:
$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 服务器可以读取它:
... ORDER BY ( SELECT (COUNT(*)) FROM printers Printers WHERE (...) ) asc, ...
有什么解决办法吗?
编辑 2:
所以 @ndm 使用 pull-request 或通过 newExpr()
函数进行修复的答案都很好。至少关于 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 的大力帮助!绝对是我的赞成票。
如评论中所链接,默认情况下,分页器仅允许对存在于主 table 中的列、其他 table 的列(连接)或计算列
生成的 window 函数中缺少括号 SQL 看起来像一个错误,the SQL Server query translator 不检查 order
中定义的表达式是否子句是一个查询,需要将生成的 SQL 括在括号中。
我已经推送了 3.9
和 4.1
的可能修复:
https://github.com/cakephp/cakephp/pull/15165
https://github.com/cakephp/cakephp/pull/15164
如果您现在无法升级,可能的解决方法是将您的子查询包裹在额外的表达式中,编译时应将内部查询表达式包裹在括号中:
$dataQuery
->select([
'Locations.Area_ID',
'Unwanted_Software' => $dataQuery->newExpr($computersQuery),
'Printers_Not_WHQL_Compatible' => $dataQuery->newExpr($printersQuery)
])
->group('Locations.Area_ID');