别名上的JOIN语句 - SQL Server

时间:2018-04-11 08:32:58

标签: sql sql-server join alias

我正在寻找有关使用别名而不是原始数据加入的最佳方式的建议。例如,数据在加入之前就已被修改。

一个例子:

CREATE TABLE Table1 (
No1 varchar(10)
);
CREATE TABLE Table2 (
No1 varchar(10)
);

INSERT INTO Table1 (No1)
VALUES ('222');
INSERT INTO Table2 (No1)
VALUES ('111');

如果我创建了一个带有case语句的连接,但是我想加入case语句的别名,这不适用于通常的连接语法,例如。

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]

这给出了结果:

|  T1 No |  T2 No |
|--------+--------|
|    111 | (null) |
| (null) |    111 |

http://www.sqlfiddle.com/#!18/203e8/1

但是,我加入别名的方法是:

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]

这给出了结果:

| T1 No | T2 No |
|-------+-------|
|   111 |   111 |

http://www.sqlfiddle.com/#!18/5fd7c/14

这正是我要找的。但是,我正在处理的现实生活中的查询是巨大的,并且查询它加入别名会使它变得如此混乱。

任何人都可以就更好的方法向我提出建议吗?或者这是唯一的方法吗?

5 个答案:

答案 0 :(得分:4)

好的,首先,了解Logical Processing Order of the SELECT statement可能对您有好处。具体来说,该顺序是:

  1. FROM
  2. ON
  3. JOIN
  4. WHERE
  5. GROUP BY
  6. WITH CUBE或WITH ROLLUP
  7. HAVING
  8. 选择
  9. DISTINCT
  10. ORDER BY
  11. TOP
  12. 请注意,SELECT是要处理的第8件事,即处理列的别名时。这意味着您无法在第9步(DISTINCT)之前引用列别名,这实际上意味着您在ORDER BY中执行了此操作,而这就是它。

    因此,如果你想引用一个由表达式派生的列,你几乎没有办法这样做,我在下面列出了一些。

    第一路:

    使用SELECTON子句中的表达式。因此:

    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];
    

    这可能会让事情变得有点混乱,因为它可以使查询“忙碌”#34;

    第二路:

    使用子选择:

    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];
    

    第三种方式

    这与使用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];
    

    第四种方式:

    您还可以在其上创建VIEW,然后JOIN

    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];
    

    这些只是一些选择,但希望能让您走上适合自己需要的正确道路。

答案 1 :(得分:2)

正如HoneyBadger所说。在Select和ON条件下使用CASE Both

<强> SQL DEMO

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];

问题是您无法使用别名,因为SELECT

的执行顺序

正如您在此处所见Order Of Execution of the SQL query

JOIN发生在SELECT创建别名

之前

答案 2 :(得分:1)

作为变体,您可以使用辅助表

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'),
...

然后是查询

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

这种方式很有用,因为您不需要为新条件重写查询。

如果这个变体适合你,你可以写得更短

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)

答案 3 :(得分:0)

如果您想避免多次编写表达式,那么唯一的选择是在将表达式设置为别名后加入(并且连接必须位于最外层范围内,因此它被视为表格)。如果问题是整洁,我总是发现使用CTE比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

您可以将子查询分开,为它们提供适当的间距和表别名,并通过在中间创建临时表或变量表来为正在阅读正确查询顺序的人提供,而无需花费资源。

答案 4 :(得分:0)

我不确定您使用full join而不是inner join的原因。但另一种解决方案是使用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可以方便地添加计算列。您不需要子查询或CTE。对查询的更改很少。