我是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)
答案 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::IN
和Criteria::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);