I have the following query:
SELECT
SUM(\"balance_transactions\".\"fee\") AS sum_id
FROM \"balance_transactions\"
JOIN charges ON balance_transactions
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! :)
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
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);
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.
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
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.