mirror of
https://github.com/doctrine/dbal.git
synced 2026-03-23 22:32:15 +01:00
Make it possible to create union main part for a CTE (#7326)
<!-- Fill in the relevant information below to help triage your pull request. --> | Q | A |------------- | ----------- | Type | bug | Fixed issues | #7318 #### Summary <!-- Provide a summary of your change. --> Technically this change adopts the solution for adding the CTE with parts to a SELECT query for UNION query handling using the `WithSQLBuilder` in case with parts has been set to allow creating queries like ```sql WITH -- CTE with parts cte_a AS (SELECT * FROM a_table) -- CTEmain part (SELECT cte_a.*, 'lit1' from cte_a) UNION (SELECT cte_a.*, 'lit2' from cte_a) ``` using the `union` and `with` api on the same level: ```php $qb = $connection->createQueryBuilder(); $cte = $qb->sub() ->select('*') ->from('a_table'); $union1 = $qb->sub() ->select('cte_a.*', $qb->expr()->literal('lit1')) ->from('cte_a'); $union1 = $qb->sub() ->select('cte_a.*', $qb->expr()->literal('lit2')) ->from('cte_a'); $qb->with('cte_a', $cte) ->union($union1) ->addUnion($union2); ``` This is a valid use-case and supported by databases supporting common table expressions albeit I could not find that documented in any documentation and real world use-cases exists and is the reason why this change has been considered as bugfix.
This commit is contained in:
@@ -369,7 +369,10 @@ or QueryBuilder instances to one of the following methods:
|
||||
->setMaxResults(100);
|
||||
|
||||
Common Table Expressions
|
||||
~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`SELECT` main query
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To define Common Table Expressions (CTEs) that can be used in select query.
|
||||
|
||||
@@ -400,6 +403,39 @@ Multiple CTEs can be defined by calling the with method multiple times.
|
||||
|
||||
Values of parameters used in a CTE should be defined in the main QueryBuilder.
|
||||
|
||||
`UNION` main query part
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To define Common Table Expressions (CTEs) that can be used in union query the union
|
||||
api needs to be used instead of using `select()`:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$qb = $connection->createQueryBuilder();
|
||||
|
||||
$baseQueryBuilder = $qb->sub()
|
||||
->select('id')
|
||||
->from('table_a');
|
||||
|
||||
$unionPart1 = $qb->sub()
|
||||
->select('id', $qb->expr()->literal('first') . ' AS value', '1 AS sort')
|
||||
->from('cte_base')
|
||||
->where($qb->expr()->eq('id', ':id1'));
|
||||
|
||||
$unionPart2 = $qb->sub()
|
||||
->select('id', $qb->expr()->literal('second') . ' AS value', '2 AS sort')
|
||||
->from('cte_base')
|
||||
->where($qb->expr()->eq('id', ':id2'));
|
||||
|
||||
$qb->with('cte_base', $baseQueryBuilder)
|
||||
->union($unionPart1)
|
||||
->addUnion($unionPart2)
|
||||
->orderBy('sort')
|
||||
->setParameter('id1', 2)
|
||||
->setParameter('id2', 1);
|
||||
|
||||
Building Expressions
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -1445,7 +1445,15 @@ class QueryBuilder
|
||||
);
|
||||
}
|
||||
|
||||
return $this->connection->getDatabasePlatform()
|
||||
$databasePlatform = $this->connection->getDatabasePlatform();
|
||||
$unionParts = [];
|
||||
if (count($this->commonTableExpressions) > 0) {
|
||||
$unionParts[] = $databasePlatform
|
||||
->createWithSQLBuilder()
|
||||
->buildSQL(...$this->commonTableExpressions);
|
||||
}
|
||||
|
||||
$unionParts[] = $databasePlatform
|
||||
->createUnionSQLBuilder()
|
||||
->buildSQL(
|
||||
new UnionQuery(
|
||||
@@ -1454,6 +1462,8 @@ class QueryBuilder
|
||||
new Limit($this->maxResults, $this->firstResult),
|
||||
),
|
||||
);
|
||||
|
||||
return implode(' ', $unionParts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -528,6 +528,38 @@ final class QueryBuilderTest extends FunctionalTestCase
|
||||
self::assertSame($expectedRows, $qb->executeQuery()->fetchAllAssociative());
|
||||
}
|
||||
|
||||
public function testCTEUnionMainQuery(): void
|
||||
{
|
||||
if (! $this->platformSupportsCTEs()) {
|
||||
self::markTestSkipped('The database platform does not support CTE.');
|
||||
}
|
||||
|
||||
$expectedRows = [['id' => 2, 'value' => 'first', 'sort' => 1], ['id' => 1, 'value' => 'second', 'sort' => 2]];
|
||||
$expectedRows = $this->prepareExpectedRows($expectedRows);
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
|
||||
$baseQueryBuilder = $qb->sub()
|
||||
->select('id')
|
||||
->from('for_update');
|
||||
|
||||
$unionPart1 = $qb->sub()
|
||||
->select('id', $qb->expr()->literal('first') . ' AS value', '1 AS sort')
|
||||
->from('cte_base')
|
||||
->where($qb->expr()->eq('id', '2'));
|
||||
|
||||
$unionPart2 = $qb->sub()
|
||||
->select('id', $qb->expr()->literal('second') . ' AS value', '2 AS sort')
|
||||
->from('cte_base')
|
||||
->where($qb->expr()->eq('id', '1'));
|
||||
|
||||
$qb->with('cte_base', $baseQueryBuilder)
|
||||
->union($unionPart1)
|
||||
->addUnion($unionPart2)
|
||||
->orderBy('sort');
|
||||
|
||||
self::assertSame($expectedRows, $qb->executeQuery()->fetchAllAssociative());
|
||||
}
|
||||
|
||||
public function testPlatformDoesNotSupportCTE(): void
|
||||
{
|
||||
if ($this->platformSupportsCTEs()) {
|
||||
@@ -549,7 +581,7 @@ final class QueryBuilderTest extends FunctionalTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<string, int>> $rows
|
||||
* @param array<array<string, int|string>> $rows
|
||||
*
|
||||
* @return array<array<string, int|string>>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user