Как мне оптимизировать этот запрос UNION? - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть эта схема тестирования:

CREATE TABLE A
(
  ID INT PRIMARY KEY,
  A VARCHAR(1),
  B VARCHAR(3),
  C VARCHAR(3),
  BID INT,
  DATE DATETIME2(7)
)

INSERT INTO A values
(1, NULL, 'AAA', 'XXX', 1, '2018-04-04'),
(2, NULL, 'BBB', 'YYY', 2, '2018-05-05'),
(3, NULL, 'CCC', 'ZZZ', 2, '2018-06-06')

CREATE TABLE B
(
  ID INT PRIMARY KEY,
  X VARCHAR(1),
  Y VARCHAR(1),
  Z VARCHAR(1)
)

INSERT INTO B values
(1, 'A', 'A', 'A'),
(2, 'B', 'B', 'B'),
(3, 'C', 'C', 'C'),
(4, 'D', 'D', 'D'),
(5, 'E', 'E', 'E')

CREATE TABLE C
(
  ID INT PRIMARY KEY,
  NAME VARCHAR(12)
)

INSERT INTO C values
(1, 'Mary'),
(2, 'John'),
(3, 'Peter')

CREATE TABLE D
(
  ID INT PRIMARY KEY,
  CID INT,
  R VARCHAR(3),
  S VARCHAR(3)
)

INSERT INTO D values
(1, 1, 'AAA', 'ABC'),
(2, 2, 'BBB', 'ZZZ'),
(3, 1, 'FOO', 'ZZZ'),
(4, 3, 'BAR', 'YYY')

И у меня есть этот запрос:

WITH T1 AS
(
    SELECT A.ID, A.A, A.B, A.C, B.X, B.Y, B.Z
    FROM A
    LEFT JOIN B
    ON A.BID = B.ID
    WHERE A.DATE BETWEEN '2018-01-01' AND '2018-12-31'
)

(
    SELECT T1.ID, T1.A, T1.B AS VALUE, T1.X, T1.Y, T1.Z, T2.NAME
    FROM T1
    LEFT JOIN
    (
        SELECT C.NAME, D.R
        FROM C
        INNER JOIN D
        ON C.ID = D.CID
    ) AS T2
    ON T1.B = T2.R
)
UNION ALL
(
    SELECT T1.ID, T1.A, T1.C AS VALUE, T1.X, T1.Y, T1.Z, T2.NAME
    FROM T1
    LEFT JOIN
    (
        SELECT C.NAME, D.S
        FROM C
        INNER JOIN D
        ON C.ID = D.CID
    ) AS T2
    ON T1.C = T2.S
)

Скрипка здесь .

План выполнения можно найти в скрипке.

Глядя на текущий план выполнения, есть два дерева, ответвляющиеся от объединения. Оба дерева практически идентичны. Это означает, что запрос внутри T1 был выполнен дважды из-за UNION ALL. Можно ли как-нибудь переписать это, возможно, заменив UNION ALL на JOIN?

P.S. Отредактированный вопрос со схемой теста и скрипкой.

Ответы [ 2 ]

1 голос
/ 24 апреля 2019

Я получил лучший план, используя следующее:

;WITH T1 AS
(
    SELECT A.ID, A.A, A.B, A.C, B.X, B.Y, B.Z
    FROM A
    LEFT JOIN B ON A.BID = B.ID
    WHERE A.DATE BETWEEN '2018-01-01' AND '2018-12-31'
)
SELECT T1.ID, T1.A, IIF(v.N=1,T1.B,T1.C) AS VALUE, T1.X, T1.Y, T1.Z, C.NAME
FROM T1
CROSS JOIN (VALUES (1),(2)) v(N) -- or (SELECT 1 N UNION ALL SELECT 2) v
LEFT JOIN D ON ((T1.B = D.R AND v.N=1) OR (T1.C = D.S AND v.N=2))
LEFT JOIN C ON C.ID = D.CID

Вы также можете переписать его без CTE (тот же план, но выглядит короче):

SELECT A.ID, A.A, IIF(v.N=1,A.B,A.C) AS VALUE, B.X, B.Y, B.Z, C.NAME
FROM A
LEFT JOIN B ON A.BID = B.ID
CROSS JOIN (VALUES (1),(2)) v(N) -- or (SELECT 1 N UNION ALL SELECT 2) v
LEFT JOIN D ON ((A.B = D.R AND v.N=1) OR (A.C = D.S AND v.N=2))
LEFT JOIN C ON C.ID = D.CID
WHERE A.DATE BETWEEN '2018-01-01' AND '2018-12-31'
0 голосов
/ 24 апреля 2019
WITH T1 AS
(
    SELECT A.ID, A.A, A.B, A.C, B.X, B.Y, B.Z
    FROM A
    LEFT JOIN B
    ON A.BID = B.ID
    WHERE A.DATE BETWEEN '2018-01-01' AND '2018-12-31'
)

(
    SELECT T1.ID, T1.A, T1.B AS VALUE, T1.X, T1.Y, T1.Z, C.NAME
    FROM T1
    Left JOIN D ON 
    T1.C = D.R
    LEFT JOIN C ON 
    C.ID = D.CID

)
UNION ALL
(
    SELECT T1.ID, T1.A, T1.C AS VALUE, T1.X, T1.Y, T1.Z, C.NAME  
    FROM T1
    Left JOIN D ON 
    T1.C = D.S
    LEFT JOIN C ON 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...