Инструкция JOIN для псевдонима-SQL Server

Я ищу совет о лучшем способе присоединиться, используя псевдоним вместо исходных данных. e.g данные изменяются до их объединения.

пример:

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 ответов


хорошо, во-первых, это, вероятно, хорошо для вас, чтобы быть в курсе порядок логической обработки инструкции SELECT. В частности, этот порядок:

  1. С
  2. ON
  3. вступить
  4. здесь
  5. ГРУППЫ ПО
  6. С куба или с Rollup
  7. С
  8. выберите
  9. DISTINCT
  10. заказать BY
  11. TOP

заметил, что SELECT - это 8-я вещь, которая должна быть обработана, когда будет обработан псевдоним столбца. Это означает, что вы не можете ссылаться на псевдоним столбца до шага 9 (DISTINCT), что действительно означает, что вы оставили это в ORDER BY и это все.

таким образом, если вы хотите ссылаться на столбец, производный от выражения, у вас есть несколько способов сделать это, некоторые из которых я перечислил ниже.

1-й путь:

используйте выражение в SELECT и ON предложения. Таким образом:

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

это может сделать вещи немного запутанными, так как это может сделать запрос "занятым".

2-й способ:

использовать подвыборки:

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

3-й путь

это в основном то же самое, что и последний вариант, однако, используя 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];

4-й Путь:

вы также можете создать 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, так и при условии

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-запроса

соединение происходит перед выбором создать псевдоним


в качестве варианта вы можете использовать вспомогательную таблицу

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)

Если вы хотите избежать записи выражений несколько раз, то единственным вариантом является присоединение после asigning выражения к псевдониму (и соединение должно быть в самой внешней области, поэтому оно рассматривается как таблица). Если проблема заключается в аккуратности, я всегда нахожу использование CTEs намного более читаемым, чем subquerying в 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

вы можете держать свои подзапросы разделенными, давать им правильный интервал и псевдоним таблицы и давать человеку, который читает правильный порядок запросов, не тратя ресурсы, создавая временные или переменные таблицы в середине.


Я не уверен, почему вы используете 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 может быть удобно для добавления в вычисляемые столбцы. Вам не нужны подзапросы или CTEs. Изменения в запросе минимальны.