从表中选择数据,其中列中的值的总和等于另一列中的值

时间:2018-02-20 14:51:35

标签: sql sql-server sql-server-2008

示例数据:

create table #temp (id int, qty int, checkvalue int)
insert into #temp values (1,1,3)
insert into #temp values (2,2,3)
insert into #temp values (3,1,3)
insert into #temp values (4,1,3)

根据上面的数据,我想显示sum(qty) = checkvalue从上到下的确切行数。请注意,所有记录的checkvalue始终相同。关于上面的示例数据,所需的输出是:

Id Qty checkValue
1   1     3
2   2     3

因为1 + 2 = 3并且不再需要显示数据。如果checkvalue为4,我们将显示第三条记录:Id:3 Qty:1 checkValue:4

这是我正在处理此问题的代码。代码运行得很好。

declare @checkValue int = (select top 1 checkvalue from #temp);
declare @counter int = 0, @sumValue int = 0;

while @sumValue < @checkValue
begin
    set @counter = @counter + 1;
    set @sumValue = @sumValue + (
                                    select t.qty from
                                    (
                                        SELECT * FROM (
                                          SELECT
                                            ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
                                            id,qty,checkvalue
                                          FROM #temp
                                        ) AS foo
                                        WHERE rownumber = @counter
                                    ) t
    )
end

declare @sql nvarchar(255) = 'select top '+cast(@counter as varchar(5))+' * from #temp'
EXECUTE sp_executesql @sql, N'@counter int', @counter = @counter;

但是,我不确定这是否是处理它的最佳方法,并想知道是否有更好的方法。这里有很多专业人士,我想听听他们对我的方法的看法以及我们如何改进它。任何建议将不胜感激!

4 个答案:

答案 0 :(得分:1)

对于SQL Server 2012及更高版本,您可以使用C16子句中的ROWS BETWEEN和使用CTE轻松实现此目的:

OVER

答案 1 :(得分:1)

试试这个:

select id, qty, checkvalue from (
    select t1.*,
           sum(t1.qty) over (partition by t2.id) [sum]
from #temp [t1] join #temp [t2] on t1.id <= t2.id
) a where checkvalue = [sum]

智能自我加入就是您所需要的:)

答案 2 :(得分:0)

一个基本的改进是尝试&amp;减少号码。迭代。你递增1,但如果你重新利用二进制搜索背后的逻辑,你就会得到一些接近这个的东西:

19

答案 3 :(得分:0)

对于SQL 2008,您可以使用recursive cte。第一次合并会导致Top 1 with ties限制结果。删除它以查看所有组合

with cte as (
    select 
        *, rn = row_number() over (order by id)
    from 
        #temp
)
, rcte as (
    select
        i = id, id, qty, sumV = qty, checkvalue, rn
    from
        cte
    union all
    select
        a.id, b.id, b.qty, a.sumV + b.qty, a.checkvalue, b.rn
    from
        rcte a
        join cte b on a.rn + 1 = b.rn
    where
        a.sumV < b.checkvalue
)

select
    top 1 with ties id, qty, checkvalue
from (
    select 
        *, needed = max(case when sumV = checkvalue then 1 else 0 end) over (partition by i) 
    from 
        rcte
) t
where
    needed = 1
order by dense_rank() over (order by i)