在我的应用程序中
ActivitiesCategories
有(hasMany
)Activities
和Activities
有(belongsToMany
带有joinTable
)Patients
:
class ActivitiesCategoriesTable extends Table
{
public function initialize(array $config): void
{
// ...
$this->hasMany('Activities', [
'foreignKey' => 'activities_category_id',
]);
// ...
}
}
class ActivitiesTable extends Table
{
public function initialize(array $config): void
{
// ...
$this->belongsTo('ActivitiesCategories', [
'foreignKey' => 'activities_category_id',
'joinType' => 'INNER',
]);
$this->belongsToMany('Patients', [
'foreignKey' => 'activity_id',
'targetForeignKey' => 'patient_id',
'joinTable' => 'patients_activities',
]);
// ...
}
}
class PatientsTable extends Table
{
public function initialize(array $config): void
{
// ...
$this->belongsToMany('Activities', [
'foreignKey' => 'patient_id',
'targetForeignKey' => 'activity_id',
'joinTable' => 'patients_activities',
]);
// ...
}
}
现在,对于每个类别和每个患者,我想知道总共有多少活动以及该患者参与了多少活动。
例如:
2024 年 5 月,Mark 参加了 12 项体育活动 总共 15 个(因此缺席 3 个)
所以我正在尝试遵循此处所示的CookBook。
现在让我们按顺序进行。为了获得每个类别的总活动,我这样做: (
find
betweenDateTime
方法使用 $StartOn
和 $EndOn
参数来限制活动日期)
$Query = $this->Activities->ActivitiesCategories->find();
$results = $Query
->select([
'name',
'total_activities' => $Query->func()->count('Activities.id'),
])
->leftJoinWith('Activities', function (SelectQuery $Query) use ($StartOn, $EndOn): SelectQuery {
return $Query->find('betweenDateTime', StartOn: $StartOn, EndOn: $EndOn);
})
->groupBy(['ActivitiesCategories.id'])
->orderByAsc('name')
->all();
这工作正常,
total_activities
属性告诉我,对于每个类别,$StartOn
和$EndOn
之间的活动数量。
现在是病人。我添加第二个
leftJoinWith()
:
$Query = $this->Activities->ActivitiesCategories->find();
$results = $Query
->select([
'name',
'total_activities' => $Query->func()->count('Activities.id'),
'total_for_patient' => $Query->func()->count('Patients.id'),
])
->leftJoinWith('Activities', function (SelectQuery $Query) use ($StartOn, $EndOn): SelectQuery {
return $Query->find('betweenDateTime', StartOn: $StartOn, EndOn: $EndOn);
})
->leftJoinWith('Activities.Patients', function (SelectQuery $Query) use ($patientId): SelectQuery {
return $Query->where(['Patients.id' => $patientId]);
})
->groupBy(['ActivitiesCategories.id'])
->orderByAsc('name')
->all();
这里发生了一些奇怪的事情:
total_for_patient
属性(相对于我添加的第二个leftJoinWith()
)报告了正确的计数。
但前一小时报告的结果似乎几乎是随机的(不可预测)。
我做错了什么?我应该怎么做?谢谢。
我忘记了:显然我想知道是否以及如何使用单个查询或在任何情况下限制查询来做到这一点。 显然我知道我可以轻松地进行两个单独的查询
您面临的问题是由于当您有多个连接时聚合函数的工作方式而产生的。具体来说,当连接乘以行时,在 SQL 中计数可能会产生不正确的结果。
要解决此问题,您可以通过在查询中利用 COUNT 和 CASE 语句来使用条件计数。以下是您可以修改查询以正确计算这两个计数的方法:
$Query = $this->Activities->ActivitiesCategories->find();
$results = $Query
->select([
'name' => 'ActivitiesCategories.name',
'total_activities' => $Query->func()->count('DISTINCT Activities.id'),
'total_for_patient' => $Query->func()->count('DISTINCT CASE WHEN Patients.id IS NOT NULL THEN Activities.id ELSE NULL END'),
])
->leftJoinWith('Activities', function (SelectQuery $Query) use ($StartOn, $EndOn): SelectQuery {
return $Query->find('betweenDateTime', ['StartOn' => $StartOn, 'EndOn' => $EndOn]);
})
->leftJoinWith('Activities.Patients', function (SelectQuery $Query) use ($patientId): SelectQuery {
return $Query->where(['Patients.id' => $patientId]);
})
->group(['ActivitiesCategories.id', 'ActivitiesCategories.name'])
->order(['ActivitiesCategories.name' => 'ASC'])
->all();