Sum for multiple date ranges in a single call?

前端 未结 10 1767
有刺的猬
有刺的猬 2021-02-14 10:36

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:22

    Here's a untested procedure you can used.

    CREATE OR REPLACE PROCEDURE sum_fees(v_start IN Date, v_end in Date) IS
    
    BEGIN
      SELECT 
       SUM("balance_transactions"."fee") AS sum_id 
       FROM "balance_transactions" 
           JOIN charges ON balance_transactions.source = charges.balance_id 
       WHERE "balance_transactions"."account_id" = 6 
          AND (balance_transactions.type = 'charge' 
          AND charges.refunded = false 
          AND charges.invoice IS NOT NULL) 
          AND ("balance_transactions"."created" BETWEEN v_start AND v_end);
    END;
    

    Then call the procedure with your range date.

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

    if i understand well you want to reutilize the date query. For this the part of the query that can be reutilized is the daily part. I mean:

    SELECT 
       SUM("balance_transactions"."fee") AS sum_id 
       FROM "balance_transactions" 
       JOIN charges ON balance_transactions.source = charges.balance_id 
       WHERE "balance_transactions"."account_id" = 6 
          AND (balance_transactions.type = 'charge' 
          AND charges.refunded = false 
          AND charges.invoice IS NOT NULL) 
          AND ("balance_transactions"."created" = 'yyyy-mm-dd');
    

    Assuming that your "created" field is just date and not timestamp, and if the data of past days doesn't change, you can dump this query to a table:

    insert into sum_table
    SELECT 
       "balance_transactions"."created" balance_created
       SUM("balance_transactions"."fee") AS balance_fee 
       FROM "balance_transactions" 
       JOIN charges ON balance_transactions.source = charges.balance_id 
       WHERE "balance_transactions"."account_id" = 6 
          AND (balance_transactions.type = 'charge' 
          AND charges.refunded = false 
          AND charges.invoice IS NOT NULL) 
       group by "balance_transactions"."created"
    ;
    

    and then change your main query to:

    SELECT 
       SUM(balance_fee) AS sum_id 
       FROM sum_table where balance_created between ('2013-12-20' AND '2014-01-19');
    

    Another optimization is to eliminate the between because usually it does not uses indexes, and if you have lots of different dates it can be slow.

    Better this way:

    SELECT 
       SUM(balance_fee) AS sum_id 
       FROM sum_table where balance_created in ('2013-12-20', '2013-12-21', '2013-12-22' ... '2014-01-19');
    

    But for this you have to create the SQL directly in the client application (ej. DAO)

    Hope this helps.

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

    Well coming from a SQL Server background I would change your where clause to

    ...
    AND (
          "balance_transactions"."created" BETWEEN '2013-12-20' AND '2014-01-19'
          OR
          "balance_transactions"."created" BETWEEN '2013-12-21' AND '2014-01-20'
          OR
          "balance_transactions"."created" BETWEEN '2013-12-23' AND '2014-01-22'
          OR
          "balance_transactions"."created" BETWEEN '2013-12-24' AND '2014-01-23'
        );
    

    Just be sure you have a good index on those dates! :)

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

    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:35

    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:36
    SELECT periods.start_date, 
         periods.end_date, 
         SUM(fee) AS Period_Sum
    FROM "balance_transactions" 
    JOIN charges ON balance_transactions.source = charges.balance_id 
    JOIN
    (SELECT CAST('2013-12-20' AS DATE) AS start_date, CAST('2014-01-19' AS DATE) AS end_date UNION     ALL
     SELECT  CAST('2013-12-21' AS DATE),CAST('2014-01-20'  AS DATE) UNION ALL
     SELECT  CAST('2013-12-22' AS DATE),  CAST('2014-01-21' AS DATE) UNION ALL
     SELECT CAST('2013-12-23' AS DATE),  CAST('2014-01-22' AS DATE) UNION ALL
     SELECT  CAST('2013-12-24' AS DATE), CAST('2014-01-23' AS 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
    

    Here is link to SQL Fiddle Where I tested it: http://sqlfiddle.com/#!10/535ac/11/0

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