Left join only on first row - sql

I have the following sample query:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON
concat(a.date,a.id)=concat(b.date,b.id)
and I'd like the following output
Row date,id,foo,purchases,budget
1 2020-04-01,test123,abc,10,50
2 2020-04-01,test123,abc,0,null
3 2020-04-01,test123,abc,0,null
I read many questions on the similar topic but I wasn't able to make it work.

You can use row_number():
select a.date, a.id, a.foo, a.purchases,
(case when a.seqnum = 1 then b.budget end) as budget
from (seleect a.*, row_number() over (partition by date, id order by purchases desc) as seqnum
from a
) a
b
using (date, id);

In the b inner query add this to the columns being selected:
ROW_NUMBER() OVER(PARTITION BY Date ORDER BY Purchases DESC) AS SequenceNumber
Then in your LEFT JOIN add "AND b.SequenceNumber = 1"
If you really are hard-coding everything and not selecting from an actual table, it would be:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON a.date = b.date
AND a.id = b.id
AND a.purchases > 0
Otherwise, if you have a table in the inner "b" query. Then it would be something like this:
WITH a As
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,10 as purchases Union all
SELECT '2020-04-01', 'test123', 'abc', 0 Union all
SELECT '2020-04-01', 'test123', 'abc', 0
),
b as
(
SELECT '2020-04-01' as date,'test123' as id,'abc' as foo,50 as budget,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY Id) SequenceNumber
)
select
a.date,a.id,a.foo,a.purchases,budget
from a
LEFT JOIN b
ON a.date = b.date
AND a.id = b.id
AND b.SequenceNumber = 1

Related

How to determine date paid given billing and payment data in BigQuery

I need to know when a bill was paid to determine how early or late it was paid. Unfortunately, I just have billing creation data and payment records.
Example 1
--raw data
WITH bill AS (
SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
SELECT 'p2', DATE('2022-01-11'), 50
)
-- setup
SELECT * FROM bill
I want a query that returns all of the data from the bill table plus the date that the bill was paid, which is theoretically derived from the payment table.
In the example above, the solution could be to sort the bill rows by the due date and apply the payments accordingly:
--raw data
WITH bill AS (
SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
SELECT 'p2', DATE('2022-01-11'), 50
),
--start solution
p AS (
SELECT
payment.id,
payment.made,
payment.amount,
SUM( payment.amount ) OVER (
ORDER BY payment.made
) AS amount_cumulative
FROM payment
),
b AS (
SELECT
bill.id,
bill.created,
bill.due,
bill.amount,
SUM( bill.amount ) OVER (
ORDER BY bill.due
) AS amount_cumulative
FROM bill
),
repayments AS (
SELECT
b.*,
p.made,
ROW_NUMBER() OVER (
PARTITION BY b.id
ORDER BY p.made ASC
) AS seq
FROM b
LEFT JOIN p
ON b.amount_cumulative <= p.amount_cumulative
WHERE p.made IS NOT NULL
)
SELECT
b.*,
repayments.made AS payment_date
FROM b
LEFT JOIN repayments
ON b.id = repayments.id
WHERE (repayments.seq = 1 OR repayments.seq IS NULL)
ORDER BY b.id
Example 2
However, that solution breaks down if we change some of the bill and payment dates (because payments must be applied to the bill with an oustanding balance and the earliest due date at the time of the payment). For example:
--raw data
WITH bill AS (
SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
SELECT 'p1' AS id, DATE('2022-01-02') AS made, 100 AS amount UNION ALL
SELECT 'p2', DATE('2022-01-11'), 50
),
--start solution
p AS (
SELECT
payment.id,
payment.made,
payment.amount,
SUM( payment.amount ) OVER (
ORDER BY payment.made
) AS amount_cumulative
FROM payment
),
b AS (
SELECT
bill.id,
bill.created,
bill.due,
bill.amount,
SUM( bill.amount ) OVER (
ORDER BY bill.due
) AS amount_cumulative
FROM bill
),
repayments AS (
SELECT
b.*,
p.made,
ROW_NUMBER() OVER (
PARTITION BY b.id
ORDER BY p.made ASC
) AS seq
FROM b
LEFT JOIN p
ON b.amount_cumulative <= p.amount_cumulative
WHERE p.made IS NOT NULL
)
SELECT
b.*,
repayments.made AS payment_date
FROM b
LEFT JOIN repayments
ON b.id = repayments.id
WHERE (repayments.seq = 1 OR repayments.seq IS NULL)
ORDER BY b.id
This example is even trickier if p1 is for an amount = 75 (in which case b2 should remain not completely paid, but b3 should be paid in full on 2022-01-11).
Trickiest Example
This is the most contrived, but it best illustrates the problem. Payment 1 goes toward Bill 1 because that is the only available bill at the time of the payment (the payment almost pays the first bill in full). At the time of Payment 2, the bill with the earliest unpaid balance is now Bill 2, so all of the payment goes toward Bill 2 (and again, the payment almost pays the second bill in full). However, at the end of this sequence, 99/100 is paid toward Bill 1 and 49/50 is paid toward Bill 2, but neither Bill 1 nor Bill 2 are paid in full, so the paid in full date should be NULL for both.
--raw data
WITH bill AS (
SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 100 AS amount UNION ALL
SELECT 'b2', DATE('2022-01-02'), DATE('2022-01-14'), 50
),
payment AS (
SELECT 'p1' AS id, DATE('2022-01-10') AS made, 99 AS amount UNION ALL
SELECT 'p2', DATE('2022-01-11'), 49
),
-- setup
SELECT * FROM bill
Question
Can I get the payment_date with a query? If so, what does that look like?
Please refer query below. Idea is to distribute payments over range.
--raw data
WITH bill AS (
SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
), payment AS (
SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
SELECT 'p2', DATE('2022-01-11'), 50
), b as (
-- setup
SELECT
bill.id,
bill.created,
bill.due,
bill.amount,
SUM( bill.amount ) OVER (
ORDER BY bill.created, bill.due
) AS amount_cumulative_created,
SUM( bill.amount ) OVER (
ORDER BY bill.due
) as amount_cumulative_due,
row_number() over (order by bill.due) crn
FROM bill
order by created
), p as (
SELECT
p.id,
p.made,
p.amount,
case when
row_number() over (order by 0) =
max(un) over (partition by p.id) then
floor(p.amount / max(un) over (partition by p.id)) +
mod(p.amount, max(un) over (partition by p.id))
else
floor(p.amount / max(un) over (partition by p.id))
end new_amount,
row_number() over (order by 0) prn
FROM
(select *,
row_number() over (order by payment.made) prn
from payment) p join b
on p.prn = b.crn
,unnest(generate_array(1,
(select coalesce(max(b.crn),0)+1 from b
where p.amount > b.amount_cumulative_due
and p.made between b.created and b.due),
1)) un
), p_distrib as (
select *,
SUM( p.new_amount ) OVER (
ORDER BY p.prn
) AS payment_cumulative, from p
)
select b.*,
(select min(pd.made) from p_distrib pd
where
--- Adjust here for what date takes preference - created vs. due.
--- Replace amount_cumulative_created with amount_cumulative_due as needed
b.amount_cumulative_created <= pd.payment_cumulative
and pd.made between b.created and b.due)
from b;

Count cummulative distinct

I have the below table. Is it possible to do a cummulative distinct count? For example, if A1 has 3 distinct values, then the count for it will be 3. Afterwards, check for A1 and A2. If A1 and A2 together have 5 distinct values, 5. Repeat until A1 + A2 ... + An and count the distinct values.
A
V
A1
V1
A1
V2
A1
V2
A2
V1
A2
V2
A2
V3
My expected output would be:
A
C
A1
2
A2
3
This answers the original version of the question.
You can aggregate twice . . . once to keep the first occurrence of v and the second to aggregate again:
select a, count(*) as new_cs
from (select v, min(a) as a
from t
group by v
) v
group by a;
Note: The above only shows as that have new values. If you want all a, then window functions are a better approach:
select a, sum(case when seqnum = 1 then 1 else 0 end) as c
from (select t.*, row_number() over (partition by v order by a) as seqnum
from t
) t
group by a
order by a;
Here is a db<>fiddle.
You can use ROW_NUMBER() window function to find the 1st occurrence of each V and then COUNT() window function to count only these 1st occurrences:
SELECT DISTINCT A,
COUNT(CASE WHEN rn = 1 THEN 1 END) OVER (ORDER BY A) C
FROM (
SELECT A, ROW_NUMBER() OVER (PARTITION BY V ORDER BY A) rn
FROM tablename
) t
ORDER BY A
See the demo.
You can use a partitioned outer join to ensure that all V values are counted for all A values and then use the FIRST_VALUE analytic function to find whether a value exists in the current or preceding A values for the V:
SELECT a,
COUNT( DISTINCT fv ) AS c
FROM (
SELECT t.a,
FIRST_VALUE(t.v) IGNORE NULLS OVER (
PARTITION BY v.v
ORDER BY t.a
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS fv
FROM ( SELECT DISTINCT v FROM table_name ) v
LEFT OUTER JOIN table_name t
PARTITION BY ( t.a )
ON ( t.v = v.v )
)
GROUP BY a
ORDER BY a
Which, for the sample data:
CREATE TABLE table_name ( A, V ) AS
SELECT 'A1', 'V1' FROM DUAL UNION ALL
SELECT 'A1', 'V2' FROM DUAL UNION ALL
SELECT 'A1', 'V3' FROM DUAL UNION ALL
SELECT 'A2', 'V1' FROM DUAL UNION ALL
SELECT 'A2', 'V3' FROM DUAL UNION ALL
SELECT 'A2', 'V4' FROM DUAL UNION ALL
SELECT 'A3', 'V2' FROM DUAL UNION ALL
SELECT 'A3', 'V3' FROM DUAL UNION ALL
SELECT 'A4', 'V1' FROM DUAL UNION ALL
SELECT 'A4', 'V5' FROM DUAL;
Outputs:
A
C
A1
3
A2
4
A3
4
A4
5
db<>fiddle here

how use column from one subquery to anoter subquery

I have two subquery .i want put p.price from first subquery into secound subquery(place XXX) . but get error => ORA-00904: "P"."PRICE": invalid identifier
select
p.product_id,
p.price,
l.delegation,
l.state
from users
inner join (
select
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p on users.serial = p.serial
inner join (
select
sso_id ,
delegation,
state,
updated_at,
row_number() over (partition by state order by updated_at asc) as tl
from payments
where state = 'green' or (state = 'yellow' and delegation > XXX)
) l on users.sso_id= l.sso_id
where
p.rn = 1
and l.tl = 1
Join on the PAYMENTS table and filter the rows to exclude the invalid state/delegation rows and then generate the ROW_NUMBER and filter to find the first row per partition:
Oracle Setup:
CREATE TABLE users ( serial, sso_id ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
CREATE TABLE prices ( serial, start_date, price, product_id ) AS
SELECT 1, DATE '2019-01-01', 20, 1 FROM DUAL UNION ALL
SELECT 1, DATE '2019-02-01', 30, 2 FROM DUAL UNION ALL
SELECT 1, DATE '2019-03-01', 25, 3 FROM DUAL UNION ALL
SELECT 2, DATE '2019-01-01', 20, 1 FROM DUAL UNION ALL
SELECT 2, DATE '2019-02-01', 25, 2 FROM DUAL UNION ALL
SELECT 2, DATE '2019-03-01', 30, 3 FROM DUAL UNION ALL
SELECT 3, DATE '2019-01-01', 40, 3 FROM DUAL;
CREATE TABLE payments ( sso_id, delegation, state, updated_at ) AS
SELECT 1, 20, 'green', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 1, 30, 'green', DATE '2019-02-01' FROM DUAL UNION ALL
SELECT 1, 27, 'green', DATE '2019-03-01' FROM DUAL UNION ALL
SELECT 1, 22, 'green', DATE '2019-04-01' FROM DUAL UNION ALL
SELECT 1, 26, 'yellow', DATE '2019-05-01' FROM DUAL UNION ALL
SELECT 2, 31, 'yellow', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 2, 31, 'green', DATE '2019-02-01' FROM DUAL UNION ALL
SELECT 3, 30, 'green', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 3, 30, 'yellow', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 3, 50, 'yellow', DATE '2019-02-01' FROM DUAL;
Query:
SELECT product_id,
price,
delegation,
state
FROM (
select p.product_id,
p.price,
l.delegation,
l.state,
row_number() over ( partition by l.sso_id, l.state order by l.updated_at asc) as tl
from users
inner join (
select serial,
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p
on ( users.serial = p.serial AND p.rn = 1 )
inner join payments l
on ( users.sso_id = l.sso_id AND ( l.state = 'green' or (l.state = 'yellow' and l.delegation > p.price ) ) )
)
where tl = 1
Output:
PRODUCT_ID | PRICE | DELEGATION | STATE
---------: | ----: | ---------: | :-----
3 | 25 | 20 | green
3 | 25 | 26 | yellow
3 | 30 | 31 | green
3 | 30 | 31 | yellow
3 | 40 | 30 | green
3 | 40 | 50 | yellow
db<>fiddle here
Use it in the main WHERE clause
select
p.product_id,
p.price,
l.delegation,
l.state
from users
inner join (
select
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p on users.serial = p.serial
inner join (
select
sso_id ,
delegation,
state,
updated_at,
row_number() over (partition by state order by updated_at asc) as tl
from payments
where state = 'green' or state = 'yellow'
) l on users.sso_id= l.sso_id
where
p.rn = 1
and l.tl = 1
-- add following condition
and l.delegation > p.price
We need to rejoin your users and price table to your payment to get matching p.price
SELECT p.product_id,
p.price,
l.delegation,
l.state
FROM users
INNER JOIN
(SELECT start_date,
price,
product_id,
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY start_date DESC) AS rn
FROM prices) p ON users.serial = p.serial
INNER JOIN
(SELECT y.sso_id,
y.delegation,
y.state,
y.updated_at,
ROW_NUMBER () OVER (PARTITION BY y.state ORDER BY y.updated_at ASC) AS tl
FROM payments y
INNER JOIN users u ON u.sso_id = y.sso_id
INNER JOIN prices p ON p.serial = y.serial
WHERE tl.state = 'green' OR (tl.state = 'yellow' AND tl.delegation > p.price)) l ON users.sso_id = l.sso_id
WHERE
AND p.rn = 1 AND l.tl = 1

join tables on different amount of columns

I wanna replace values in the column Attribute in T1 with the values from T2. If sub_id is NULL in T2, then replace f values in T1 with t for all the rows with ID in T2, otherwise do it only for the rows with sub_id.
Below are the dummy tables and the result I get.
WITH T1 AS ( SELECT 1 AS id, 2 as sub_id, '2017-08-20' AS date, 'f' AS attribute UNION ALL
SELECT 1, 3 as sub_id, '2017-08-19', 'f' UNION ALL
SELECT 1, 4 as sub_id,'2017-08-18', 'f' UNION ALL
SELECT 2, 5 as sub_id,'2017-08-20', 'f' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', 'f'
),
T2 AS (
SELECT 1 AS id, null as sub_id, '2017-08-19' AS date, '2017-08-25' AS date_end, 't' AS attribute UNION ALL
SELECT 2, 5 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't'
)
SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end
AND a.sub_id = case when b.sub_id is not null then a.sub_id end
In the above result I wanna have the attribute column with t values in all the eligible rows.
I achieved what I want with another subquery:
WITH T1 AS ( SELECT 1 AS id, 2 as sub_id, '2017-08-20' AS date, 'f' AS attribute UNION ALL
SELECT 1, 3 as sub_id, '2017-08-19', 'f' UNION ALL
SELECT 1, 4 as sub_id,'2017-08-18', 'f' UNION ALL
SELECT 2, 5 as sub_id,'2017-08-20', 'f' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', 'f'
),
T2 AS (
SELECT 1 AS id, null as sub_id, '2017-08-19' AS date, '2017-08-25' AS date_end, 't' AS attribute UNION ALL
SELECT 2, 5 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't' UNION ALL
SELECT 2, 6 as sub_id,'2017-08-19', '2017-08-25' AS date_end, 't'
) ,
adv_level AS ( SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM T1 AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end AND a.sub_id = case when b.sub_id is not null then a.sub_id end)
SELECT
a.id,
a.date,
a.sub_id,
COALESCE(b.attribute, a.attribute) as attribute
FROM adv_level AS a
LEFT JOIN T2 AS b
ON a.id = b.id AND a.date >= b.date AND a.date <= b.date_end
but is there a way to do it just once?

How to get running sum of a column in sql server

Hi I have a column with name Qty from table Bills
i want a column that show the running sum of Qty column like this :
Qty Run_Sum
1 1
2 3
3 6
4 10
5 15
Suggest me some appropriate method to make running some thankx
if you RDBMS supports window function,
for SQL Server 2012
SELECT Qty,
SUM(Qty) OVER (ORDER BY Qty) AS CumulativeTOTAL
FROM tableName
SQLFiddle Demo
for SQL Server 2008
SELECT a.Qty, (SELECT SUM(b.Qty)
FROM TableName b
WHERE b.Qty <= a.Qty)
FROM TableName a
ORDER BY a.Qty;
SQLFiddle Demo
SQLFiddle demo
SELECT Qty,
SUM(Qty) OVER (ORDER BY Qty) Run_Sum
FROM t ORDER BY Qty
For SQLServer prior to 2012:
select Qty,
(select sum(Qty) from t where Qty<=t1.Qty)
from t t1 order by Qty
SQLFiddle demo
Or also you can do it without subquery:
select t1.Qty, sum(t2.Qty)
from t t1
join t t2 on (t1.Qty>=t2.Qty)
group by t1.Qty
order by t1.Qty
SQLFiddle demo
Here's a sample using Oracle/analytical functions:
select id, qty, sum(qty) over(order by id asc) run_sum
from test;
http://www.sqlfiddle.com/#!4/3d149/1
Check this
DECLARE #TEMP table
(
ID int IDENTITY(1,1),
QUANTITY int
)
INSERT INTO #TEMP
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 8 UNION ALL
SELECT 7 UNION ALL
SELECT 5 UNION ALL
SELECT 1
SELECT t.QUANTITY AS Qty, SUM(t1.QUANTITY) AS Run_Sum
FROM #TEMP t
INNER JOIN #TEMP t1
ON t1.ID <= t.ID
GROUP BY t.ID, t.QUANTITY
ORDER BY t.ID
;with cte as (
select top 1 Qty, Qty as RunningSum
from Bills
order by Qty
union all
select t.Qty, cte.RunningSum + t.Qty
from cte
inner join Bills t on cte.Qty + 1 = t.Qty
)
select * from cte
#mahmud:
See what this gives
DECLARE #Bills table
(
QUANTITY int
)
INSERT INTO #Bills
SELECT 2 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 1 UNION ALL
SELECT 3 UNION ALL
SELECT -5 UNION ALL
SELECT 5 UNION ALL
select 1
;with cte as (
select top 1 QUANTITY, QUANTITY as RunningSum
from #Bills
order by QUANTITY
union all
select t.QUANTITY, cte.RunningSum + t.QUANTITY
from cte
inner join #Bills t on cte.QUANTITY + 1 = t.QUANTITY
)
select * from cte