How can I create a Running Total Sumifs-like function in SQL?

57 views Asked by At

I'm pretty new to SQL, but Excel has become far too slow to continue working with, so I'm trying SQLiteStudio. I'm looking to create a column in a query showing running total over time (characterized as Schedule Points, marking each percent through a project's run time). Complete marks whether a Location has completed the install (Y/NULL), and is used simply to filter out incomplete locations from further calculations.

I currently have

With cte as(
Select [Location]
        ,[HW/NonHW]
        ,[Obligation/Actual]
        ,[Schedule Point]
        ,[CY20$]
        ,[Vendor Name]
        ,[Vendor Zip Code]
        ,[Complete]
        ,[System Rollup (Import)]
        ,IIf([Complete] = "Y", [CY20$], 0) As [Completed Costs]

FROM data)
Select [Location]
        ,[HW/NonHW]
        ,[Obligation/Actual]
        ,[Schedule Point]
        ,[CY20$]
        ,[Vendor Name]
        ,[Vendor Zip Code]
        ,[Complete]
        ,[System Rollup (Import)]
        ,[Completed Costs]
        ,SUM([Completed Costs]) OVER (PARTITION BY [Obligation/Actual], [Normalized Schedule Location 1%],[System Rollup (Import)], [HW/NonHW]) As [CY20$ Summed]

 FROM cte

At this point, what I'm looking to do is a sum not for each Schedule Point, but all prior Schedule Points (i.e. the <= operator in an Excel sumifs statement)

For reference, here is the sumifs I am trying to replicate:

=SUMIFS($N$2:$N$541790,$AU$2:$AU$541790,"Y",$AQ$2:$AQ$541790,AQ2,$AI$2:$AI$541790,AI2,$AH$2:$AH$541790,AH2,$AJ$2:$AJ$541790, "<=" & AJ2)

N is CY20$, AU is Complete, AQ is System, AI is Obligation/Actual, AH is HW/NonHW, AJ is Schedule Point.

Any help would be appreciated!

1

There are 1 answers

8
Lajos Arpad On

The equivalent to SUMIFS is a combination of SUM and CASE-WHEN in SQL.

Abstract example:

SELECT
    SUM(
        CASE
            WHEN <condition1> AND <condition2> AND <condition3> THEN 1
            ELSE 0
        END
    )
FROM yourtable

In the above, condition1, condition2 and condition3 are logical expressions, the < and > are just notifying that you have an expression there, it is not part of the syntax. It is also unnecessary to have exactly 3 conditions, you can have as many as you like. Also, it is unnecessary to use AND as the operator, you can construct your own expression as you like. The reason for which I have used the AND operator was that you intend to have a disjunction, presumably, based on the fact that you used SUMIFS.

A more concrete example:

CREATE TABLE person(
    number int,
    name   text,
    age    int
);

INSERT INTO person(number, name, age)
VALUES(1, 'Joe', 12);
INSERT INTO person(number, name, age)
VALUES(2, 'Jane' 12);
INSERT INTO person(number, name, age)
VALUES(3, 'Robert', 16);
INSERT INTO person(number, name, age)
VALUES(4, 'Roberta', 15);
INSERT INTO person(number, name, age)
VALUES(5, 'Blian', 18);
INSERT INTO person(number, name, age)
VALUES(6, 'Bigusdqs', 19);

SELECT 
    SUM(
        CASE
            WHEN age <= 16 AND name <> 'Joe' THEN 1
            ELSE 0
        END
    ) AS MySUMIFS
FROM person;

EDIT

If we are interested to know how many people have a smaller age than the current person, then we can do a join:

SELECT 
    SUM(
        CASE
            WHEN p2.age <= p1.age THEN 1
            ELSE 0
        END
    ) AS MySUMIFS, name
FROM person p1
JOIN person p2
ON p1.name <> p2.name
GROUP BY p1.name;

EDIT2

Created a Fiddle based on the ideas described above, you can reach it at https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=3cb0232e5d669071a3aa5bb1df68dbca

The code in the fiddle:

CREATE TABLE person(
    number int,
    name   text,
    age    int
);

INSERT INTO person(number, name, age)
VALUES(1, 'Joe', 12);
INSERT INTO person(number, name, age)
VALUES(2, 'Jane' 12);
INSERT INTO person(number, name, age)
VALUES(3, 'Robert', 16);
INSERT INTO person(number, name, age)
VALUES(4, 'Roberta', 15);
INSERT INTO person(number, name, age)
VALUES(5, 'Blian', 18);
INSERT INTO person(number, name, age)
VALUES(6, 'Bigusdqs', 19);
SELECT 
    SUM(
        CASE
            WHEN p2.age <= p1.age THEN 1
            ELSE 0
        END
    ) AS MySUMIFS, p1.name
FROM person p1
JOIN person p2
ON p1.name <> p2.name
GROUP BY p1.name;

enter image description here