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

Problem with a query using contain(), order() and limit() in Cakephp 4

我有以下两个关联:

议程日期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查询returns一个错误是:

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 链接到 AgendaDates):

$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 关联的情况下却不会?

严格分组

only_full_group_by 模式要求 SELECTHAVINGORDER BY 中的所有字段必须是功能相关的(列 a 唯一确定列 b), 或聚合。

分组时,并非组中某一列的所有值都必须相等,想象一个 Articles hasMany Comments 关系,如果您加入 Comments 并按 Articles.id 分组,所有Articles 的列可以由 id 主键列确定,但是 Comments 的列不能,每个 Article.id 可能有多个不同的 Comments 列值,像这样:

Articles.id Comments.created
1 2022-01-01
1 2022-01-02
2 2022-02-01
2 2022-02-02

如果你有这样的结果,那么 MySQL 将不知道在被要求订购时从组中选择两个不同的 Comments.created 值中的哪一个,因此发出一个查询可以产生这样的结果会报错。

当禁用严格分组模式时,MySQL 将被允许从组中基本上随机选择一个值,这使得结果不可预测,这可能是一个问题。使用聚合可以解决这个问题,例如 MIN(Comments.created) 使用组中的最小日期值。

您可以在文档中找到关于该主题的更多信息,通常在整个互联网上:

hasMany 与 hasOne

你的两个查询产生不同的 SQL,hasMany 关联总是在一个单独的查询中检索,也就是你调用 translations 查找器的地方,一个单独的查询没有加入、排序和限制发生,因此翻译的分组不会造成任何问题,因为只涉及一个用于分组的字段。

hasOne 关联默认使用连接,所以是单个查询,因此在这里您要在该单个查询上调用 translations 查找器,即您正在进行排序的查询和限制,这就是翻译分组与引用组中字段的排序之间冲突的根源。

tl;博士

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

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

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