Doctrine2 DBAL 存在查询

pza*_*zaj 2 mysql symfony dbal

我想请求您帮助Doctrine2 DBAL使用QueryBuilder. 我习惯了 ORM,但我认为对于在侦听器中调用的此类查询来说,这是一种矫枉过正的做法。

我需要一个查询SELECT EXISTS,但我不知道如何使用 DBAL 构造它QueryBuilder

我已经创建了一个子查询:

$subQuery = $connection->createQueryBuilder();
$subQuery
    ->select('o.id')
    ->from('order', 'o')
    ->leftJoin('o', 'payment', 'p')
    ->where($subQuery->expr()->isNull('p.id'))
;
Run Code Online (Sandbox Code Playgroud)

我主要是想检查是否有未付款的订单。我现在不知道如何构建SELECT EXISTS查询?有人能指出我正确的方向吗?我在想这样的事情:

$qb->select('EXISTS(?)')->setParameter($subQuery->getDQL())
Run Code Online (Sandbox Code Playgroud)

这是正确的解决方案吗?

@编辑

经过一段时间的思考,我决定使用 ORM。不幸的是,这也不起作用,我收到一个错误:

line 0, col 7: Error: Expected known function, got 'EXISTS'

DQL 为: SELECT EXISTS(<subquery here>)

考虑到它是用 QueryBuilder 构建的,这有点奇怪:

/* @var $qb QueryBuilder */
$qb = $this->em->createQueryBuilder();
$qb
        ->select($qb->expr()->exists($subQuery->getDQL()));
Run Code Online (Sandbox Code Playgroud)

fyr*_*rye 5

晚了几年,但您需要在 QueryBuilder 的or语句部分EXISTS中指定子查询 SQL ,而不是使用参数。SELECTWHERE

此外,由于order是 MySQL 中的保留字,因此您需要使用标识符引号 ` (反引号)来转义表名。

使用 ORM 时;您必须指定FROM引用实体的语句,因此您需要更改您的方法。

$connection = $this->em->getConnection();
$expr = $connection->getExpressionBuilder();
$qbSub = $connection->createQueryBuilder()
    ->select('1')
    ->from('`order`', 'o')
    ->leftJoin('o', '`payment`', 'p', $expr->eq('p.order_id', 'o.id'))
    ->where($expr->isNull('p.id'));

/**
 * @return string "1" if a record exists, "0" otherwise
 */
$connection->createQueryBuilder()
    ->select('EXISTS(' . $qbSub->getSQL() . ')')
    ->execute()
    ->fetchColumn();
Run Code Online (Sandbox Code Playgroud)

结果 SQL

SELECT EXISTS(
   SELECT 1
   FROM `order` AS o
   LEFT JOIN `payment` AS p
   ON p.order_id = o.id
   WHERE p.id IS NULL
);
Run Code Online (Sandbox Code Playgroud)

QueryBuilder::setParameter()注意:如果您有任何参数,则必须使用顶级查询而不是子查询来绑定占位符的值。

$qbSub = $connection->createQueryBuilder()
    ->select('1')
    ->from('`order`', 'o')
    ->leftJoin('o', '`payment`', 'p', $expr->andX(
        $expr->eq('p.order_id', 'o.id'), 
        $expr->eq('p.name', ':name') // subquery placeholder
    ))
    ->where($expr->isNull('p.id'));

$connection->createQueryBuilder()
    ->select('EXISTS(' . $qbSub->getSQL() . ')')
    ->setParameter('name', $value) // subquery placeholder param value
    ->execute()
    ->fetchColumn();
Run Code Online (Sandbox Code Playgroud)

但是,我建议将您的查询从排除联接更改为包含联接NOT EXISTS。这样做将从结果集中过滤掉已付款的订单。而不是尝试加入每笔付款的每笔订单并检索返回的付款null。极大地提高了查询的性能。

示例db-fiddle

SELECT EXISTS (
    SELECT 1
    FROM `order` AS o
    WHERE NOT EXISTS(
        SELECT NULL
        FROM `payment` AS p
        WHERE p.order_id = o.id
    )
)
Run Code Online (Sandbox Code Playgroud)