Poin utamanya kemungkinan besar Anda JOIN
dan GROUP
atas segalanya hanya untuk mendapatkan max(created)
. Dapatkan nilai ini secara terpisah.
Anda menyebutkan semua indeks yang diperlukan di sini:di report_rank.created
dan pada kunci asing. Anda baik-baik saja di sana. (Jika Anda tertarik lebih baik daripada "baik", terus membaca !)
LEFT JOIN report_site
akan dipaksa ke JOIN
plain biasa dengan WHERE
ayat. Saya mengganti JOIN
plain biasa . Saya juga banyak menyederhanakan sintaks Anda.
Diperbarui Juli 2015 dengan kueri yang lebih sederhana, lebih cepat, dan fungsi yang lebih cerdas.
Solusi untuk beberapa baris
report_rank.created
adalah tidak unik dan Anda ingin semua baris terbaru.
Menggunakan fungsi jendela rank()
dalam subkueri.
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;
Mengapa DESC NULLS LAST
?
Solusi untuk satu baris
Jika report_rank.created
adalah unik atau Anda puas dengan 1 baris apa pun dengan 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;
Harus lebih cepat, tetap saja. Opsi lainnya:
Kecepatan Tertinggi dengan indeks parsial yang disesuaikan secara dinamis
Anda mungkin telah memperhatikan bagian yang dikomentari dalam kueri terakhir:
AND r.created > f_report_rank_cap()
Anda menyebutkan 50 juta. baris, itu banyak. Berikut adalah cara untuk mempercepatnya:
- Buat
IMMUTABLE
simple sederhana fungsi mengembalikan stempel waktu yang dijamin lebih tua dari deretan minat saat masih semuda mungkin. - Buat indeks parsial hanya pada baris yang lebih muda - berdasarkan fungsi ini.
- Gunakan
WHERE
kondisi dalam kueri yang cocok dengan kondisi indeks. - Buat fungsi lain yang memperbarui objek ini ke baris terbaru dengan DDL dinamis. (Dikurangi margin aman jika baris terbaru dihapus / dinonaktifkan - jika itu bisa terjadi)
- Aktifkan fungsi sekunder ini pada waktu tidak aktif dengan aktivitas bersamaan minimum per cronjob atau sesuai permintaan. Sesering yang Anda inginkan, tidak ada salahnya, hanya perlu kunci eksklusif pendek di atas meja.
Berikut adalah demo kerja lengkap .
@erikcw, Anda harus mengaktifkan bagian komentar seperti yang diinstruksikan di bawah ini.
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.';
Telepon:
SELECT f_report_rank_set_cap();
Lihat:
SELECT f_report_rank_cap();
Batalkan komentar pada klausa AND r.created > f_report_rank_cap()
dalam kueri di atas dan amati perbedaannya. Verifikasi bahwa indeks digunakan dengan EXPLAIN ANALYZE
.
Manual tentang konkurensi dan REINDEX
: