Sum for multiple date ranges in a single call?

前端 未结 10 1369
礼貌的吻别
礼貌的吻别 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:21

    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:24
    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)
  • 2021-02-14 11:27

    I am coming from oracle PL/ SQL back ground. In my opinion you can use below query to use it. My understanding is that if any transaction is happening on 19-NOV-2013 1.00 AM is always going to be in 19 Nov to 20 Nov bucket so We should not worry about range I have created below query. I hope so it will help you.

    SELECT DISTINCT account_id,
      TRUNC(created)
      ||' to '
      || (TRUNC(created)+1) period,
      SUM(FEE) over (partition BY account_id,TRUNC(created) ) sumid
    FROM balance_transactions a,
      charges b
    WHERE a.source =balance_id
    AND b.refunded =0
    AND b.invoice IS NOT NULL
    AND a.type     ='charge'
    ORDER BY TRUNC(created)
      ||' to '
      || (TRUNC(created)+1);
    
    0 讨论(0)
  • 2021-02-14 11:28

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

    It hasn't been mentioned yet and would not be as efficient as a single select BUT as a solution you could use a UNION and group by your own static identifier (date range group) such as:

    SELECT 
       'a' AS DateRangeIdentifier,
       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 '2013-01-01' AND '2013-01-31')
    
    UNION 
    
    SELECT 
       'b' AS DateRangeIdentifier,
       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 '2013-07-01' AND '2013-07-31')
    
    UNION 
    
    SELECT 
       'c' AS DateRangeIdentifier,
       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 '2013-12-20' AND '2014-01-19')
    
    GROUP BY DateRangeIdentifier, sum_id
    

    This way would at least yield a single database request and not a loop in your code.

    See updated Fiddle here: http://sqlfiddle.com/#!15/9ce0f/5

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

    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)
提交回复
热议问题