Sum for multiple date ranges in a single call?

前端 未结 10 1352
礼貌的吻别
礼貌的吻别 2021-02-14 11:03

I have the following query:

SELECT 
   SUM(\"balance_transactions\".\"fee\") AS sum_id 
   FROM \"balance_transactions\" 
   JOIN charges ON balance_transactions         


        
相关标签:
10条回答
  • 2021-02-14 11:39

    Assuming I understand your request correctly I think what you need is something along these lines:

    SELECT "periods"."start_date", 
           "periods"."end_date", 
           SUM(CASE WHEN "balance_transactions"."created" BETWEEN "periods"."start_date" AND "periods"."end_date" THEN "balance_transactions"."fee" ELSE 0.00 END) AS period_sum
      FROM "balance_transactions" 
      JOIN charges ON balance_transactions.source = charges.balance_id 
      JOIN ( SELECT '2013-12-20'::date as start_date, '2014-01-19'::date as end_date UNION ALL
             SELECT '2013-12-21'::date as start_date, '2014-01-20'::date as end_date UNION ALL
             SELECT '2013-12-22'::date as start_date, '2014-01-21'::date as end_date UNION ALL
             SELECT '2013-12-23'::date as start_date, '2014-01-22'::date as end_date UNION ALL
             SELECT '2013-12-24'::date as start_date, '2014-01-23'::date as end_date
             ) as periods
        ON "balance_transactions"."created" BETWEEN "periods"."start_date" AND "periods"."end_date"
     WHERE "balance_transactions"."account_id" = 6 
       AND "balance_transactions"."type" = 'charge' 
       AND "charges"."refunded" = false 
       AND "charges"."invoice" IS NOT NULL
     GROUP BY "periods"."start_date", "periods"."end_date"
    

    This should return you all the periods you're interested in in one single resultset. Since the query is 'generated' on the fly in your front-end you can add as many rows to the periods part as you want.

    Edit: with some trial and error I managed to get it working [in sqlFiddle][1] and updated the syntax above accordingly.

    0 讨论(0)
  • 2021-02-14 11:41

    This will return all the month ranges' sum between a given date and today.

    Tuples output - SQL Fiddle

    select distinct on (s.d)
        s.d as "date",
        sum(bt.fee) over (
            partition by daterange(s.d, (s.d + interval '1 month')::date, '[]')
        ) as sum_id
    from
        balance_transactions bt
        inner join
        charges on bt.source = charges.balance_id
        right join
        (
            select d::date as d
            from generate_series (
                '2013-12-20'::date,
                current_date,
                interval '1 day'
            ) s(d)
        ) s on s.d = bt.created
    where
        bt.account_id = 6
        and bt.type = 'charge'
        and charges.refunded = false
        and charges.invoice is not null
    order by s.d
    

    Array output. Does not work on SQL Fiddle but works in my desktop.

    select array_agg(("date", sum_id)) as arr_sum_id
    from (
        select distinct on (s.d)
            s.d as "date",
            sum(bt.fee) over (
                partition by daterange(s.d, (s.d + interval '1 month')::date, '[]')
            ) as sum_id
        from
            balance_transactions bt
            inner join
            charges on bt.source = charges.balance_id
            right join
            (
                select d::date as d
                from generate_series (
                    '2013-12-20'::date,
                    current_date,
                    interval '1 day'
                ) s(d)
            ) s on s.d = bt.created
        where
            bt.account_id = 6
            and bt.type = 'charge'
            and charges.refunded = false
            and charges.invoice is not null
        order by s.d
    ) s
    
    0 讨论(0)
  • 2021-02-14 11:41

    Try this:

    create table timeframes (
        start_dt date,
        end_dt date
    );
    
    insert into timeframes values ('2013-12-20', '2014-01-19');
    insert into timeframes values ('2013-12-21', '2014-01-20');
    insert into timeframes values ('2013-12-22', '2014-01-21');
    insert into timeframes values ('2013-12-23', '2014-01-22');
    insert into timeframes values ('2013-12-24', '2014-01-23');
    
    SELECT 
        tf.start_date, 
        tf.end_date, 
        SUM(CASE 
            WHEN t.created BETWEEN tf.start_date AND tf.end_date THEN t.fee
            ELSE 0.00 
        END) as transaction_sum
    FROM 
        balance_transactions t
    INNER JOIN 
        charges c
    ON 
        t.source = c.balance_id 
    INNER JOIN 
        timeframes tf
    ON 
        t.created BETWEEN tf.start_date AND tf.end_date
    WHERE 
        t.account_id = 6
    AND 
        (
        t.type = 'charge' 
            AND 
        c.refunded = false 
            AND 
        c.invoice IS NOT NULL
        ) 
    GROUP BY
        tf.start_date, 
        tf.end_date
    
    0 讨论(0)
  • 2021-02-14 11:42

    I worked on the following code. It uses an XML file. The file or string contains the date ranges you need to sumarize. The Stored Procedure will return a table with the totals per specific range.

    /*****************CREATES/POPULATES FAKE TABLES WITH A SIMILAR STRUCTURE TO THE ONE YOU ARE USING************/
    DECLARE @balance_transactions TABLE(fee FLOAT,
                                        source INT,
                                        account_id INT,
                                        [type] VARCHAR(25),
                                        created DATETIME)
    
    INSERT INTO @balance_transactions
    SELECT 12.5, 1, 6, 'charge', '01/15/2012'
    UNION
    SELECT 70, 2, 6, 'charge', '01/16/2012'
    UNION
    SELECT 136.89, 3, 6, 'charge', '01/17/2012'
    UNION
    SELECT 29.16, 4, 6, 'charge', '01/18/2012'
    UNION
    SELECT 1369.54, 5, 6, 'charge', '02/21/2012'
    UNION
    SELECT 468.85, 6, 6, 'charge', '02/22/2012'
    UNION
    SELECT 65.8, 7, 6, 'charge', '02/22/2012'
    UNION
    SELECT 1236.87, 8, 6, 'charge', '02/22/2012'
    
    DECLARE @charges TABLE(balance_id INT,
                           refunded BIT,
                           invoice INT)
    
    INSERT INTO @charges
    SELECT 1, 0, 7
    UNION
    SELECT 2, 0, 8
    UNION
    SELECT 3, 0, 9
    UNION
    SELECT 4, 0, 10
    UNION
    SELECT 5, 0, 11
    UNION
    SELECT 6, 0, 12
    UNION
    SELECT 7, 0, null
    UNION
    SELECT 8, 0, null
    /*******************************************************/
    
    /*
    You can use the code below for creating an Stored Procedure.
    The SP will return a table with the SUM of all those values indicating the Date Range.
    
    spGetTotalsPerDateRange 'your xml goes here'
    
    results:
    
    fromDate                |toDate                     |total
    2012-01-15 00:00:00.000 |2012-01-30 00:00:00.000    |248.55
    2012-02-15 00:00:00.000 |2012-02-28 00:00:00.000    |3141.06
    */
    SET DATEFORMAT MDY
    
    DECLARE @XmlDocumentHandle int
    DECLARE @XmlDocument nvarchar(4000)
    SET @XmlDocument = 
    N'<dates>
        <range>
           <fromDate>01/15/2012</fromDate>
           <toDate>01/30/2012</toDate>
        </range>
        <range>   
           <fromDate>02/15/2012</fromDate>
           <toDate>02/28/2012</toDate>
        </range>
    </dates>'
    
    EXEC sp_xml_preparedocument @XmlDocumentHandle OUTPUT, @XmlDocument
    
    DECLARE @feeTotal TABLE(fromDate DATETIME,
                            toDate DATETIME,
                            total FLOAT)
    
    
    DECLARE @fromDate DATETIME
    DECLARE @toDate DATETIME
    DECLARE ranges_cur CURSOR FOR
    
    SELECT fromDate, toDate
    FROM OPENXML (@XmlDocumentHandle, '/dates/range',2)
         WITH (fromDate  DATETIME,
               toDate DATETIME);
    
    OPEN ranges_cur;
    FETCH NEXT FROM ranges_cur INTO @fromDate, @toDate;
    WHILE @@FETCH_STATUS = 0
       BEGIN
          INSERT INTO @feeTotal
          SELECT @fromDate, 
                 @toDate,
                 SUM(bt.fee)
          FROM @balance_transactions bt
          INNER JOIN @charges c ON bt.source = c.balance_id 
          WHERE bt.account_id = 6 
          AND (bt.type = 'charge' 
          AND c.refunded = 0 
          AND c.invoice IS NOT NULL)
          AND (bt.created >= @fromDate AND bt.created <= @toDate);
    
          FETCH NEXT FROM ranges_cur INTO @fromDate, @toDate;
       END;
    CLOSE ranges_cur;
    DEALLOCATE ranges_cur;
    
    SELECT fromDate,
           toDate,
           total
    FROM @feeTotal
    
    EXEC sp_xml_removedocument @XmlDocumentHandle
    
    GO
    

    The parametrized query will look like the following one.

    CREATE PROCEDURE spGetTotalsPerDateRange(@XmlDocument NVARCHAR(4000),
                                             @type VARCHAR(50) = 'charge',
                                             @refunded BIT = 0)
    AS
    BEGIN
        SET DATEFORMAT MDY
    
        DECLARE @XmlDocumentHandle INT
        EXEC sp_xml_preparedocument @XmlDocumentHandle OUTPUT, @XmlDocument
    
        DECLARE @feeTotal TABLE(fromDate DATETIME,
                                toDate DATETIME,
                                total FLOAT)
    
    
        DECLARE @fromDate DATETIME
        DECLARE @toDate DATETIME
        DECLARE ranges_cur CURSOR FOR
    
        SELECT fromDate, toDate
        FROM OPENXML (@XmlDocumentHandle, '/dates/range',2)
             WITH (fromDate  DATETIME,
                   toDate DATETIME);
    
        OPEN ranges_cur;
        FETCH NEXT FROM ranges_cur INTO @fromDate, @toDate;
        WHILE @@FETCH_STATUS = 0
           BEGIN
              INSERT INTO @feeTotal
              SELECT @fromDate, 
                     @toDate,
                     SUM(bt.fee)
              FROM balance_transactions bt
              INNER JOIN charges c ON bt.source = c.balance_id 
              WHERE bt.account_id = 6 
              AND (bt.type = 'charge' 
              AND c.refunded = 0 
              AND c.invoice IS NOT NULL)
              AND (bt.created >= @fromDate AND bt.created <= @toDate);
    
              FETCH NEXT FROM ranges_cur INTO @fromDate, @toDate;
           END;
        CLOSE ranges_cur;
        DEALLOCATE ranges_cur;
    
        SELECT fromDate,
               toDate,
               total
        FROM @feeTotal
    
        EXEC sp_xml_removedocument @XmlDocumentHandle
    
    END
    GO
    

    The first code was tested using fake data and it works properly. You need to make the required adjustments to types and names to the SP columns and variable types in accordance to your tables definition.

    The idea of this approach is being able to report any information in the way you need it. You can also pass additional parameters via XML attributes. Check more info of OPEN XML on MSDN website

    Hope it helps

    0 讨论(0)
提交回复
热议问题