如何在T-SQL上的Union子句上获得更好的性能

时间:2019-04-24 12:05:54

标签: sql sql-server tsql query-performance

我有三个桌子。每个表包含超过3M行。我运行以下代码:

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 个答案:

答案 0 :(得分:5)

首先使用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子查询中的各个查询中。

答案 1 :(得分:3)

我建议在每个表上创建一个覆盖索引,类似于:

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 :(得分:2)

我不确定查询计划程序是否足够聪明,可以通过联合外部的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