重要な点は、JOIN
である可能性が最も高いです。 およびGROUP
max(created)
を取得するためだけにすべてを 。この値は個別に取得してください。
ここで必要なすべてのインデックスについて言及しました:report_rank.created
および外部キー。あなたはそこで大丈夫です。 (「大丈夫」よりも興味がある場合は、読み続けてください !)
LEFT JOIN report_site
プレーンなJOIN
に強制されます WHERE
によって 句。プレーンなJOIN
に置き換えました 。また、構文を大幅に簡略化しました。
2015年7月更新 よりシンプルで高速なクエリとよりスマートな機能を備えています。
複数行のソリューション
report_rank.created
ユニークではない すべてが必要です 最新の行。
ウィンドウ関数の使用rank()
サブクエリで。
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;
DESC NULLS LAST
が選ばれる理由 ?
1行のソリューション
report_rank.created
の場合 ユニークです または、任意の1行に満足しています 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;
それでも、もっと速いはずです。その他のオプション:
動的に調整された部分インデックスによる究極の速度
最後のクエリでコメントされた部分に気づいたかもしれません:
AND r.created > f_report_rank_cap()
あなたは50ミオについて言及しました。行、それはたくさんです。これが物事をスピードアップする方法です:
- 単純な
IMMUTABLE
を作成します 可能な限り若い間、対象の行よりも古いことが保証されているタイムスタンプを返す関数。 - 部分インデックス を作成します 若い行のみ-この関数に基づく。
-
WHERE
を使用する インデックス条件に一致するクエリの条件。 - これらのオブジェクトを動的DDLを使用して最新の行に更新する別の関数を作成します。 (マイナス安全マージン 最新の行が削除/非アクティブ化された場合-それが発生する可能性がある場合)
- この二次関数をオフタイムに呼び出し、cronジョブごとまたはオンデマンドで最小限の同時アクティビティを実行します。何度でも害を及ぼすことはできません。テーブルに短い排他ロックが必要なだけです。
これが完全なデモです 。
@erikcw、以下の手順に従って、コメント部分をアクティブ化する必要があります。
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.';
電話:
SELECT f_report_rank_set_cap();
参照:
SELECT f_report_rank_cap();
AND r.created > f_report_rank_cap()
句のコメントを解除します 上記のクエリで違いを観察します。インデックスがEXPLAIN ANALYZE
で使用されることを確認します 。