Het belangrijkste punt is hoogstwaarschijnlijk dat je JOIN
en GROUP
over alles om max(created)
. te krijgen . Verkrijg deze waarde apart.
Je noemde alle indexen die hier nodig zijn:op report_rank.created
en op de buitenlandse sleutels. Het gaat je goed daar. (Als je geïnteresseerd bent in beter dan "alright", blijf lezen !)
De LEFT JOIN report_site
wordt gedwongen tot een eenvoudige JOIN
door de WHERE
clausule. Ik heb een gewone JOIN
. vervangen . Ik heb je syntaxis ook veel vereenvoudigd.
Bijgewerkt in juli 2015 met eenvoudigere, snellere zoekopdrachten en slimmere functies.
Oplossing voor meerdere rijen
report_rank.created
is niet uniek en je wilt alles de laatste rijen.
De vensterfunctie gebruiken rank()
in een subquery.
SELECT r.id, r.keyword_id, r.site_id
, r.rank, r.url, r.competition
, r.source, r.country, r.created -- same as "max"
FROM (
SELECT *, rank() OVER (ORDER BY created DESC NULLS LAST) AS rnk
FROM report_rank r
WHERE EXISTS (
SELECT *
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE
)
) sub
WHERE rnk = 1;
Waarom DESC NULLS LAST
?
Oplossing voor één rij
Als report_rank.created
is uniek of je bent tevreden met elke rij met max(created)
:
SELECT id, keyword_id, site_id
, rank, url, competition
, source, country, created -- same as "max"
FROM report_rank r
WHERE EXISTS (
SELECT 1
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE
)
-- AND r.created > f_report_rank_cap()
ORDER BY r.created DESC NULLS LAST
LIMIT 1;
Moet nog sneller kunnen. Meer opties:
Ultieme snelheid met dynamisch aangepaste gedeeltelijke index
Je hebt misschien het becommentarieerde gedeelte in de laatste vraag opgemerkt:
AND r.created > f_report_rank_cap()
U noemde 50 miljoen. rijen, dat is veel. Hier is een manier om dingen te versnellen:
- Maak een eenvoudige
IMMUTABLE
functie die een tijdstempel retourneert dat gegarandeerd ouder is dan de rijen van interesse, terwijl het zo jong mogelijk is. - Maak een gedeeltelijke index alleen op jongere rijen - gebaseerd op deze functie.
- Gebruik een
WHERE
voorwaarde in zoekopdrachten die overeenkomt met de indexvoorwaarde. - Maak een andere functie die deze objecten bijwerkt naar de laatste rij met dynamische DDL. (Min een veilige marge in het geval dat de nieuwste rij(en) worden verwijderd / gedeactiveerd - als dat kan gebeuren)
- Roep deze secundaire functie op buiten de tijd met een minimum aan gelijktijdige activiteit per cronjob of op aanvraag. Zo vaak als je wilt, kan geen kwaad, het heeft alleen een kort exclusief slotje op tafel nodig.
Hier is een compleet werkende demo .
@erikcw, je moet het gedeelte met commentaar activeren zoals hieronder wordt aangegeven.
CREATE TABLE report_rank(created timestamp);
INSERT INTO report_rank VALUES ('2011-11-11 11:11'),(now());
-- initial function
CREATE OR REPLACE FUNCTION f_report_rank_cap()
RETURNS timestamp LANGUAGE sql COST 1 IMMUTABLE AS
$y$SELECT timestamp '-infinity'$y$; -- or as high as you can safely bet.
-- initial index; 1st run indexes whole tbl if starting with '-infinity'
CREATE INDEX report_rank_recent_idx ON report_rank (created DESC NULLS LAST)
WHERE created > f_report_rank_cap();
-- function to update function & reindex
CREATE OR REPLACE FUNCTION f_report_rank_set_cap()
RETURNS void AS
$func$
DECLARE
_secure_margin CONSTANT interval := interval '1 day'; -- adjust to your case
_cap timestamp; -- exclude older rows than this from partial index
BEGIN
SELECT max(created) - _secure_margin
FROM report_rank
WHERE created > f_report_rank_cap() + _secure_margin
/* not needed for the demo; @erikcw needs to activate this
AND EXISTS (
SELECT *
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE)
*/
INTO _cap;
IF FOUND THEN
-- recreate function
EXECUTE format('
CREATE OR REPLACE FUNCTION f_report_rank_cap()
RETURNS timestamp LANGUAGE sql IMMUTABLE AS
$y$SELECT %L::timestamp$y$', _cap);
-- reindex
REINDEX INDEX report_rank_recent_idx;
END IF;
END
$func$ LANGUAGE plpgsql;
COMMENT ON FUNCTION f_report_rank_set_cap()
IS 'Dynamically recreate function f_report_rank_cap()
and reindex partial index on report_rank.';
Bel:
SELECT f_report_rank_set_cap();
Zie:
SELECT f_report_rank_cap();
Maak commentaar op de clausule AND r.created > f_report_rank_cap()
in de bovenstaande query en observeer het verschil. Controleer of de index wordt gebruikt met EXPLAIN ANALYZE
.
De handleiding over gelijktijdigheid en REINDEX
: