【问题标题】:JOIN statement on an alias - SQL Server别名上的 JOIN 语句 - SQL Server
【发布时间】:2018-04-11 08:32:58
【问题描述】:

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

一个例子:

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

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

任何人都可以就更好的方法给我建议吗?还是这是唯一的方法?

【问题讨论】:

  • 您可以在连接条件本身中使用 case 表达式。如果表很大,性能可能会成为问题,

标签: sql sql-server join alias


【解决方案1】:

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

  1. 来自
  2. 开启
  3. 加入
  4. 在哪里
  5. 分组依据
  6. 使用多维数据集或使用汇总
  7. 选择
  8. 不同
  9. 订购人
  10. 顶部

请注意,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];

这会使事情变得有点混乱,因为它会使查询“忙”。

第二种方式:

使用子选择:

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

这些只是几个选项,但希望这能让您走上适合自己需求的正确道路。

【讨论】:

    【解决方案2】:

    正如蜜獾所说。在 Select 和 ON 条件下都使用 CASE

    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 创建别名之前

    【讨论】:

    • 我唯一担心的是现实生活中的查询有左右连接,所以如果我在连接中遇到问题,我担心如果你找到我可能会丢失一些数据
    • 我添加更多信息。但我不能回答假设。我们需要一些数据来了解问题所在以及如何解决。
    【解决方案3】:

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

    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)
    

    【讨论】:

    • 我认为这是最好的方法;易于维护、易于阅读、易于索引。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      我不确定您为什么使用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。对查询的更改很少。

      【讨论】:

      • 我只是使用完全连接来展示我想要实现的目标
      猜你喜欢
      • 1970-01-01
      • 2014-12-18
      • 2011-07-09
      • 1970-01-01
      • 2012-10-02
      • 2013-12-02
      • 1970-01-01
      • 2016-04-02
      • 1970-01-01
      相关资源
      最近更新 更多