Cumulative sum of a column - sql

I have a table that has the below data.
COUNTRY LEVEL NUM_OF_DUPLICATES
US 9 6
US 8 24
US 7 12
US 6 20
US 5 39
US 4 81
US 3 80
US 2 430
US 1 178
US 0 430
I wrote a query that will calculate the sum of cumulative rows and got the below output .
COUNTRY LEVEL NUM_OF_DUPLICATES POOL
US 9 6 6
US 8 24 30
US 7 12 42
US 6 20 62
US 5 39 101
US 4 81 182
US 3 80 262
US 2 130 392
US 1 178 570
US 0 254 824
Now I want to to filter the data and take only where the POOL <=300, if the POOL field does not have the value 300 then I should take the first value after 300. So, in the above example we do not have the value 300 in the field POOL, so we take the next immediate value after 300 which is 392. So I need a query so that I can pull the records POOL <= 392(as per the example above) which will yield me the output as
COUNTRY LEVEL NUM_OF_DUPLICATES POOL
US 9 6 6
US 8 24 30
US 7 12 42
US 6 20 62
US 5 39 101
US 4 81 182
US 3 80 262
US 2 130 392
Please let me know your thoughts. Thanks in advance.

declare #t table(Country varchar(5), Level int, Num_of_Duplicates int)
insert into #t(Country, Level, Num_of_Duplicates)
values
('US', 9, 6),
('US', 8, 24),
('US', 7, 12),
('US', 6, 20),
('US', 5, 39),
('US', 4, 81),
('US', 3, 80),
('US', 2, 130/*-92*/),
('US', 1, 178),
('US', 0, 430);
select *, sum(Num_of_Duplicates) over(partition by country order by Level desc),
(sum(Num_of_Duplicates) over(partition by country order by Level desc)-Num_of_Duplicates) / 300 as flag,--any row which starts before 300 will have flag=0
--or
case when sum(Num_of_Duplicates) over(partition by country order by Level desc)-Num_of_Duplicates < 300 then 1 else 0 end as startsbefore300
from #t;
select *
from
(
select *, sum(Num_of_Duplicates) over(partition by country order by Level desc) as Pool
from #t
) as t
where Pool - Num_of_Duplicates < 300 ;

The logic here is quite simple:
Calculate the running sum POOL value up to the current row.
Filter rows so that the previous row's total is < 300, you can either subtract the current row's value, or use a second sum
If the total up to the current row is exactly 300, the previous row will be less, so this row will be included
If the current row's total is more than 300, but the previous row is less then it will also be included
All higher rows are excluded
It's unclear what ordering you want. I've used NUM_OF_DUPLICATES column ascending, but you may want something else
SELECT
COUNTRY,
LEVEL,
NUM_OF_DUPLICATES,
POOL
FROM (
SELECT *,
POOL = SUM(NUM_OF_DUPLICATES) OVER (ORDER BY NUM_OF_DUPLICATES ROWS UNBOUNDED PRECEDING)
-- alternative calculation
-- ,POOLPrev = SUM(NUM_OF_DUPLICATES) OVER (ORDER BY NUM_OF_DUPLICATES ROWS UNBOUNDED PRECEDING AND 1 PRECEDING)
FROM YourTable
) t
WHERE POOL - NUM_OF_DUPLICATES < 300;
-- you could also use POOLPrev above

I used two temp tables to get the answer.
DECLARE #t TABLE(Country VARCHAR(5), [Level] INT, Num_of_Duplicates INT)
INSERT INTO #t(Country, Level, Num_of_Duplicates)
VALUES ('US', 9, 6),
('US', 8, 24),
('US', 7, 12),
('US', 6, 20),
('US', 5, 39),
('US', 4, 81),
('US', 3, 80),
('US', 2, 130),
('US', 1, 178),
('US', 0, 254);
SELECT
Country
,Level
, Num_of_Duplicates
, SUM (Num_of_Duplicates) OVER (ORDER BY id) AS [POOL]
INTO #temp_table
FROM
(
SELECT
Country,
level,
Num_of_Duplicates,
ROW_NUMBER() OVER (ORDER BY country) AS id
FROM #t
) AS A
SELECT
[POOL],
ROW_NUMBER() OVER (ORDER BY [POOL] ) AS [rank]
INTO #Temp_2
FROM #temp_table
WHERE [POOL] >= 300
SELECT *
FROM #temp_table WHERE
[POOL] <= (SELECT [POOL] FROM #Temp_2 WHERE [rank] = 1 )
DROP TABLE #temp_table
DROP TABLE #Temp_2

Related

Display Average Billing Amount For Each Customer only between years 2019-2021

QUESTION : Display Average Billing Amount For Each Customer ONLY between YEAR(2019-2021).
If customer doesn't have any billing amount for any of the particular year then consider as 0.
-------: OUTPUT :
Customer_ID | Customer_Name | AVG_Billed_Amount
-------------------------------------------------------------------------
1 | A | 87.00
2 | B | 200.00
3 | C | 183.00
--------: EXPLANATION :
If any customer doesn't have any billing records for these 3 years then we need to consider as one record with billing_amount = 0
Like Customer C doesn't have any record for Year 2020, so for C Average will be
(250+300+0)/3 = 183.33 OR 183.00
TEMP TABLE HAS FOLLOWING DATA
DROP TABLE IF EXISTS #TEMP;
CREATE TABLE #TEMP
(
Customer_ID INT
, Customer_Name NVARCHAR(100)
, Billing_ID NVARCHAR(100)
, Billing_creation_Date DATETIME
, Billed_Amount INT
);
INSERT INTO #TEMP
SELECT 1, 'A', 'ID1', TRY_CAST('10-10-2020' AS DATETIME), 100 UNION ALL
SELECT 1, 'A', 'ID2', TRY_CAST('11-11-2020' AS DATETIME), 150 UNION ALL
SELECT 1, 'A', 'ID3', TRY_CAST('12-11-2021' AS DATETIME), 100 UNION ALL
SELECT 2, 'B', 'ID4', TRY_CAST('10-11-2019' AS DATETIME), 150 UNION ALL
SELECT 2, 'B', 'ID5', TRY_CAST('11-11-2020' AS DATETIME), 200 UNION ALL
SELECT 2, 'B', 'ID6', TRY_CAST('12-11-2021' AS DATETIME), 250 UNION ALL
SELECT 3, 'C', 'ID7', TRY_CAST('01-01-2018' AS DATETIME), 100 UNION ALL
SELECT 3, 'C', 'ID8', TRY_CAST('05-01-2019' AS DATETIME), 250 UNION ALL
SELECT 3, 'C', 'ID9', TRY_CAST('06-01-2021' AS DATETIME), 300
-----------------------------------------------------------------------------------
Here, 'A' has 3 transactions - TWICE in year 2020(100+150) and 1 in year 2021(100), but none in 2019(SO, Billed_Amount= 0).
so the average will be calculated as (100+150+100+0)/4
DECLARE #BILL_dATE DATE = (SELECT Billing_creation_date from #temp group by customer_id, Billing_creation_date) /*-- THIS THROWS ERROR AS #BILL_DATE WON'T ACCEPT MULTIPLE VALUES.*/
OUTPUT should look like this:
Customer_ID
Customer_Name
AVG_Billed_Amount
1
A
87.00
2
B
200.00
3
C
183.00
You just need a formula to count the number of missing years.
That's 3 - COUNT(DISTINCT YEAR(Billing_creation_Date)
Then the average = SUM() / (COUNT() + (3 - COUNT(DISTINCT YEAR)))...
SELECT
Customer_ID,
Customer_Name,
SUM(Billed_Amount) * 1.0
/
(COUNT(*) + 3 - COUNT(DISTINCT YEAR(Billing_creation_Date)))
AS AVG_Billed_amount
FROM
#temp
WHERE
Billing_creation_Date >= '2019-01-01'
AND Billing_creation_Date < '2022-01-01'
GROUP BY
Customer_ID,
Customer_Name
Demo : https://dbfiddle.uk/ILcfiGWL
Note: The WHERE clause in another answer here would cause a scan of the table, due to hiding the filtered column behind a function. The way I've formed the WHERE clause allows a "Range Seek" if the column is in an index.
Here is a query that can do that :
select s.Customer_ID, s.Customer_Name, sum(Billed_amount)/ ( 6 - count(1)) as AVG_Billed_Amount from (
select Customer_ID, Customer_Name, sum(Billed_Amount) as Billed_amount
from TEMP
where year(Billing_creation_Date) between 2019 and 2021
group by Customer_ID, year(Billing_creation_Date)
) as s
group by Customer_ID;
According to your description the customer_name C will be 137.5000 not 183.00 since 2018 is not counted and 2020 is not there.

Finding the largest subsets of consecutive rows with a maximum gap size (gaps and islands)

I'm trying to solve a SQL puzzle. The goal is to find subsets wherein the acceptible gap size is less than some maximum. Think of (say) searching for suspicious credit card behaviour by looking for n transactions within m minutes.
I'm using Postgres 9.6, but a correct solution to the puzzle sticks to ANSI SQL:2008.
Input
t
amt
1
10
4
10
16
40
20
10
30
50
60
5
61
5
62
5
63
5
72
5
90
30
create table d(t int, amt int);
insert into d
values (1, 10),
(4, 10),
(16, 40),
(20, 10),
(30, 50),
(60, 5),
(61, 5),
(62, 5),
(63, 5),
(72, 5),
(90, 30);
Expected Output
All subsequences such that the difference of t with the previous row is less than 10.
start_t
end_t
cnt
total
1
4
2
20
16
20
2
50
30
30
1
50
60
72
5
25
90
90
1
30
Notes
I've tried the "difference of row_number" (Tabibitosan method), but the fact that t is not necessarily consecutive foiled my efforts.
Thank you for your help!
Flag the start of group and aggregate groups
select min(t) start_t, max(t) end_t, count(*) cnt, sum(amt) total
from (
select t, amt, sum(flag) over(order by t) grp
from (
select t, amt, case when t - lag(t, 1, t-11) over(order by t) >= 10 then 1 end flag
from d
) t
) t
group by grp

Is this possible in SQL? Min and Max Dates On a Total. Where it changes in between Dates

I am trying to figure out how to write a query that will give me the correct historical data between dates. But only using sql. I know it is possible coding a loop, but I'm not sure if this is possible in a SQL query. Dates: DD/MM/YYYY
An Example of Data
ID
Points
DATE
1
10
01/01/2018
1
20
02/01/2019
1
25
03/01/2020
1
10
04/01/2021
With a simple query
SELECT ID, Points, MIN(Date), MAX(Date)
FROM table
GROUP BY ID,POINTS
The Min date for 10 points would be 01/01/2018, and the Max Date would be 04/01/2021. Which would be wrong in this instance. As It should be:
ID
Points
Min DATE
Max DATE
1
10
01/01/2018
01/01/2019
1
20
02/01/2019
02/01/2020
1
25
03/01/2020
03/01/2021
1
10
04/01/2021
04/01/2021
I was thinking of using LAG, but need some ideas here. What I haven't told you is there is a record per day. So I would need to group until a change of points. This is to create a view from the data that I already have.
It looks like - for your sample data set - the following lead should suffice:
select id, points, date as MinDate,
IsNull(DateAdd(day, -1, Lead(Date,1) over(partition by Id order by Date)), Date) as MaxDate
from t
Example Fiddle
I'm guessing you want the MAX date to be 1 day before the next MIN date.
And you can use the window function LEAD to get the next MIN date.
And if you group also by the year, then the date ranges match the expected result.
SELECT ID, Points
, MIN([Date]) AS [Min Date]
, COALESCE(DATEADD(day, -1, LEAD(MIN([Date])) OVER (PARTITION BY ID ORDER BY MIN([Date]))), MAX([Date])) AS [Max Date]
FROM your_table
GROUP BY ID, Points, YEAR([Date]);
ID
Points
Min Date
Max Date
1
10
2018-01-01
2019-01-01
1
20
2019-01-02
2020-01-02
1
25
2020-01-03
2021-01-03
1
10
2021-01-04
2021-01-04
Test on db<>fiddle here
We can do this by creating two tables one with the minimum and one with the maximum date for each grouping and then combining them
CREATE TABLE dataa(
id INT,
points INT,
ddate DATE);
INSERT INTO dataa values(1 , 10 ,'2018-10-01');
INSERT INTO dataa values(1 , 20 ,'2019-01-02');
INSERT INTO dataa values(1 , 25 ,'2020-01-03');
INSERT INTO dataa values(1 , 10 ,'2021-01-04');
SELECT
mi.id, mi.points,mi.date minDate, ma.date maxDate
FROM
(select id, points, min(ddate) date from dataa group by id,points) mi
JOIN
(select id, points, max(ddate) date from dataa group by id,points) ma
ON
mi.id = ma.id
AND
mi.points = ma.points;
DROP TABLE dataa;
this gives the following output
+------+--------+------------+------------+
| id | points | minDate | maxDate |
+------+--------+------------+------------+
| 1 | 10 | 2018-10-01 | 2021-01-04 |
| 1 | 20 | 2019-01-02 | 2019-01-02 |
| 1 | 25 | 2020-01-03 | 2020-01-03 |
+------+--------+------------+------------+
I've used the default date formatting. This could be modified if you wish.
*** See my other answer, as I don't think this answer is correct after reexamining the OPs question. Leaving ths answer in place, in case it has any value.
As I understand the problem consecutive daily values with the same value for a given ID may be ignored. This can be done by examining the prior value using the LAG() function and excluding records where the current value is unchanged from the prior.
From the remaining records, the LEAD() function can be used to look ahead to the next included record to extract the date where this value is superseded. Max Date is then calculated as one day prior.
Below is an example that includes expanded test data to cover multiple IDs and repeated Points values.
DECLARE #Data TABLE (Id INT, Points INT, Date DATE)
INSERT #Data
VALUES
(1, 10, '2018-01-01'), -- Start
(1, 20, '2019-01-02'), -- Updated
(1, 25, '2020-01-03'), -- Updated
(1, 10, '2021-01-04'), -- Updated
(2, 10, '2022-01-01'), -- Start
(2, 20, '2022-02-01'), -- Updated
(2, 20, '2022-03-01'), -- No change
(2, 20, '2022-04-01'), -- No change
(2, 20, '2022-05-01'), -- No change
(2, 25, '2022-06-01'), -- Updated
(2, 25, '2022-07-01'), -- No change
(2, 20, '2022-08-01'), -- Updated
(2, 25, '2022-09-08'), -- Updated
(2, 10, '2022-10-09'), -- Updated
(3, 10, '2022-01-01'), -- Start
(3, 10, '2022-01-02'), -- No change
(3, 20, '2022-01-03'), -- Updated
(3, 20, '2022-01-04'), -- No change
(3, 20, '2022-01-05'), -- No change
(3, 10, '2022-01-06'), -- Updated
(3, 10, '2022-01-07'); -- No change
WITH CTE AS (
SELECT *, PriorPoints = LAG(Points) OVER (PARTITION BY Id ORDER BY Date)
FROM #Data
)
SELECT ID, Points, MinDate = Date,
MaxDate = DATEADD(day, -1, (LEAD(Date) OVER (PARTITION BY Id ORDER BY Date)))
FROM CTE
WHERE (PriorPoints <> Points OR PriorPoints IS NULL) -- Exclude unchanged
ORDER BY Id, Date
Results:
ID
Points
MinDate
MaxDate
1
10
2018-01-01
2019-01-01
1
20
2019-01-02
2020-01-02
1
25
2020-01-03
2021-01-03
1
10
2021-01-04
null
2
10
2022-01-01
2022-01-31
2
20
2022-02-01
2022-05-31
2
25
2022-06-01
2022-07-31
2
20
2022-08-01
2022-09-07
2
25
2022-09-08
2022-10-08
2
10
2022-10-09
null
3
10
2022-01-01
2022-01-02
3
20
2022-01-03
2022-01-05
3
10
2022-01-06
null
db<>fiddle
For the last value for a given ID, the calculated MaxDate is NULL indicating no upper bound to the date range. If you really want MaxDate = MinDate for this case, you can add ISNULL( ..., Date).
(I am adding this as an alternative (and simpler) interpretation of the OP's question.)
Problem restatement: Given a collection if IDs, Dates, and Points values, a group is defined as any consecutive sequence of the same Points value for a given ID and ascending dates. For each such group, calculate the min and max dates.
The start of such a group can be identified as a row where the Points value changes from the preceding value, or if there is no preceding value for a given ID. If we first tag such rows (NewGroup = 1), we can then assign group numbers based on a count of preceding tagged rows (including the current row). Once we have assigned group numbers, it is then a simple matter to apply a group and aggregate operation.
Below is a sample that includes some additional test data to show multiple IDs and repeating values.
DECLARE #Data TABLE (Id INT, Points INT, Date DATE)
INSERT #Data
VALUES
(1, 10, '2018-01-01'), -- Start
(1, 20, '2019-01-02'), -- Updated
(1, 25, '2020-01-03'), -- Updated
(1, 10, '2021-01-04'), -- Updated
(2, 10, '2022-01-01'), -- Start
(2, 20, '2022-02-01'), -- Updated
(2, 20, '2022-03-01'), -- No change
(2, 20, '2022-04-01'), -- No change
(2, 20, '2022-05-01'), -- No change
(2, 25, '2022-06-01'), -- Updated
(2, 25, '2022-07-01'), -- No change
(2, 20, '2022-08-01'), -- Updated
(2, 25, '2022-09-08'), -- Updated
(2, 10, '2022-10-09'), -- Updated
(3, 10, '2022-01-01'), -- Start
(3, 10, '2022-01-02'), -- No change
(3, 20, '2022-01-03'), -- Updated
(3, 20, '2022-01-04'), -- No change
(3, 20, '2022-01-05'), -- No change
(3, 10, '2022-01-06'), -- Updated
(3, 10, '2022-01-07'); -- No change
WITH CTE AS (
SELECT *,
PriorPoints = LAG(Points) OVER (PARTITION BY Id ORDER BY Date)
FROM #Data
)
, CTE2 AS (
SELECT *,
NewGroup = CASE WHEN (PriorPoints <> Points OR PriorPoints IS NULL)
THEN 1 ELSE 0 END
FROM CTE
)
, CTE3 AS (
SELECT *, GroupNo = SUM(NewGroup) OVER(
PARTITION BY ID
ORDER BY Date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
FROM CTE2
)
SELECT Id, Points, MinDate = MIN(Date), MaxDate = MAX(Date)
FROM CTE3
GROUP BY Id, GroupNo, Points
ORDER BY Id, GroupNo
Results:
Id
Points
MinDate
MaxDate
1
10
2018-01-01
2018-01-01
1
20
2019-01-02
2019-01-02
1
25
2020-01-03
2020-01-03
1
10
2021-01-04
2021-01-04
2
10
2022-01-01
2022-01-01
2
20
2022-02-01
2022-05-01
2
25
2022-06-01
2022-07-01
2
20
2022-08-01
2022-08-01
2
25
2022-09-08
2022-09-08
2
10
2022-10-09
2022-10-09
3
10
2022-01-01
2022-01-02
3
20
2022-01-03
2022-01-05
3
10
2022-01-06
2022-01-07
To see the intermediate results, replace the final select with SELECT * FROM CTE3 ORDER BY Id, Date.
If you wish to treat gaps in dates as group criteria, add a PriorDate calculation to CTE and add OR Date <> PriorDate to the NewGroup condition.
db<>fiddle
Caution: In your original post, you state that "this is to create a view". Beware that if the above logic is included in a view, the entire result may be recalculated every time the view is accessed, regardless of any ID or date criteria applied. It might make more sense to use the above to populate and periodically refresh a historic roll-up data table for efficient access. Another alternative is to make a stored procedure with appropriate parameters that could filter that data before feeding it into the above.

SQL Server : group by consecutive

I have this table:
CREATE TABLE yourtable
(
HevEvenementID INT,
HjvNumeSequJour INT,
HteTypeEvenID INT
);
INSERT INTO yourtable
VALUES (12074, 1, 66), (12074, 2, 66), (12074, 3, 5),
(12074, 4, 7), (12074, 5, 17), (12074, 6, 17),
(12074, 7, 17), (12074, 8, 17), (12074, 9, 17), (12074, 10, 5)
I need to group by consecutive HteTypeEvenID. Right now I am doing this:
SELECT
HevEvenementID,
MAX(HjvNumeSequJour) AS HjvNumeSequJour,
HteTypeEvenID
FROM
(SELECT
HevEvenementID,
HjvNumeSequJour,
HteTypeEvenID
FROM
yourtable y) AS s
GROUP BY
HevEvenementID, HteTypeEvenID
ORDER BY
HevEvenementID,HjvNumeSequJour, HteTypeEvenID
which returns this:
HevEvenementID HjvNumeSequJour HteTypeEvenID
---------------------------------------------
12074 2 66
12074 4 7
12074 9 17
12074 10 5
I need to group by consecutive HteTypeEvenID, to get this result:
HevEvenementID HjvNumeSequJour HteTypeEvenID
----------------------------------------------
12074 2 66
12074 3 5
12074 4 7
12074 9 17
12074 10 5
Any suggestions?
In SQL Server, you can do this with aggregation and difference of row numbers:
select HevEvenementID, HteTypeEvenID,
max(HjvNumeSequJour)
from (select t.*,
row_number() over (partition by HevEvenementID order by HjvNumeSequJour) as seqnum_1,
row_number() over (partition by HevEvenementID, HteTypeEvenID order by HjvNumeSequJour) as seqnum_2
from yourtable t
) t
group by HevEvenementID, HteTypeEvenID, (seqnum_1 - seqnum_2)
order by max(HjvNumeSequJour);
I think the best way to understand how this works is by staring at the results of the subquery. You will see how the difference between the two values defines the groups of adjacent values.

sql payment distribution

I am looking for a query where a certain amount gets distributed to each invoice below based on the account_num and item_order. Also, if partial_payment_allowed is set to 'N' then distribution of the above amount should only happen if the distributed amount is greater than the invoice_amt else it should skip the row and carry on to next invoice of the account.
Item_order inv_amount Partial_pmt account_num cr_amt
1 1256 Y 12 1000
2 1134 Y 12 1000
1 800 Y 13 1200
2 200 N 13 1200
3 156 N 13 1200
In above data, each account has a cr_amt which can be distributed according to item_order. So after distribution result would be
account_num Item_order inv_amount Partial_pmt Dist_amt Bal_amt
12 1 1256 Y 1000 256
12 2 1134 Y 256 878
13 1 800 Y 800 400
13 2 200 N 200 200
13 3 156 N 100 100
We are trying to avoid loops, any comments are highly appreciated.Thank you.
Extending the answer to this question:
payment distrubution oracle sql query
You can still use the SQL MODEL clause. In this version, you need separate calculations for each distinct account_num. You can achieve this using the PARTITION keyword of the SQL MODEL clause to partition by account_num.
Like this (see SQL comments for step-by-step explanation):
-- Set up test data (since I don't have your table)
WITH inv_raw (item_order, inv_amount, partial_pmt_allowed, account_num, cr_amt) AS (
SELECT 1, 1256, 'Y', 12, 1000 FROM DUAL UNION ALL
SELECT 2, 1134, 'Y', 12, 1000 FROM DUAL UNION ALL
SELECT 3, 800, 'Y', 13, 1200 FROM DUAL UNION ALL
SELECT 4, 200, 'N',13, 1200 FROM DUAL UNION ALL
SELECT 5, 156, 'N',13, 1200 FROM DUAL),
-- Ensure that the column we are ordering by is densely populated
inv_dense (dense_item_order, item_order, inv_amount, partial_pmt_allowed, account_num, cr_amt) AS
( SELECT DENSE_RANK() OVER ( PARTITION BY account_num ORDER BY item_order ), item_order, inv_amount, partial_pmt_allowed, account_num, cr_amt FROM inv_raw )
-- Give us a way to input the payment amount
--param AS ( SELECT 1100 p_payment_amount FROM DUAL )
-- The actual query starts here
SELECT
account_num,
item_order,
inv_amount,
partial_pmt_allowed,
applied dist_amount,
remaining_out balance_amt,
cr_amt
FROM inv_dense
MODEL
-- We want a completely separate calculation for each distinct account_num
PARTITION BY ( account_num )
-- We'll output one row for each value of dense_item_order.
-- We made item_order "dense" so we can do things like CV()-1 to get the
-- previous row's values.
DIMENSION BY ( dense_item_order )
MEASURES ( cr_amt, item_order, inv_amount,
partial_pmt_allowed, 0 applied,
0 remaining_in, 0 remaining_out )
RULES AUTOMATIC ORDER (
-- The amount carried into the first row is the payment amount
remaining_in[1] = cr_amt[1],
-- The amount carried into subsequent rows is the amount we carried out of the prior row
remaining_in[dense_item_order > 1] = remaining_out[CV()-1],
-- The amount applied depends on whether the amount remaining can cover the invoice
-- and whether partial payments are allowed
applied[ANY] = CASE WHEN remaining_in[CV()] >= inv_amount[CV()] OR partial_pmt_allowed[CV()] = 'Y' THEN LEAST(inv_amount[CV()], remaining_in[CV()]) ELSE 0 END,
-- The amount we carry out is the amount we brought in minus what we applied
remaining_out[ANY] = remaining_in[CV()] - applied[CV()]
)
ORDER BY account_num, item_order;