在Cakephp 4中使用contain()、order()和limit()进行查询时出现问题

46qrfjad  于 2022-11-11  发布在  PHP
关注(0)|答案(1)|浏览(150)

我有以下两个联想:
日程日期hasOne图像说明
日程日期hasMany照片
当我执行此查询时:

$dates = $this->AgendaDates
                        ->find()
                        ->order(['AgendaDates.date' => 'ASC'])
                        ->contain(['ImageDescriptive' => function ($q) { // AgendaDates hasOne ImageDescriptive
                            return $q->find('translations');
                        }])
                        ->limit(2);

我有以下错误:

SQLSTATE[42000]:语法错误或访问冲突:1055 ORDER BY子句的表达式#1不在GROUP BY子句中,并且包含非聚集列'brigittepatient.AgendaDates.date',该列在功能上与GROUP BY子句中的列无关;这与sql_mode=only_full_group_by不兼容

返回错误的SQL查询是:

SELECT 
  FichiersI18n.id AS FichiersI18n__id, 
  FichiersI18n.locale AS FichiersI18n__locale, 
  FichiersI18n.model AS FichiersI18n__model, 
  FichiersI18n.foreign_key AS FichiersI18n__foreign_key, 
  FichiersI18n.field AS FichiersI18n__field, 
  FichiersI18n.content AS FichiersI18n__content 
FROM 
  fichiers_i18n FichiersI18n 
  INNER JOIN (
    SELECT 
      (ImageDescriptive.id) 
    FROM 
      agenda_dates AgendaDates 
      LEFT JOIN fichiers ImageDescriptive ON (
        ImageDescriptive.model = 'AgendaDates' 
        AND ImageDescriptive.field = 'image_descriptive' 
        AND AgendaDates.id = ImageDescriptive.foreign_key
      ) 
    GROUP BY 
      ImageDescriptive.id 
    ORDER BY 
      AgendaDates.date ASC 
    LIMIT 
      2
  ) ImageDescriptive ON FichiersI18n.foreign_key = ImageDescriptive.id 
WHERE 
  FichiersI18n.model = 'Fichiers'

奇怪的是,当我做同样的事情,但与照片(链接到议程日期的hasMany,而不是hasOne):

$dates = $this->AgendaDates
                        ->find()
                        ->order(['AgendaDates.date' => 'ASC'])
                        ->contain(['Photos' => function ($q) { // AgendaDates hasMany Photos
                            return $q->find('translations');
                        }])
                        ->limit(2);

我没有错误,查询如下所示:

SELECT 
  FichiersI18n.id AS FichiersI18n__id, 
  FichiersI18n.locale AS FichiersI18n__locale, 
  FichiersI18n.model AS FichiersI18n__model, 
  FichiersI18n.foreign_key AS FichiersI18n__foreign_key, 
  FichiersI18n.field AS FichiersI18n__field, 
  FichiersI18n.content AS FichiersI18n__content 
FROM 
  fichiers_i18n FichiersI18n 
  INNER JOIN (
    SELECT 
      (Photos.id) 
    FROM 
      fichiers Photos 
    WHERE 
      (
        Photos.model = 'AgendaDates' 
        AND Photos.field = 'photos' 
        AND Photos.foreign_key in (5, 3)
      ) 
    GROUP BY 
      Photos.id
  ) Photos ON FichiersI18n.foreign_key = Photos.id 
WHERE 
  FichiersI18n.model = 'Fichiers'

我真的不明白为什么我的cakephp查询在hasOne关联的情况下会抛出错误,而在hasMany关联的情况下却不会?

dw1jzc5e

dw1jzc5e1#

严格分组

only_full_group_by模式要求SELECTHAVINGORDER BY中的所有字段必须是功能相关的(列a唯一确定列b)或聚合。
分组时,组中列的所有值不一定都相等,假设有一个Articles hasMany Comments关系,如果加入Comments并按Articles.id分组,则Articles的所有列都可以由id主键列确定,但Comments的列不能确定,每个Article.id可以有多个不同的Comments列值,如下所示:
| Articles.id | Comments.created |
| - -|- -|
| 一个|2022年1月1日|
| 一个|2022年1月2日|
| 2个|2022年2月1日|
| 2个|2022年2月2日|
如果有这样的结果,那么当要求MySQL按Comments.created值排序时,MySQL将不知道从组中选择两个不同的Comments.created值中的哪一个,因此发出一个可能产生这样的结果的查询将抛出错误。
当严格分组方式被禁用时,MySQL将被允许从组中随机选取一个值,这使得结果不可预测,这可能是一个问题。使用聚合可以解决这个问题,例如MIN(Comments.created)使用组中的最小日期值。
你可以在文档中找到更多关于这个主题的信息,通常在互联网上都可以找到:



有多个与有一个

您的两个查询产生不同的SQL,hasMany关联总是在单独的查询中检索,这是您调用translations查找器的地方,一个单独的查询,其中没有发生连接、排序和限制,因此翻译的分组不会导致任何问题,因为只涉及用于分组的一个字段。
默认情况下,hasOne关联使用连接,因此是一个查询,因此在这里,您要对该查询调用translations查找器,在该查询中,您要进行排序和限制,这就是翻译分组和引用组中字段的排序之间冲突的根源。

tl;dr

通过对翻译行为使用select策略应该可以避免此问题,该策略将使用查询得主键而不是应用了分组得子查询来获取翻译:

$this->addBehavior('Translate', [
    'strategy' => 'select',
    // ...
]);

ImageDescriptive关联使用INNER连接可能也会解决这个问题(即,如果查询可能产生的多个null值是函数依赖问题的根源),但当涉及null值时,它当然会改变查询记录的方式。

相关问题