PHP/SQL select 基于求和结果

PHP/SQL select based on sum result

我很难根据总和来限制我的查询结果。示例代码:

    $rows = Entry::find()
      ->section('cities')
      ->select('state')
      ->having("sum(case when covered = '1' then 1 else 0 end) = 0")
      ->asArray()
      ->all();

我只想 select 个“覆盖”了 0 个城市的州。 运行 我在“有”行上方的代码似乎被忽略了(它包括零和非零情况)。我尝试使用“where”而不是“having”,但它会导致 PDOException - “无效使用组函数”。我认为“拥有”是正确的方法,但我犯了一个新手错误——有什么提示吗?

/*** 更新 1 ***/

感谢@scaisEdge 和@angelm 的提示 -- groupBy 有帮助,但它仍然没有按预期工作。似乎“有”线仍然被忽略。修改后的代码:

    $test = Entry::find()
      ->section('cities')
      ->select(['state', 'covered', "sum(case when covered = '1' then 1 else 0 end) as numCovered"])
      ->groupBy('state')
      ->having("sum(case when covered = '1' then 1 else 0 end) = 0")
      ->asArray()
      ->all();

我记录了以下结果:

{state: "AL", covered: "0", numCovered: "0"}
{state: "AK", covered: "0", numCovered: "0"}
{state: "CA", covered: "1", numCovered: "19"}
{state: "CO", covered: "0", numCovered: "0"}
...

正如您在上面看到的,当 numCovered 明显不为 0 时,状态 (CA) 被包括在内。

我还尝试了以下“拥有”代码(我认为是相同的):

->having("numCovered = 0")

/*** 更新 2 ***/

尽管有 numCovered = 19,但使用 @cpalmer 建议的简化测试用例仍然会导致“CA”被 selected。我现在想知道这是否是 Craft CMS 的一个特点,因为看起来我的查询是正确的?

    $test = Entry::find()
      ->section('cities')
      ->select('state')
      ->groupBy('state')
      ->having("sum(case when covered = '1' then 1 else 0 end) = 0")
      ->asArray()
      ->all();

有没有不用having写这个查询的方法?

/*** 更新 3 ***/

正如@pocketrocket 发布的数据库 Fiddle 所建议的,我的 sql 应该 工作。转储原始 sql 表明 having 行被忽略。问题可能出在 CraftCMS/Yii 和我对环境缺乏了解。

尝试为状态添加一个 groupBy()

$rows = Entry::find()
  ->section('cities')
  ->select('state')
  ->groupBy('state')
  ->having("sum(case when covered = '1' then 1 else 0 end) = 0")
  ->asArray()
  ->all();

尝试从 select 编辑的列中删除 'covered'。在某些 SQL 数据库中,当您对数据进行分组时,仅在 select 语句中有一个不在 group by 语句或聚合中的列可能会导致错误。对于您的情况,我认为此专栏会导致意外结果。如果你真的想包含它,把它扔进一个聚合中并给它起别名,比如 'MAX(covered) as covered'

$test = Entry::find()
      ->section('cities')
      ->select(['state', "sum(case when covered = '1' then 1 else 0 end) as numCovered"])
      ->groupBy('state')
      ->having("sum(case when covered = '1' then 1 else 0 end) = 0")
      ->asArray()
      ->all();

尝试输入 having 部分“numCovered = 0”。

首先:我完全支持@Olivier,您应该将问题的 SQL 部分与体系结构本身分离。对于 SQL 部分,重要的是让其他人知道您使用的是哪个数据库或 SQL 方言(MySQL、PostgreSQL、MsSQL...) .

只是猜测 MySQL 您正在使用什么: 这两种方式实际上都应该有效,重复查询部分以按名称

拥有或引用它

如果两者都不起作用,并且您想在没有 having 的情况下工作,也许您可​​以实现子选择?

SELECT 
    state, 
    sum(case when covered = '1' then 1 else 0 end) as numCovered 
FROM cities 
GROUP BY state
HAVING sum(case when covered = '1' then 1 else 0 end) = 0;

SELECT 
    state, 
    sum(case when covered = '1' then 1 else 0 end) as numCovered 
FROM cities 
GROUP BY state
HAVING numCovered = 0;

SELECT * FROM (
  SELECT 
      state, 
      sum(case when covered = '1' then 1 else 0 end) as numCovered 
  FROM cities 
  GROUP BY state
) sub_select
WHERE sub_select.numCovered = 0;

您可以在这里尝试一下:DB Fiddle Link

嗯,我猜你忘记了 having 子句中 0 周围的单引号。

$test = Entry::find()
  ->section('cities')
  ->select('state')
  ->groupBy('state')
  ->having("sum(case when covered = '1' then 1 else 0 end) = '0'")
  ->asArray()
  ->all();