简体   繁体   中英

Selecting data from table where sum of values in a column equal to the value in another column

Sample data:

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)

According to data above, I would like to show exact number of lines from top to bottom where sum(qty) = checkvalue . Note that checkvalue is same for all the records all the time. Regarding the sample data above, the desired output is:

Id Qty checkValue
1   1     3
2   2     3

Because 1+2=3 and no more data is needed to show. If checkvalue was 4, we would show the third record: Id:3 Qty:1 checkValue:4 as well.

This is the code I am handling this problem. The code is working very well.

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;

However, I am not sure if this is the best way to deal with it and wonder if there is a better approach. There are many professionals here and I'd like to hear from them about what they think about my approach and how we can improve it. Any advice would be appreciated!

For SQL Server 2012, and onwards, you can easily achieve this using ROWS BETWEEN in your OVER clause and the use of a CTE:

WITH Running AS(
    SELECT *,
           SUM(qty) OVER (ORDER BY id
                          ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningQty
    FROM #temp t)
SELECT id, qty, checkvalue
FROM Running
WHERE RunningQty <= checkvalue;

Try this:

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]

Smart self-join is all you need :)

One basic improvement is to try & reduce the no. of iterations. You're incrementing by 1, but if you repurpose the logic behind binary searching, you'd get something close to this:

DECLARE @RoughAverage int = 1 -- Some arbitrary value. The closer it is to the real average, the faster things should be.
DECLARE @CheckValue int = (SELECT TOP 1 checkvalue FROM #temp)
DECLARE @Sum int = 0

WHILE 1 = 1 -- Refer to BREAK below.
BEGIN
    SELECT TOP (@RoughAverage) @Sum = SUM(qty) OVER(ORDER BY id)
    FROM #temp
    ORDER BY id

    IF @Sum = @CheckValue
        BREAK -- Indicating you reached your objective.
    ELSE
        SET @RoughAverage = @CheckValue - @Sum -- Most likely incomplete like this.
END

For SQL 2008 you can use recursive cte . Top 1 with ties limits result with first combination. Remove it to see all combinations

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM