You can hugely simplify the query using DISTINCT ON
:
SELECT DISTINCT ON (client, practice, account_id, encounter_id, charge_id)
id, charge_id, post_date
FROM charges
ORDER BY client, practice, account_id, encounter_id, charge_id, post_date DESC, id DESC;
Will be considerably faster in any case. Detailed explanation in this related answer on SO:
Select first row in each GROUP BY group?
The last ORDER BY
expression id DESC
is optional to break ties if rest should not be unambiguous, yet. May not be needed.
Support this with a matching multicolumn index:
CREATE INDEX charges_latest_idx ON charges
(client, practice, account_id, encounter_id, charge_id, post_date DESC, id DESC);
Whether such an index will be useful depends on undisclosed details.
Note in particular, that sort order has to match the query. In Postgres 9.2 or later this may even function as covering index, depending on undisclosed details.
Depending on undisclosed details, a materialized view might be a candidate, too. The more write operations the smaller the likelihood this would help. The same goes for the covering index.
After some processing this boiled down to:
While your predicate d."SettlementPointName" = 'John'
is filtering a single value for "SettlementPointName" anyway, simplify to:
SELECT count( d."SettlementPointPrice" < 10.5 OR NULL) AS da_00_10
, count(d."SettlementPointPrice" >= 10.5 AND d."SettlementPointPrice" < 20.5 OR NULL) AS da_11_20
, count(d."SettlementPointPrice" >= 20.5 AND d."SettlementPointPrice" < 30.5 OR NULL) AS da_21_30
FROM public.da d
JOIN public.rt_aggregate r USING ("DeliveryDate", "SettlementPointName")
WHERE d."SettlementPointName" = 'John'
AND d."DeliveryDate" >= '2015-02-01'
AND d."DeliveryDate" <= '2015-02-20'
AND r."DeliveryHour" = 14
AND date_part('hour', d."DeliveryHour") = r."DeliveryHour";
About the counting technique:
Or better, yet, use the new aggregate filter technique in pg 9.4:
SELECT d."SettlementPointName"
, count(*) FILTER (WHERE d."SettlementPointPrice" < 10.5) AS da_00_10
, count(*) FILTER (WHERE d."SettlementPointPrice" >= 10.5
AND d."SettlementPointPrice" < 20.5) AS da_11_20
, count(*) FILTER (WHERE d."SettlementPointPrice" >= 20.5
AND d."SettlementPointPrice" < 30.5) AS da_21_30
FROM public.da d
JOIN public.rt_aggregate r USING ("DeliveryDate", "SettlementPointName")
WHERE d."DeliveryDate" >= '2015-02-01'
AND d."DeliveryDate" <= '2015-02-20'
AND r."DeliveryHour" = 14
AND date_part('hour', d."DeliveryHour") = r."DeliveryHour"
GROUP BY 1;
This time, selecting all names and returning one row per name like you asked in the comment.
Details for FILTER
:
Best Answer
Yes, they are, because they do not appear in plain C.