簡體   English   中英

別名上的JOIN語句 - SQL Server

[英]JOIN statement on an alias - SQL Server

我正在尋找有關使用別名而不是原始數據加入的最佳方式的建議。 例如,數據在加入之前被修改。

一個例子:

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

這正是我要找的。 但是,我正在處理的現實生活中的查詢是巨大的,並且查詢它加入別名會使它變得如此混亂。

任何人都可以就更好的方法給我建議嗎? 或者這是唯一的方法嗎?

好的,首先,您可能知道SELECT語句邏輯處理順序 具體來說,該順序是:

  1. 加入
  2. 哪里
  3. 通過...分組
  4. WITH CUBE或WITH ROLLUP
  5. HAVING
  6. 選擇
  7. 不同
  8. 訂購
  9. 最佳

請注意, SELECT是第8個要處理的內容,即處理列的別名時。 這意味着你不能在步驟9( DISTINCT )之前引用列別名,這實際上意味着你在ORDER BY這樣做了,就是這樣。

因此,如果要引用由表達式派生的列,則幾乎沒有這樣做的方法,我在下面列出了一些方法。

第一種方式:

SELECTON子句中使用Expression。 從而:

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

這些只是幾個選項,但希望能讓您走上適合您自己需求的正確道路。

正如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的執行順序

正如您在此處可以看到SQL查詢的執行順序

JOIN在SELECT創建別名之前發生

作為變體,您可以使用輔助表

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)

如果您想避免多次編寫表達式,那么唯一的選擇是在將表達式指定為別名后加入(並且連接必須位於最外層范圍內,因此它被視為表格)。 如果問題是整潔,我總是發現使用CTE比FROM subquery更具可讀性。

;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

您可以將子查詢分開,為它們提供適當的間距和表別名,並通過在中間創建臨時表或變量表來為正在閱讀正確查詢順序的人提供,而無需花費資源。

我不確定你為什么使用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。 對查詢的更改很少。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM