Reputation: 2378
I am looking for advice on the best way to join using an alias instead of the original data. e.g the data is modified before it is joined.
An example:
CREATE TABLE Table1 (
No1 varchar(10)
);
CREATE TABLE Table2 (
No1 varchar(10)
);
INSERT INTO Table1 (No1)
VALUES ('222');
INSERT INTO Table2 (No1)
VALUES ('111');
If i created a join with a case statement but i wanted to join on the alias of the case statement this doesnt work with usual join syntax e.g.
SELECT
CASE WHEN T1.[No1] = '222' THEN '111' ELSE T1.[No1] END AS [T1 No],
T2.[No1] AS [T2 No]
FROM Table1 T1
FULL JOIN Table2 T2
ON T1.[No1] = T2.[No1]
This gives result:
| T1 No | T2 No |
|--------+--------|
| 111 | (null) |
| (null) | 111 |
http://www.sqlfiddle.com/#!18/203e8/1
However, the approach i have taken to join on the alias is:
SELECT
T1.[T1 No],
T2.[No1] AS [T2 No]
FROM
(
SELECT
CASE WHEN T1.[No1] = '222' THEN '111' ELSE T1.[No1] END AS [T1 No]
FROM Table1 T1
) T1
JOIN Table2 T2
ON T1.[T1 No] = T2.[No1]
This gives result:
| T1 No | T2 No |
|-------+-------|
| 111 | 111 |
http://www.sqlfiddle.com/#!18/5fd7c/14
Which is exactly what i am looking for. However, the real life query i am dealing with is huge and sub-querying it to join on an alias makes it so messy.
Can anyone give me advice on a better approach to this? or is this the only way to do it?
Upvotes: 7
Views: 6751
Reputation: 1269503
I'm not sure why you are using full join
rather than inner join
. But another solution is to use apply
:
SELECT . . .
FROM Table1 T1 CROSS APPLY
(VALUES (CASE WHEN T1.[No1] = '222' THEN '111' ELSE T1.[No1] END)
) V([T1 No]) JOIN
Table2 T2
ON V.[T1 No] = T2.[No1];
APPLY
can be handy for adding in computed columns. You don't need subqueries or CTEs. The changes to the query are minimal.
Upvotes: 0
Reputation: 3906
As variant you can use an auxiliary table
CREATE TABLE Link(
Table1_No1 varchar(10),
Table2_No1 varchar(10),
PRIMARY KEY(Table1_No1),
UNIQUE(Table1_No1,Table2_No1)
)
INSERT Link(Table1_No1,Table2_No1)VALUES
('222','111'),
('444','333'),
...
And then a query
SELECT
T1.No1 [T1 No],
T2.No1 [T2 No]
FROM
(
SELECT ISNULL(L.Table2_No1,T1.No1) No1
FROM Table1 T1
LEFT JOIN Link L ON L.Table1_No1=T1.No1
) T1
JOIN Table2 T2 ON T1.No1=T2.No1
This way is useful because you don't need rewrite your query for new conditions.
And if this variant suits you, you can write it more shorter
SELECT
ISNULL(L.Table2_No1,T1.No1) [T1 No],
T2.No1 [T2 No]
FROM Table1 T1
LEFT JOIN Link L ON L.Table1_No1=T1.No1
JOIN Table2 T2 ON T2.No1=ISNULL(L.Table2_No1,T1.No1)
Upvotes: 1
Reputation: 14189
If you want to avoid writing your expressions multiple times, then the only option is joining after asigning the expression to an alias (and the join must be in an outmost scope, so it's treated like a table). If the problem is the tidiness, I always find using CTEs a lot more readable than subquerying in the FROM
.
;WITH ComplexQueryCalculations AS
(
SELECT
ID = T.ID,
SomeColumn = T.SomeColumn,
ExpressionResult = CASE
WHEN T.PlanetsAlign = 1 AND X.OtherColumn > 100
THEN (N.OtherColumn * 100) / NULLIF(N.AnotherColumn, 0)
ELSE
N.OtherColumn END
FROM
Table1 AS T
INNER JOIN Table2 AS N ON T.SomeColumn = N.SomeColumn
LEFT JOIN Table3 AS X ON
T.SomeColumn = CONVERT(INT, X.SomeColumn) AND
N.SomeColumn = X.OtherColumn
WHERE
T.ID <= 15000 AND
CHARINDEX('_', T.SomeColumn) > 1 AND
(
T.SomeColumn <> 'Property' OR
(T.SomeColumn = 'Property' AND X.SomeColumn BETWEEN 1 AND 100)
)
),
FilteredExpressionResult AS
(
SELECT
C.ID,
C.SomeColumn,
C.ExpressionResult
FROM
ComplexQueryCalculations AS C -- Reference previous CTE
WHERE
C.ExpressionResult >= 50 -- Filter the alias!
)
SELECT
F.*
FROM
FilteredExpressionResult AS F
INNER JOIN YetAnotherTable AS Y ON F.ID = Y.ID
WHERE
Y.SomeColumn IS NOT NULL
You can keep your subqueries separated, give them proper spacing and table alias, and give the person who is reading a proper query order, without spending resources by creating temporary or variable tables in the middle.
Upvotes: 0
Reputation: 95554
Ok, firstly, it's probably good for you to be aware of the Logical Processing Order of the SELECT statement. Specifically, that order is:
Notice that that SELECT
is the 8th thing to be processed, which is when the alias of a column would be processed. This means you can't reference a columns alias until step 9 (DISTINCT
), which really means your left with doing so in the ORDER BY
and that's it.
Thus, if you want to reference a column that is derived by an expression, you have few ways of doing so, some I have listed below.
1st way:
Use the Expression in the SELECT
and the ON
clause. Thus:
SELECT CASE WHEN T1.[No1] = '222' THEN '111'
ELSE T1.[No1]
END AS [T1 No],
T2.[No1] AS [T2 No]
FROM Table1 T1
JOIN Table2 T2 ON CASE WHEN T1.[No1] = '222' THEN '111'
ELSE T1.[No1]
END = T2.[No1];
This can make things a little confusing, as it can make the query "busy".
2nd way:
Use a Subselect:
SELECT [T1 No]
FROM (SELECT CASE WHEN T1.[No1] = '222' THEN '111'
ELSE T1.[No1]
END AS [T1 No],
FROM Table1 T1) AS Tsq1
JOIN Table2 T2 ON Tsq1.[T1 No] = T2.[No1];
3rd way
This is basically the same as the last option, however, using a CTE
WITH T1 AS (
SELECT CASE WHEN T1.[No1] = '222' THEN '111'
ELSE T1.[No1]
END AS [T1 No],
FROM Table1 T1)
SELECT [T1 No]
FROM T1
JOIN Table2 T2 ON T1.[T1 No] = T2.[No1];
4th Way:
You also could create a VIEW
, and then JOIN
on that:
CREATE VIEW Table1_vw AS
SELECT *,
SELECT CASE WHEN T1.[No1] = '222' THEN '111'
ELSE T1.[No1]
END AS [T1 No]
FROM Table1 T1;
GO
SELECT T1.[T1 No]
FROM Table1_vw T1
JOIN Table2 T2 ON T1.[T1 No] = T2.[No1];
These are just a few options, but hopefully that puts you on the right path for what works for your own needs.
Upvotes: 10
Reputation: 48177
As HoneyBadger said. Use the CASE Both in the Select and on the ON condition
SELECT CASE WHEN T1.[No1] = '222' THEN '111' ELSE T1.[No1] END AS [T1 No],
T2.[No1] AS [T2 No]
FROM Table1 T1
JOIN Table2 T2
ON CASE WHEN T1.[No1] = '222' THEN '111' ELSE T1.[No1] END = T2.[No1];
The problem is you cant use the alias because the execution order of the SELECT
As you can see here Order Of Execution of the SQL query
The JOIN happen before the SELECT create the alias
Upvotes: 2