UNION ALL in CTE
-
19-04-2021 - |
Pregunta
I'm trying to get a UNION ALL working with a CTE which I'm using for paging. I need to get all records matching a set of criteria from two tables, then page the results. The first table's CTE looks like this:
;WITH Results_CTE AS (SELECT t1.SomeIntKey1, ROW_NUMBER() OVER (ORDER BY SomeIntKey1) AS RowNum
FROM Table1 t1
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey1
WHERE Postcode LIKE 'CHX 1XX%'
) SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.SomeIntKey1 = d.Key WHERE RowNum >= 0 AND RowNum <= 10 OPTION(RECOMPILE)
The second table's paging SQL (which works fine) is:
;WITH Results_CTE AS (SELECT t2.SomeIntKey2, ROW_NUMBER() OVER (ORDER BY SomeIntKey2) AS RowNum
FROM Table2 t2
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey2
WHERE Postcode LIKE 'CHX 1XX%'
) SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.SomeIntKey2 = d.Key WHERE RowNum >= 0 AND RowNum <= 10 OPTION(RECOMPILE)
For the combined paged data, I've tried something like:
;WITH Results_CTE AS (SELECT t2.SomeIntKey2, ROW_NUMBER() OVER (ORDER BY SomeIntKey2) AS RowNum
FROM Table2 t2
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey2
WHERE Postcode LIKE 'CHX 1XX%'
UNION ALL
SELECT t1.SomeIntKey1
FROM Table1 t1
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey1
WHERE Postcode LIKE 'CHX 1XX%'
) SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.SomeIntKey2 = d.Key WHERE RowNum >= 0 AND RowNum <= 10 OPTION(RECOMPILE)
However, this results in an error:
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
I know UNION ALLs can be confusing at the best of times, especially with joins, but I'm essentially getting a list of INT keys from two tables, then joining them to a third which contains the data I need (keys from both tables will be present in the joined column on the Data
table.
Solución
You need to make sure that both result sets have the same columns:
WITH Results_CTE AS
(
SELECT
t2.SomeIntKey2 as Key,
ROW_NUMBER() OVER (ORDER BY SomeIntKey2) AS RowNum
FROM
Table2 t2
LEFT JOIN CalculatedData d
ON d.Key = t1.SomeIntKey2
WHERE Postcode LIKE 'CHX 1XX%'
UNION ALL
SELECT
t1.SomeIntKey1 as Key,
0 as RowNum
FROM
Table1 t1
LEFT JOIN CalculatedData d
ON d.Key = t1.SomeIntKey1
WHERE Postcode LIKE 'CHX 1XX%'
)
SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.SomeIntKey2 = d.Key
WHERE RowNum >= 0 AND RowNum <= 10 OPTION(RECOMPILE)
Please note that the second part of the UNION ALL
now always returns 0 for the RowNum. If you want to have the RowNum column for the result of the UNION ALL
you need another sub query:
WITH Results_CTE AS
(
SELECT
s.Key,
ROW_NUMBER() OVER (ORDER BY s.Key) AS RowNum
FROM
(
SELECT
t2.SomeIntKey2 as Key,
FROM
Table2 t2
LEFT JOIN CalculatedData d
ON d.Key = t1.SomeIntKey2
WHERE Postcode LIKE 'CHX 1XX%'
UNION ALL
SELECT
t1.SomeIntKey1 as Key
FROM
Table1 t1
LEFT JOIN CalculatedData d
ON d.Key = t1.SomeIntKey1
WHERE Postcode LIKE 'CHX 1XX%'
)
)
SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.SomeIntKey2 = d.Key
WHERE RowNum >= 0 AND RowNum <= 10 OPTION(RECOMPILE)
Otros consejos
Your second SELECT in the your UNION ALL doesn't return the same number of columns, hence the problem.
Try:
;WITH Results_CTE AS (
SELECT IntKey, ROW_NUMBER() OVER (ORDER BY IntKey) AS RowNum
FROM
(
SELECT t2.SomeIntKey2 AS IntKey
FROM Table2 t2
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey2
WHERE Postcode LIKE 'CHX 1XX%'
UNION ALL
SELECT t1.SomeIntKey1 AS IntKey
FROM Table1 t1
LEFT JOIN CalculatedData d ON d.Key = t1.SomeIntKey1
WHERE Postcode LIKE 'CHX 1XX%'
) t
)
SELECT * FROM Results_CTE a INNER JOIN CalclatedData d ON a.IntKey = d.Key