UNION and UNION ALL let you merge the results of two or more independent queries into a single result set. Both queries can target different files, different paths within the same file, or different filter conditions.
UNION vs UNION ALL
| Clause | Duplicate rows |
|---|
UNION | Removed (hash-based deduplication) |
UNION ALL | Kept |
Column count requirement
The number of selected columns must match across all combined queries. If they do not match, FiQueLa throws a QueryLogicException.
// These column counts must be equal
$query1->select('name', 'price'); // 2 columns
$query2->select('name', 'price'); // 2 columns — OK
Queries using SELECT * skip this validation.
Basic usage
use FQL\Enum\Operator;
use FQL\Stream\Json;
$stream = Json::open('./data/products.json');
$cheapProducts = $stream->query()
->select('id', 'name', 'price')
->from('data.products')
->where('price', Operator::LESS_THAN_OR_EQUAL, 100);
$expensiveProducts = $stream->query()
->select('id', 'name', 'price')
->from('data.products')
->where('price', Operator::GREATER_THAN_OR_EQUAL, 400);
// UNION — removes duplicate rows
$results = $cheapProducts->union($expensiveProducts)->execute();
// UNION ALL — keeps all rows including duplicates
$results = $cheapProducts->unionAll($expensiveProducts)->execute();
Chaining multiple unions
You can chain as many unions as needed. Each union executes its own independent pipeline.
use FQL\Enum\Operator;
use FQL\Stream\Json;
$stream = Json::open('./data/products.json');
$q1 = $stream->query()
->select('id', 'name', 'price')
->from('data.products')
->where('id', Operator::EQUAL, 1);
$q2 = $stream->query()
->select('id', 'name', 'price')
->from('data.products')
->where('id', Operator::EQUAL, 3);
$q3 = $stream->query()
->select('id', 'name', 'price')
->from('data.products')
->where('id', Operator::EQUAL, 5);
$results = $q1
->union($q2)
->unionAll($q3)
->execute();
How UNION interacts with EXPLAIN
When you run EXPLAIN or EXPLAIN ANALYZE on a query that includes a union, each union branch reports its own sub-phases in the output.
Single union produces phases like:
stream → where → union_stream → union_where → union
Multiple unions index each branch:
stream → where → union_1_stream → union_1_where → union_1
union_2_stream → union_2_where → union_2
See EXPLAIN and EXPLAIN ANALYZE for the full column reference.
Each union sub-query executes its own full pipeline — FROM → WHERE → GROUP BY → SELECT → etc. Clauses like ORDER BY and LIMIT on the main query apply to the final merged result, not to individual branches.