SQL Server 顶部与顶部和底部的关系

Posted

技术标签:

【中文标题】SQL Server 顶部与顶部和底部的关系【英文标题】:SQL Server top with ties for both top and bottom 【发布时间】:2013-01-20 02:50:54 【问题描述】:

我正在寻找一种更有效的方法来在 sql server 2008 中实现分页。

我需要检索结果集行@from 到@to,按分值排序,但我还需要检索@from 和@to 之前和之后与那些特定@from 和@ 的分值匹配的所有行到行。例如,这可能类似于以下两个查询之一:

注意,分数值不是唯一的,没有索引,也不会以预先排序的形式提供给查询的其余部分。

a) (实际上,我认为第一个示例不能保证给出我需要的结果,因为我不认为 2 个子查询中的 order by 可以保证产生相同的排序。但是为了了解我希望查询语法做什么,仅考虑此示例中的子查询中的 order-by 是相同的)

select *  
from (  
    select top (@to) * with ties  
    from result_set  
    order by score desc  
)  tt  
union  
select  
from (  
    select top (@to - @from + 1) * with ties  
    from (  
        select top (@to) *  
        result_set
        order by score desc  
    ) tt  
    order by score asc  
)  tt
order by score desc

b)

/*EDIT: nested the rank() window function and related where criteria one level deeper than
the count(*), as this gave me significant (25%) decrease in runtime with my data, and just  
kinda makes sense.*/  

select score  
from (  
    select  
        score,  
        myrank,  
        count(*) over (partition by score) frequency  
    from (
        select  
            score,  
            rank() over (order by score desc) myrank
        from result_set  
    ) tt
    where myrank <= @to
) tt  
where @from <= myrank + frequency - 1  
order by score desc  

我使用语法 (b) 结合以下测试 CTE 获得了我需要的结果:

with result_set (score) as (
    select 0.5 union all
    select 0.5 union all
    select 0.2 union all
    select 0.1 union all
    select 0.55 union all
    select 0.5 union all
    select 0.1
)

但分区窗口函数需要 2 个嵌套循环和 3 个惰性假脱机运算符。有没有更有效的语法?

这是当前的实际执行计划:

【问题讨论】:

我的目标是看看是否有什么可以做的,它不假设对分数值进行任何索引,对分数值进行任何预排序,或者使用来自先前执行的查询,例如我不知道上次运行查询时的最低分值是多少。 理想情况下,窗口函数查询的执行似乎不一定需要假脱机和连接。我想在 SQL Server 2012 中可能会有一个更新的窗口函数语法,它可能只是做同样的事情,比我的 2008 做的要好一点。但我想使用 2008 年开发人员版或企业版中的现有内容。 我没有详细查看您的问题,但这是您总是得到的窗口聚合的计划形状。只有一个线轴在计划中出现了 3 次。有关Common Subexpression Spool 的更多详细信息,请点击此处 【参考方案1】:

你可以先得到最低值。然后使用顶部关系进行常规分页,但包括等于底部值的值:

DECLARE @pageSize int = @to - @from + 1

DECLARE @bottomValue <datatype> = 
(
     SELECT MIN(score)
     FROM 
     (
          SELECT TOP(@from) score
          FROM <TABLENAME>
          ORDER BY score DESC
     )
)

SET @from = @from - 1

SELECT TOP (@pageSize) * WITH TIES --TopTies
FROM <TABLENAME>
WHERE <COLUMN_ID> NOT IN
                (
                                SELECT TOP (@from) <COLUMN_ID>
                                FROM <TABLENAME>
                                ORDER BY score DESC
                )
ORDER BY score DESC

UNION

SELECT *
FROM <TABLENAME>
WHERE score = @bottomValue --BottomTies

使用此查询的好处是,对于第一页,您可以获得更快的响应时间(更少的读取)。缺点是随着@from 的增加,读取也会增加。但是,我认为,如果您使用 row_number 或 rank 之类的函数,则无论是第一页还是最后一页,都必须评估所有表数据。

另一种方式,不确定是否最适合您的情况,将 lastvalue 发送到 sp:

--Parameter
@lastScore <datatype> = null


--Logic

SELECT TOP(@to) * WITH TIES
FROM [TABLENAME]
WHERE score < @lastScore OR @lastScore IS NULL -- opt1
--WHERE score <= @lastScore OR @lastScore IS NULL opt2
ORDER BY score DESC

【讨论】:

您好,感谢您的建议。不幸的是,该代码没有返回正确的结果。例如,带有窗口函数的给定查询,当与 (@from, @to) = (3, 6) 一起使用时,以及与示例 result_set 数据一起使用时,返回所有三行,分数 = 0.5,而建议的查询仅返回 2这些行。 我喜欢关于窗口函数与 order by 的批判性思维。对于当前情况,result_set 数据没有预先排序,因此 order by 也需要评估整个集合。我确实尝试了一个测试,但是使用 row_number 和一个带有预排序数据的临时表,并且根据实际的执行计划,这两种方法似乎都从临时表中访问了相同数量的 actual 行,带有窗口-查询产生更大的估计行数。 使用@lastScore 值是个好主意,但目前我还不能这样做。 嗨@JMHicks,是的,我没有记住 top 会限制结果集包含所有值。我编辑了答案,添加了一个 UNION 而不是 where 子句。看看吧!关于未预排序的数据集,我更多的是考虑索引列。

以上是关于SQL Server 顶部与顶部和底部的关系的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 减号查询。如果顶部 SQL 和底部 SQL 不包含 NULL,我如何获得带有 NULLS 的结果?

与顶部布局指南和底部布局指南的距离相等

实现移动端顶部与底部固定,内容区优化的效果

在 Redshift sql 查询中选择表的顶部/底部 50%

与文件上传器和顶部对齐的标签一起使用时,如何对齐底部的按钮?

将两个元素与表格单元格的顶部和底部对齐