Ada berbagai cara yang lebih sederhana dan lebih cepat.
2x DISTINCT ON
SELECT *
FROM (
SELECT DISTINCT ON (name)
name, week AS first_week, value AS first_val
FROM tbl
ORDER BY name, week
) f
JOIN (
SELECT DISTINCT ON (name)
name, week AS last_week, value AS last_val
FROM tbl
ORDER BY name, week DESC
) l USING (name);
Atau lebih pendek:
SELECT *
FROM (SELECT DISTINCT ON (1) name, week AS first_week, value AS first_val FROM tbl ORDER BY 1,2) f
JOIN (SELECT DISTINCT ON (1) name, week AS last_week , value AS last_val FROM tbl ORDER BY 1,2 DESC) l USING (name);
Sederhana dan mudah dimengerti. Juga tercepat dalam tes lama saya. Penjelasan mendetail untuk DISTINCT ON
:
- Pilih baris pertama di setiap grup GROUP BY?
2x fungsi jendela, 1x DISTINCT ON
SELECT DISTINCT ON (name)
name, week AS first_week, value AS first_val
, first_value(week) OVER w AS last_week
, first_value(value) OVER w AS last_value
FROM tbl t
WINDOW w AS (PARTITION BY name ORDER BY week DESC)
ORDER BY name, week;
WINDOW
yang eksplisit klausa hanya mempersingkat kode, tidak berpengaruh pada kinerja.
first_value()
dari tipe komposit
Fungsi agregat min()
atau max()
tidak menerima tipe komposit sebagai input. Anda harus membuat fungsi agregat khusus (yang tidak terlalu sulit).
Tetapi fungsi jendela first_value()
dan last_value()
lakukan . Berdasarkan hal itu, kami dapat merancang solusi sederhana:
Kueri sederhana
SELECT DISTINCT ON (name)
name, week AS first_week, value AS first_value
,(first_value((week, value)) OVER (PARTITION BY name ORDER BY week DESC))::text AS l
FROM tbl t
ORDER BY name, week;
Keluaran memiliki semua data, tetapi nilai untuk minggu terakhir dimasukkan ke dalam catatan anonim (secara opsional dilemparkan ke text
). Anda mungkin memerlukan nilai yang telah didekomposisi.
Hasil terurai dengan penggunaan oportunistik jenis tabel
Untuk itu diperlukan jenis komposit yang terkenal. Definisi tabel yang disesuaikan akan memungkinkan penggunaan oportunistik dari jenis tabel itu sendiri secara langsung:
CREATE TABLE tbl (week int, value int, name text); -- optimized column order
week
dan value
datang dulu, jadi sekarang kita bisa mengurutkan berdasarkan jenis tabel itu sendiri:
SELECT (l).name, first_week, first_val
, (l).week AS last_week, (l).value AS last_val
FROM (
SELECT DISTINCT ON (name)
week AS first_week, value AS first_val
, first_value(t) OVER (PARTITION BY name ORDER BY week DESC) AS l
FROM tbl t
ORDER BY name, week
) sub;
Hasil yang didekomposisi dari jenis baris yang ditentukan pengguna
Itu mungkin tidak mungkin dalam banyak kasus. Daftarkan tipe komposit dengan CREATE TYPE
(permanen) atau dengan CREATE TEMP TABLE
(selama sesi):
CREATE TEMP TABLE nv(last_week int, last_val int); -- register composite type
SELECT name, first_week, first_val, (l).last_week, (l).last_val
FROM (
SELECT DISTINCT ON (name)
name, week AS first_week, value AS first_val
, first_value((week, value)::nv) OVER (PARTITION BY name ORDER BY week DESC) AS l
FROM tbl t
ORDER BY name, week
) sub;
Fungsi agregat khusus first()
&last()
Buat fungsi dan agregat satu kali per database:
CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement)
RETURNS anyelement
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $1;'
CREATE AGGREGATE public.first(anyelement) (
SFUNC = public.first_agg
, STYPE = anyelement
, PARALLEL = safe
);
CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement)
RETURNS anyelement
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $2';
CREATE AGGREGATE public.last(anyelement) (
SFUNC = public.last_agg
, STYPE = anyelement
, PARALLEL = safe
);
Kemudian:
SELECT name
, first(week) AS first_week, first(value) AS first_val
, last(week) AS last_week , last(value) AS last_val
FROM (SELECT * FROM tbl ORDER BY name, week) t
GROUP BY name;
Mungkin solusi yang paling elegan. Lebih cepat dengan modul tambahan first_last_agg
menyediakan implementasi C.
Bandingkan instruksi di Wiki Postgres.
Terkait:
- Menghitung pertumbuhan pengikut dari waktu ke waktu untuk setiap influencer
db<>main biola di sini (menampilkan semua)
sqlfiddle lama
Masing-masing kueri ini jauh lebih cepat daripada jawaban yang diterima saat ini dalam tes cepat pada tabel dengan 50 ribu baris dengan EXPLAIN ANALYZE
.
Ada lebih banyak cara. Bergantung pada distribusi data, gaya kueri yang berbeda mungkin (jauh) lebih cepat. Lihat:
- Optimalkan kueri GROUP BY untuk mengambil baris terbaru per pengguna