13

我还需要有关分页和UNION ALL用于多个表的帮助:

UNION ALL在使用并仅返回特定行数来连接多个表时,如何实现优化的分页...


declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20

set rowcount @PageCount

select Row_Number() OVER(Order by col1) as RowNumber, col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where RowNumber > @startRow

表 3、4 和 5 有大量行(数百万行),而表 1 和 2 可能只有几千行。

如果 startRow 为“0”,我只期望第 1 行到第 20 行的数据(来自表 1)。我得到了正确的结果,但是在 sql server 尝试所有数据并对其进行过滤时,剩余表的开销很高。

@dateFrom 和 @dateTo 的间隔越长,在尝试从整个结果集中仅检索几行时,我的查询就会显着变慢

请帮助我如何用类似的逻辑实现一个简单但更好的方法。:(

4

5 回答 5

3

考虑使用 OFFSET FETCH 子句(从 MSSQL 2012 开始工作):

declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20


select col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
order by col1
offset @startRow rows
fetch next @PageCount rows only

我还想在这里提一下,为什么这个查询总是需要 O(n*log(n)) 时间。
要执行此查询,数据库需要:

  1. 将多个列表合并为一个列表 - 每个表需要 O(n) 时间,其中 n - 表中的总行数;
  2. 按 col1 排序列表 - 采用 O(n*log(n)),其中 n - 是总行数
  3. 按排序顺序遍历列表,跳过@startRow 行,获取下一个@PageCount 行。

如您所见,您需要对所有数据进行联合和排序以获得预期的结果(数字 3)。

如果此查询的性能仍然很差并且您想增加,请尝试:

  • 在所有表中创建基于 col1 的聚集索引
  • 在所有表中创建基于 col1 的非集群索引,并**包括要在选择列表中输出的所有其他列**。
于 2016-10-01T21:29:50.023 回答
1

您的数据库设计可能存在问题,因为您有 5 个类似的表。但除此之外,您可以将您的 UNION ALL 查询具体化为一个永久表或一个带有适当索引的临时#-表,最后使用 ROW_NUMBER() 子句对具体化数据集进行分页。

于 2017-05-24T10:15:58.653 回答
0

与其应用基于经典OFFSET的分页(请注意,SQL Server 2012 现在原生支持它),我认为您的特定用例可以从本文中描述的通常称为“seek 方法”的方法中受益匪浅。您的查询将如下所示。

select top 20 col1, col2
from
(
    select col1, col2 from t1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where (col1 > @lastValueForCol1)
   or (col1 = @lastValueForCol1 and col2 > @lastValueForCol2)
order by col1, col2

@lastValueForCol1@lastValueForCol2值是上一页最后一条记录的相应值。这允许您获取“下一页”。如果ORDER BY方向是DESC,只需使用即可<。如果(col1, col2)不是全局唯一的tmpTable,您可能需要在查询和WHEREandORDER BY子句中添加另一列,以避免在页面之间丢失记录。

使用上述方法,您不能在没有先获取之前的 40 条记录的情况下立即跳转到第 3 页。但通常,无论如何你都不想跳那么远。相反,您将获得更快的查询,该查询可能能够在恒定时间内获取数据,具体取决于您的索引。另外,您的页面保持“稳定”,无论基础数据是否发生变化(例如,在第 1 页,而您在第 4 页)。

注意,“seek 方法”也称为keyset paging

索引

虽然使用“seek 方法”进行分页总是比使用时快OFFSET,但您仍应确保(col1, col2)在每个表中都对其进行了索引!

于 2013-10-27T11:39:17.750 回答
0
select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)

本质上与普通表一样有效,前提是您用于分页的排序键上有一个索引。这通常会导致所有表都合并连接的查询计划。合并连接是一种流式操作。它的成本与绘制的行数成正比,而不是与表中的行数成正比。

SQL Server 中的按行号分页始终通过枚举行从头到尾进行,直到到达所需的窗口。行是从一个表中绘制还是从多个合并表中绘制并没有根本的区别。

因此,加快速度的一个好机会是创建一个覆盖索引 keyed on col1。不幸的是,不可能同时索引between (@dateFrom and @dateTo)。因此,您必须尝试两种索引策略并选择最有效的方法。

于 2016-09-27T12:56:53.750 回答
-1

由于表在分页的结果集中排序(Union ALL 不排序),因此没有理由从所有 5 个表中进行选择。您应该将代码更改为:

  • 从表 1 中查询。看看你是否有足够的记录。
  • 如果不是从表 2 中查询,依此类推。

根据每次查询的记录数管理偏移计数。这样,您只查询您需要的表。

您甚至可以通过根据过滤器选择表中的记录数来进行优化,以了解是否需要从中查询任何数据。所以如果你想要 30-50 条记录,而 table1 里面只有 20 条匹配的记录,你可以完全跳过它。

于 2015-08-20T07:44:29.733 回答