【问题标题】:CakePHP 3 - How to define multiple conditions for one and the same field?CakePHP 3 - 如何为同一个字段定义多个条件?
【发布时间】:2018-03-21 16:02:42
【问题描述】:

我正在使用 CakePHP 3.5.13 开发一个应用程序。它与“遗留”数据库相关联——即没有根据 CakePHP 命名约定编写的数据库。尽管如此,我还是烘焙了它并生成了表格和实体文件。

我在使用 ORM 构建 SQL 查询时遇到问题。

查询 - 在普通 SQL 中 - 如下所示:

SELECT s.id FROM substances s WHERE s.id 
IN
    (SELECT fs.substance_id FROM filters_substances fs WHERE fs.filter_id IN (147))
AND s.id IN 
    (SELECT fs.substance_id FROM filters_substances fs WHERE fs.filter_id IN (47,93))
AND s.id NOT IN
    (SELECT fs.substance_id FROM filters_substances fs WHERE fs.filter_id IN (148,354))

我已经设法使用 ORM 编写了以下内容:

$subquery_in = $FiltersSubstances
    ->find()
    ->select(['FiltersSubstances.substance_id'])
    ->where(['FiltersSubstances.filter_id IN' => [47,93]]);

$subquery_not_in = $FiltersSubstances
    ->find()
    ->select(['FiltersSubstances.substance_id'])
    ->where(['FiltersSubstances.filter_id IN' => [354]]);

$Substances = TableRegistry::get('Substances');

$query = $Substances
    ->find()
    ->where([
        'Substances.id IN' => $subquery_in,
        'Substances.id NOT IN ' => $subquery_not_in
    ]);

debug($query);
debug($query->all());

上述查询工作正常,但不完整:它会给我所有结果,但不考虑包含fs.filter_id IN (147) 的行(在普通 SQL 中)。

我遇到的问题是我不知道如何生成多个AND IN 子查询。

我尝试了以下方法:

// Same as PHP given previously, plus:

$subquery_in2 = $FiltersSubstances
    ->find()
    ->select(['FiltersSubstances.substance_id'])
    ->where(['FiltersSubstances.filter_id IN' => [147]]);

// Modified $query:

$query = $Substances
    ->find()
    ->where([
        'Substances.id IN' => [$subquery_in, $subquery_in2],
        'Substances.id NOT IN ' => $subquery_not_in]
    );

// Also tried:

$query = $Substances
    ->find()
    ->where([
        'Substances.id IN' => $subquery_in,
        'Substances.id IN' => $subquery_in2,
        'Substances.id NOT IN ' => $subquery_not_in
    ]);

上述尝试无效 - SQL 错误。

请任何人建议是否/如何在 ORM 中构造此查询?

我曾考虑采用手工编写的“原始 SQL”路线。这似乎是一个巨大的耻辱,因为 ORM 完成了我需要的 95%。

我已阅读Cakephp 3 NOT IN query,这是我设法完成我需要完成的工作的第一部分的方法。但是,该示例中的查询可以说不那么复杂,并且不能满足我的需求。

重要提示:原始 SQL 会产生正确的结果。我知道有些人可能会说,为什么不将所有IN ID 分组到一个查询中。但是,这会产生非常不同且不正确的结果。 IN... AND IN 在这里非常重要。我知道查询没问题,就是我感兴趣的Cake的ORM怎么写。

【问题讨论】:

  • 我知道您不能组合 INNOT IN 以防相同的 substance_id 可能出现在组中,但前两个 IN 应该是可合并的,因为它们通过AND? 组合在一起?
  • @ndm 如果您尝试这样做,它会给出非常不同的结果。这就是我添加“重要”注释的原因。例如,如果我执行 - 在原始 SQL 中 - WHERE fs.filter_id IN (147,47,93))(全部在一个条件下)它会产生超过 6000 行。而当它们分开时它会产生 2(fs.filter_id IN (147) 后跟 fs.filter_id IN (47,93))。不幸的是,我无法提供数据库。但是以这种方式运行查询时存在细微差别。
  • 是的,对不起,我有点分心了,当然不一样,因为IN 基本上是OR 的列表...
  • 没问题,我总是感谢任何帮助。我想我的问题是这样的条件是否可以写在 ORM 中,或者我是否最好走“在 Cake 中编写自己的 SQL”这条路?这是一种耻辱,因为 ORM 使编写大部分查询变得非常容易,而且我讨厌必须编写一些东西来构造 SQL 的所有这些部分。该应用程序允许用户选择各种过滤器,因此也有不同的过滤器,但现在我最感兴趣的是知道这是否可以通过 ORM 实现。

标签: cakephp query-builder cakephp-3.x


【解决方案1】:

你的问题基本上是你需要多次使用同一个标识符,这在PHP数组中是无效的。

你可以通过嵌套条件来解决这个问题:

->where([
    ['Substances.id IN' => $subquery_in],
    ['Substances.id IN' => $subquery_in2],
    'Substances.id NOT IN ' => $subquery_not_in
])

通过发出多个where() 调用:

->where(['Substances.id IN' => $subquery_in])
->where(['Substances.id IN' => $subquery_in2])
->where(['Substances.id NOT IN' => $subquery_not_in])

或使用表达式:

->where(function (\Cake\Database\Expression\QueryExpression $exp) use (
    $subquery_in,
    $subquery_in2,
    $subquery_not_in
) {
    return $exp
        ->in('Substances.id', $subquery_in)
        ->in('Substances.id', $subquery_in2)
        ->notIn('Substances.id', $subquery_not_in);
})

还可以将表达式与常规数组语法混合:

$inExpressions = $query
    ->newExpr()
    ->in('Substances.id', $subquery_in)
    ->in('Substances.id', $subquery_in2);

// ...

->where([
    $inExpressions,
    'Substances.id NOT IN ' => $subquery_not_in
])

另见

【讨论】:

  • 这真是太好了。用于构造此类语句的普通 PHP 更为复杂。我很高兴 ORM 能够做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多