Как я могу получить лучшую производительность по выражению union на t-sql - PullRequest
3 голосов
/ 24 апреля 2019

У меня есть три таблицы. Каждая таблица содержит более 3 миллионов строк. Я запускаю следующий код:

SELECT * FROM 
(
    SELECT col_1, col_2, col_3, [date], 1 as type FROM table_1
    UNION
    SELECT col_1, col_2, col_3, [date], 2 as type FROM table_2 
    UNION
    SELECT col_1, col_2, col_3, [date], 3 as type FROM table_3
) AS tb 
tb.[date] BETWEEN (start_date) AND (end_date)  
ORDER BY [date] DESC OFFSET n ROWS FETCH NEXT m ROWS ONLY

Но когда я получаю большой интервал дат, запрос выполняется медленнее. Например: когда я получаю интервал 2019-01-01 и 2019-04-01, запрос выполняется около 13-14 секунд:

Execution plan

Этот результат очень плохой. Я хочу получить результат за 1 секунду. Что я могу сделать?

Ответы [ 3 ]

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

Начните с использования UNION ALL вместо UNION:

SELECT *
FROM (SELECT col_1, col_2, col_3, [date], 1 as type FROM table_1
      UNION ALL
      SELECT col_1, col_2, col_3, [date], 2 as type FROM table_2 
      UNION ALL
      SELECT col_1, col_2, col_3, [date], 3 as type FROM table_3
     ) AS tb 
WHERE tb.[date] BETWEEN (start_date) AND (end_date)  
ORDER BY [date] DESC
OFFSET n ROWS FETCH NEXT m ROWS ONLY;

SQL несет накладные расходы на удаление дубликатов с UNION. UNION ALL не несет эти накладные расходы.

Кроме того, индекс date в каждой из таблиц должен помочь. В SQL Server есть хороший оптимизатор, который обычно распространяет такие условия на отдельные запросы в подзапросе UNION / UNION ALL.

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

Я бы предложил создать индекс покрытия для каждой таблицы, подобный:

CREATE INDEX ix1 ON table_1 (date) INCLUDE (column1, column2, column3)

Это должно помочь с предложением WHERE. Кроме того, SQL Server не придется прикасаться к таблицам, поскольку вся необходимая информация присутствует в индексе.


Вот еще один удар по этому. Предполагая, что OFFSET n ROWS FETCH NEXT m ROWS ONLY соответствует очень небольшому проценту строк между начальной и конечной датой, напишите запрос, такой как:

WITH cte1 AS (
    -- find the first date after n + m window
    SELECT date
    FROM (
        SELECT date FROM table_1 UNION ALL
        SELECT date FROM table_2 UNION ALL
        SELECT date FROM table_3
    ) AS x
    WHERE date BETWEEN '2019-01-01' AND '2019-04-01'
    ORDER BY date DESC OFFSET (n + m) ROWS FETCH NEXT 1 ROW ONLY
), cte2 AS (
    SELECT date, column_1, column_2, column_3, 1 AS type FROM table_1 UNION ALL
    SELECT date, column_1, column_2, column_3, 1 AS type FROM table_2 UNION ALL
    SELECT date, column_1, column_2, column_3, 1 AS type FROM table_3
)
SELECT *
FROM cte2
WHERE date <= '2019-04-01' AND date > (SELECT date FROM cte1)
ORDER BY date DESC OFFSET n ROWS FETCH NEXT m ROWS ONLY
2 голосов
/ 24 апреля 2019

Я не уверен, что планировщик запросов достаточно умен, чтобы ограничить результаты объединения выражением where за пределами объединения, поэтому попробуйте переместить условие даты в каждый из запросов объединения, чтобыне объединяйте вместе все три таблицы перед выполнением условия:

SELECT * FROM 
(
    SELECT col_1, col_2, col_3, [date], 1 as type FROM table_1 where table_1.[date] between (start_date) and (end_date)
    UNION
    SELECT col_1, col_2, col_3, [date], 2 as type FROM table_2 where table_2.[date] between (start_date) and (end_date) 
    UNION
    SELECT col_1, col_2, col_3, [date], 3 as type FROM table_3 where table_3.[date] between (start_date) and (end_date)
) AS tb 
ORDER BY [date] DESC OFFSET n ROWS FETCH NEXT m ROWS ONLY
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...