TYPO3 DBAL Querybuilder:嵌套 SELECT 语句?

TYPO3 DBAL Querybuilder: Nested SELECT statements?

是否可以使用 DBAL QueryBuilder 构建如下所示的嵌套 SELECT 语句?

SELECT i.id, i.stable_id, i.version, i.title
FROM initiatives AS i
INNER JOIN (
    SELECT stable_id, MAX(version) AS max_version FROM initiatives GROUP BY stable_id
) AS tbl1
ON i.stable_id = tbl1.stable_id AND i.version = tbl1.max_version
ORDER BY i.stable_id ASC

目标是查询外部非 TYPO3 table,其中包含每个数据集的不同版本。只应呈现版本号最高的数据集。数据库如下所示:

id, stable_id, version, [其余数据行]

stable_id是数据集的外部id。 id 是内部自增 id。 version 也会自动递增。

代码示例:

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
    ->select(...$this->select)
    ->from($this->table)
    ->join(
        'initiatives',
        $queryBuilder
            ->select('stable_id, MAX(version) AS max_version' )
            ->from('initiatives')
            ->groupBy('stable_id'),
        'tbl1',
        $queryBuilder->and(
            $queryBuilder->expr()->eq(
                'initiatives.stable_id',
                $queryBuilder->quoteIdentifier('tbl1.stable_id')
            ),
            $queryBuilder->expr()->eq(
                'initiatives.version',
                $queryBuilder->quoteIdentifier('tbl1.max_version')
            )
        )
    )
    ->orderBy('stable_id', 'DESC')

我想不出 ON ... AND 语句的正确语法。有什么想法吗?

Extbase 查询具有 JOIN 功能,但在其他方面非常有限。不过,您可以使用自定义 SQL(参见 ->statement() here)。

构建复杂查询的更好 API 是 (Doctrine DBAL) QueryBuilder, including support for JOINs, database functions like MAX() and raw expressions (->addSelectLiteral()). Make sure to read until the ExpressionBuilder 它变得有趣的地方。

所以 Extbase 查询对于检索 Extbase(模型)对象很有用。它可以隐含地使用它对您的数据结构的了解,以便为您节省一些代码,但只支持相当简单的查询。

(Doctrine DBAL) QueryBuilder 满足所有其他需求。如果需要,您也可以将原始数据转换为 Extbase 模型。 (for example $propertyMapper->convert($data, Job::class)).

我意识到我们对两者缺乏明确的区分,因为它们在某个时候都被称为“QueryBuilder”,但它们是完全不同的。这就是为什么我喜欢在提到非 Extbase 时添加“Doctrine”。

具有 JOIN ON 多个条件的示例。

$q = TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(TYPO3\CMS\Core\Database\ConnectionPool::class)
    ->getQueryBuilderForTable('fe_users');

$res = $q->select('*')
    ->from('tt_content', 'c')
    ->join(
        'c',
        'be_users',
        'bu',
        $q->expr()->andX(
            $q->expr()->eq(
                'c.cruser_id', $q->quoteIdentifier('bu.uid')
            ),
            $q->expr()->comparison(
                '2', '=', '2'
            )
        )
    )
    ->setMaxResults(5)
    ->execute()
    ->fetchAllAssociative();

简短回答:这是不可能的,因为要加入的 table 是动态生成的。相关表达式被反引号,因此导致 SQL 错误。

但是:SQL 查询可以更改为下面的 SQL 查询,其功能基本相同:

SELECT i1.id,stable_id, version, title, p.name, l.name, s.name
FROM initiatives i1
WHERE version = (
    SELECT MAX(i2.version)
    FROM initiatives i2
    WHERE i1.stable_id = i2.stable_id
)
ORDER BY stable_id ASC

这可以用 DBAL queryBuilder 重建:

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
    ->select(...$this->select)
    ->from($this->table)
    ->where(
        $queryBuilder->expr()->eq(
            'initiatives.version',
            '(SELECT MAX(i2.version) FROM initiatives i2 WHERE initiatives.stable_id = i2.stable_id)'
        ),
    ->orderBy('stable_id', 'DESC')
    ->setMaxResults( 50 )
    ->execute();