Der wichtigste Punkt ist höchstwahrscheinlich, dass Sie JOIN
werden und GROUP
über alles, nur um max(created)
zu erhalten . Holen Sie sich diesen Wert separat.
Sie haben alle Indizes erwähnt, die hier benötigt werden:auf report_rank.created
und auf den Fremdschlüsseln. Da geht es dir gut. (Wenn Sie an besser als "in Ordnung" interessiert sind, lesen Sie weiter !)
Die LEFT JOIN report_site
wird zu einem einfachen JOIN
gezwungen durch das WHERE
Klausel. Ich habe ein einfaches JOIN
ersetzt . Ich habe auch Ihre Syntax stark vereinfacht.
Aktualisiert im Juli 2015 mit einfacheren, schnelleren Abfragen und intelligenteren Funktionen.
Lösung für mehrere Zeilen
report_rank.created
ist nicht eindeutig und Sie wollen alle die neuesten Zeilen.
Mit der Fensterfunktion rank()
in einer Unterabfrage.
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;
Warum DESC NULLS LAST
?
Lösung für eine Reihe
Wenn report_rank.created
ist einzigartig oder Sie sind mit einer beliebigen Zeile zufrieden mit 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;
Sollte immer noch schneller sein. Weitere Optionen:
Ultimate Speed mit dynamisch angepasstem Teilindex
Möglicherweise ist Ihnen der kommentierte Teil in der letzten Abfrage aufgefallen:
AND r.created > f_report_rank_cap()
Sie haben 50 Mio. Reihen, das ist eine Menge. Hier ist eine Möglichkeit, die Dinge zu beschleunigen:
- Erstellen Sie einen einfachen
IMMUTABLE
Funktion, die einen Zeitstempel zurückgibt, der garantiert älter als die relevanten Zeilen ist, aber so jung wie möglich. - Erstellen Sie einen Teilindex nur in jüngeren Zeilen - basierend auf dieser Funktion.
- Verwenden Sie ein
WHERE
Bedingung in Abfragen, die der Indexbedingung entspricht. - Erstellen Sie eine weitere Funktion, die diese Objekte mit dynamischer DDL auf die neueste Zeile aktualisiert. (Abzüglich einer sicheren Marge falls die neueste(n) Zeile(n) gelöscht / deaktiviert werden - falls das passieren kann)
- Rufen Sie diese sekundäre Funktion in Ruhezeiten mit einem Minimum an gleichzeitiger Aktivität per Cronjob oder bei Bedarf auf. So oft du willst, kann nicht schaden, es braucht nur eine kurze exklusive Sperre auf dem Tisch.
Hier ist eine vollständig funktionierende Demo .
@erikcw, Sie müssen den kommentierten Teil wie unten beschrieben aktivieren.
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.';
Aufruf:
SELECT f_report_rank_set_cap();
Siehe:
SELECT f_report_rank_cap();
Entkommentieren Sie die Klausel AND r.created > f_report_rank_cap()
in die Abfrage oben und beobachten Sie den Unterschied. Überprüfen Sie mit EXPLAIN ANALYZE
, ob der Index verwendet wird .
Das Handbuch zu Parallelität und REINDEX
: