1

这是一个非常明显的数据问题,但我在任何地方都找不到简单的解决方案。

使用 TYPO3 QueryBuilder,如何从每个用户有多个条目的表中为每个用户选择最近的条目?

uid  user_id  value  crdate
1    1        0      123456
2    1        1      123400
3    2        1      123356
4    2        0      123300

我已经尝试了许多原始 SQL 方法,并最终找到了一种基于此解决方案的有效方法 - 如何在 SQL 中的另一列中选择具有 MAX(列值)、DISTINCT 的行?

SELECT * 
FROM `tx_tablename` AS `tt` 
INNER JOIN (
    SELECT `uid`, `user_id`, MAX(`crdate`) AS `MaxDateTime` 
    FROM `tx_tablename` 
    GROUP BY `user_id`
) AS `groupedtt` 
ON `tt`.`user_id` = `groupedtt`.`user_id` 
AND `tt`.`crdate` = `groupedtt`.`MaxDateTime` 
WHERE `tt`.`consent_content` = 3

但是我看不到如何在 QueryBuilder 中重现这一点,因为 ->join() 语句只接受表名作为参数,而不接受 SQL,并且 ->join() 只接受一个连接条件,而不是两个。

有没有其他人找到适用于 QueryBuilder 的解决方案?非常感谢

4

3 回答 3

2

引用是在 TYPO3 QueryBuilder 中完成的。您可以直接使用 ConcreteQueryBuilder 绕过它。

但是这样做,你必须自己引用标识符,否则会抛出异常。

这应该可以在您的伪代码中解决问题:

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
...
$subQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable('tx_tablename');

$subQuery = $subQueryBuilder
  ->select('uid', 'user_id')
  ->from('tx_tablename')
  ->addSelectLiteral(
    $subQueryBuilder->expr()->max('crdate', 'max_crdate')
  )
  ->groupBy('user_id');

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable('tx_tablename');

$queryResult = $queryBuilder
  ->select('a.*')
  ->from('tx_tablename', 'a')
;

$queryBuilder
    ->getConcreteQueryBuilder()
        ->innerJoin(
            $queryBuilder->quoteIdentifier('a'), // !!! important, quote identifier yourself
            '(' . $subQuery->getSQL() . ')',
            $queryBuilder->quoteIdentifier('b'), // !!! important, quote identifier yourself
            $queryBuilder->expr()->andX(
                $queryBuilder->expr()->eq('a.user_id', $queryBuilder->quoteIdentifier('b.user_id')),
                $queryBuilder->expr()->eq('a.crdate', $queryBuilder->quoteIdentifier('b.max_crdate'))
            ) // andX()
        ) // innerJoin()
;

$queryResult = $queryBuilder->execute();

编辑 1

固定代码示例。需要quoteIdentifier()而不是createNamedParam().

笔记

如果您使用嵌套选择/子选择 AND 使用命名参数,则必须使用最外层的 queryBuilder 实例来创建命名参数,而不是当前级别的 queryBuilder。

于 2020-07-15T22:11:48.300 回答
0

你是对的——你不能在join()innerJoin()leftJoin()rightJoin()TYPO3 中使用子查询作为参数,因为这些值是使用转义的quoteIdentifier()(参见GitHub 上的 TYPO3 v10.2 源代码)并添加了反引号。

我想知道以下 SQL 查询是否返回您所追求的结果:

SELECT `uid`, `user_id`, value, MAX(`crdate`)
FROM `tx_tablename`
GROUP BY `user_id`
HAVING MAX(`crdate`);

在这种情况下,Doctrine 代码如下所示:

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
...
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable('tx_tablename');

$queryResult = $queryBuilder
  ->select('uid', 'user_id', 'value')
  ->from('tx_tablename')
  ->addSelectLiteral(
    $queryBuilder->expr()->max('crdate', 'crdate')
  )
  ->add('having', 'MAX(`crdate`)')
  ->groupBy('user_id')
  ->execute();
于 2019-12-28T03:35:05.577 回答
0

您可能需要一个子查询。试试下面的。

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
...
$subQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable('tx_tablename');

$subQuery = $subQueryBuilder
  ->select('uid', 'user_id')
  ->from('tx_tablename')
  ->addSelectLiteral(
    $subQueryBuilder->expr()->max('crdate', 'max_crdate')
  )
  ->groupBy('user_id');

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable('tx_tablename');

$queryResult = $queryBuilder
  ->select('a.*')
  ->from('tx_tablename', 'a')
  ->innerJoin(
    'a',
    '(' . $subQuery->getSQL() . ')',
    'b',
    $queryBuilder->expr()->andX(
      $queryBuilder->expr()->eq('a.user_id', $queryBuilder->createNamedParameter('b.user_id', \PDO::PARAM_STR)),
      $queryBuilder->expr()->eq('a.crdate', $queryBuilder->createNamedParameter('b.max_crdate', \PDO::PARAM_STR))
    )
  )
  ->execute();

但是,现在的代码在查询中产生了双反引号 ( `innerJoin() ) 。我不知道如何摆脱它们,但代码显示了这个概念。

于 2019-12-24T07:59:24.270 回答