推测相当于“存在”

时间:2011-02-01 04:33:32

标签: php sql propel

我是Propel的新手并且一直在阅读文档。但是,我没有找到一个明确的等同于SQL的EXISTS和NOT EXISTS结构。例如,Linq in .NET有Any()。在“惯用语”Propel中是否有以下等同物?

SELECT a.column1, a column2, a.etc
FROM   TableA a
WHERE  NOT EXISTS (SELECT 1
                   FROM   TableB b
                   WHERE  b.someIdColumn = a.someIdColumn
                   AND    b.aNullableDateColumn IS NULL)

4 个答案:

答案 0 :(得分:5)

在做了一些挖掘之后,我相信我对我的问题有了答案,或者至少是现有的答案。

EXISTS或NOT EXISTS之后出现的是子查询。虽然这个事实看起来很明显,但我最初并没有把注意力集中在子查询上。我在这个主题上找到了few resources。基本上,选项是使用JOIN重写查询(这是@Kaltas答案的核心)或使用Criteria::CUSTOM。我决定我可能更喜欢第二个选项,因为它允许我保留子查询,可能有助于我的数据库性能。

我做了很多关于Criteria::CUSTOM的阅读,但是真正帮助我的唯一阅读是阅读Propel 1.5源代码。真的很简单。只需在where调用中逐字地(使用数据库的表和列名称, Propel的对象名称)以及EXISTS或NOT EXISTS放置子查询,例如:

TableAQuery::create()
    ->where('NOT EXISTS (SELECT 1 FROM TableB WHERE TableA.someIdColumn = TableB.someIdColumn AND TableB.aNullableDateColumn IS NULL)')
    ->find();

就这么简单。在内部,where方法经历了一些解释子句的可能性,并且找不到匹配项,它将该子句视为Criteria::CUSTOM并将其按原样插入到SQL查询中。因此,我不能使用表别名,例如。

如果我有时间,也许我会采用更“ORM-ish”的方式来做这件事并提交补丁。不过,有人可能会打败我。

答案 1 :(得分:0)

我认为您可以将查询重写为:

SELECT
  a.column1,
  a.column2,
  a.etc
FROM
  TableA a
WHERE
  (SELECT
    COUNT(*)
  FROM
    TableB b
  WHERE
    b.someIdColumn = a.someIdColumn
      AND
    b.aNullableDateColumn IS NULL
  ) > 0

在Propel中很容易实现。

甚至更清洁,更容易在Propel中完成:

SELECT
  a.column1,
  a.column2,
  a.etc
FROM
  TableA a
    LEFT JOIN
  TableB b ON (b.someIdColumn = a.someIdColumn)
WHERE
  b.aNullableDateColumn IS NULL
    AND
  b.primaryKeyColumn IS NOT NULL

答案 2 :(得分:0)

与推进1.6一样,你现在可以使用Criteria::INCriteria::NOT_IN

示例:选择不在UserGroup中的所有用户

$users = UserQuery::create()->filterById(
                                        UserPerUserGroupQuery::create()->select('user_id')->find(), CRITERIA::NOT_IN
                                         )
                             ->orderByUserName()
                             ->find();

答案 3 :(得分:0)

旧线程,在Propel中仍然不存在。

但是,您可以使用子查询和 group by 来模拟存在。仍然很糟糕,但是如果您不想使用SQL,则可以留在Propel中:

$caseBIsNull = 'CASE WHEN TableB.aNullableDateColumn IS NULL THEN 1 ELSE NULL END';
$subQuery = TableAQuery::create()
  ->leftJoinTableB()         // Propel might break without a join, see below
  ->addAsColumn('cnt', "COUNT($caseBIsNull)")
  ->addAsColumn('totalBs', 'COUNT(TableB.someIdColumn)')
  ->addAsColumn('someIdColumn', 'TableA.someIdColumn')
  ->groupBy('TableA.someIdColumn')
  ->having('cnt = 0 OR totalBs = 0') 
;

TableAQuery::create()
  ->addSelectQuery($subQuery, 'groupedExists', false)
  ->where("TableA.someIdColumn = groupedExists.someIdColumn")
;

子查询获取所有ID,其中不计算任何空数据列或TableB中不存在匹配条目,并且联接删除所有具有不匹配ID的行。

在SQL中,这将是:

SELECT a.*
FROM   TableA a
JOIN   (
    SELECT    a1.someIdColumn,
              COUNT(...) as cnt,
              COUNT(b.someIdColumn) as totalBs
    FROM      TableA a1
    LEFT JOIN TableB b ON (a1.someIdColumn = b.someIdColumn)
    GROUP BY  a.someIdColumn
    HAVING    cnt = 0 OR totalBs = 0
) mockExists ON a.someIdColumn = mockExists.someIdColumn;

本机“ EXISTS”不需要加入并分组,并且可以在第一次出现时中止,因此这可能效率较低。

请注意,至少在Propel 2.0.0-dev中,您需要在子查询中具有联接,否则该语句将在没有表的情况下构建并失败。

顺便说一句,如果您需要使用SQL但想要获取Propel对象,则可以使用ObjectFormatter

use Propel\Runtime\Propel;
use Propel\Runtime\Formatter\ObjectFormatter;

$query     = 'SELECT ... WHERE EXISTS ...';
$con       = Propel::getConnection();
$stmt      = $con->prepare($query);
$stmt->execute();
$fetcher   = $con->getDataFetcher($stmt);
$formatter = new ObjectFormatter();
$criteria  = TableAQuery::create();
$result    = $formatter->init($criteria)->format($fetcher);