PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Contoh Tabel dan Metode Lain untuk Mendapatkan Tuple Acak

TABLESAMPLE PostgreSQL membawa beberapa keuntungan lebih banyak dibandingkan dengan cara tradisional lainnya untuk mendapatkan tupel acak.

TABLESAMPLE adalah klausa SQL SELECT dan menyediakan dua metode pengambilan sampel yaitu SYSTEM dan BERNOULLI . Dengan bantuan TABLESAMPLE kita dapat dengan mudah mengambil baris acak dari sebuah tabel. Untuk membaca lebih lanjut tentang TABLESAMPLE Anda dapat memeriksa posting blog sebelumnya .

Dalam posting blog ini kita akan berbicara tentang cara alternatif untuk mendapatkan baris acak. Bagaimana orang memilih baris acak sebelum TABLESAMPLE , apa pro dan kontra dari metode lain dan apa yang kami peroleh dengan TABLESAMPLE ?

Ada postingan blog yang luar biasa tentang memilih baris acak, Anda dapat mulai membaca postingan blog berikut untuk mendapatkan pemahaman mendalam tentang topik ini.

Pikiran Saya Mendapatkan Baris Acak

Mendapatkan Baris Acak dari Tabel Database

random_agg()

Mari kita bandingkan cara tradisional untuk mendapatkan baris acak dari tabel dengan cara baru yang disediakan oleh TABLESAMPLE.

Sebelum TABLESAMPLE klausa, ada 3 metode yang umum digunakan untuk memilih baris dari tabel secara acak.

1- Pesan secara acak()

Untuk tujuan pengujian, kita perlu membuat tabel dan memasukkan beberapa data ke dalamnya.

Mari buat tabel ts_test dan masukkan 1 juta baris ke dalamnya:

CREATE TABLE ts_test (
  id SERIAL PRIMARY KEY,
  title TEXT
);

INSERT INTO ts_test (title)
SELECT
    'Record #' || i
FROM
    generate_series(1, 1000000) i;

Mempertimbangkan pernyataan SQL berikut untuk memilih 10 baris acak:

SELECT * FROM ts_test ORDER BY random() LIMIT 10;

Menyebabkan PostgreSQL melakukan pemindaian tabel penuh dan juga pemesanan. Oleh karena itu metode ini tidak disukai untuk tabel dengan jumlah baris yang banyak karena alasan kinerja.

Mari kita lihat EXPLAIN ANALYZE output dari kueri ini di atas:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test ORDER BY random() LIMIT 10;
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=33959.03..33959.05 rows=10 width=36) (actual time=1956.786..1956.807 rows=10 loops=1)
 -> Sort (cost=33959.03..35981.18 rows=808863 width=36) (actual time=1956.780..1956.789 rows=10 loops=1)
 Sort Key: (random())
 Sort Method: top-N heapsort Memory: 25kB
 -> Seq Scan on ts_test (cost=0.00..16479.79 rows=808863 width=36) (actual time=0.276..1001.109 rows=1000000 loops=1)
 Planning time: 1.434 ms
 Execution time: 1956.900 ms
(7 rows)

Sebagai EXPLAIN ANALYZE menunjukkan, memilih 10 dari 1 juta baris membutuhkan waktu hampir 2 detik. Kueri juga menggunakan keluaran random() sebagai kunci pengurutan untuk mengurutkan hasil. Menyortir tampaknya menjadi tugas yang paling memakan waktu di sini. Mari kita jalankan ini dengan skenario menggunakan TABLESAMPLE .

Di PostgreSQL 9.5, untuk mendapatkan jumlah baris yang tepat secara acak, kita dapat menggunakan metode pengambilan sampel SYSTEM_ROWS. Disediakan oleh tsm_system_rows modul contrib, memungkinkan kita untuk menentukan berapa banyak baris yang harus dikembalikan dengan pengambilan sampel. Biasanya hanya persentase yang diminta yang dapat ditentukan dengan TABLESAMPLE SYSTEM dan BERNOULLI metode.

Pertama, kita harus membuat tsm_system_rows ekstensi untuk menggunakan metode ini karena keduanya TABLESAMPLE SYSTEM dan TABLESAMPLE BERNOULLI metode tidak menjamin bahwa persentase yang diberikan akan menghasilkan jumlah baris yang diharapkan. Silakan periksa TABLESAMPLE p sebelumnya harus mengingat mengapa mereka bekerja seperti itu.

Mari kita mulai dengan membuat tsm_system_rows ekstensi:

random=# CREATE EXTENSION tsm_system_rows;
CREATE EXTENSION

Sekarang mari kita bandingkan “ORDER BY random()EXPLAIN ANALYZE output dengan EXPLAIN ANALYZE keluaran tsm_system_rows kueri yang mengembalikan 10 baris acak dari 1 juta tabel baris.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE SYSTEM_ROWS(10);
 QUERY PLAN
-------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test (cost=0.00..4.10 rows=10 width=18) (actual time=0.069..0.083 rows=10 loops=1)
 Sampling: system_rows ('10'::bigint)
 Planning time: 0.646 ms
 Execution time: 0.159 ms
(4 rows)

Seluruh kueri membutuhkan waktu 0,159 ms. Metode pengambilan sampel ini sangat cepat dibandingkan dengan metode “ORDER BY random() ” yang membutuhkan waktu 1956,9 md.

2- Bandingkan dengan random()

SQL berikut memungkinkan kita untuk mengambil baris acak dengan probabilitas 10%

SELECT * FROM ts_test WHERE random() <= 0.1;

Metode ini lebih cepat daripada memesan secara acak karena tidak mengurutkan baris yang dipilih. Ini akan mengembalikan perkiraan persentase baris dari tabel seperti BERNOULLI atau SYSTEM TABLESAMPLE metode.

Mari kita periksa EXPLAIN ANALYZE keluaran random() kueri di atas:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test WHERE random() <= 0.1;
 QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on ts_test (cost=0.00..21369.00 rows=333333 width=18) (actual time=0.089..280.483 rows=100014 loops=1)
 Filter: (random() <= '0.1'::double precision)
 Rows Removed by Filter: 899986
 Planning time: 0.704 ms
 Execution time: 367.527 ms
(5 rows)

Kueri membutuhkan waktu 367,5 md. Jauh lebih baik daripada ORDER BY random() . Tetapi lebih sulit untuk mengontrol jumlah baris yang tepat. Mari kita bandingkan “random()EXPLAIN ANALYZE output dengan EXPLAIN ANALYZE keluaran TABLESAMPLE BERNOULLI kueri yang mengembalikan sekitar 10% baris acak dari tabel 1 juta baris.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE BERNOULLI (10);
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test  (cost=0.00..7369.00 rows=100000 width=18) (actual time=0.015..147.002 rows=100155 loops=1)
   Sampling: bernoulli ('10'::real)
 Planning time: 0.076 ms
 Execution time: 239.289 ms
(4 rows)

Kami memberikan 10 sebagai parameter untuk BERNOULLI karena kami membutuhkan 10% dari semua catatan.

Di sini kita dapat melihat bahwa BERNOULLI metode ini membutuhkan 239.289 ms untuk dieksekusi. Kedua metode ini sangat mirip dalam cara kerjanya, BERNOULLI sedikit lebih cepat karena pemilihan acak semua dilakukan pada level yang lebih rendah. Salah satu keuntungan menggunakan BERNOULLI dibandingkan dengan WHERE random() <= 0.1 adalah REPEATABLE klausa yang kami jelaskan di TABLESAMPLE sebelumnya pos.

3- Kolom tambahan dengan nilai acak

Metode ini menyarankan untuk menambahkan kolom baru dengan nilai yang ditetapkan secara acak, menambahkan indeks ke dalamnya, melakukan pengurutan berdasarkan kolom tersebut, dan secara opsional memperbarui nilainya secara berkala untuk mengacak distribusi.

Strategi ini memungkinkan pengambilan sampel acak yang sebagian besar dapat diulang. Ini bekerja jauh lebih cepat daripada metode pertama, tetapi perlu upaya untuk menyiapkan untuk pertama kalinya, dan menghasilkan biaya kinerja dalam operasi penyisipan, pembaruan, dan penghapusan.

Mari kita terapkan metode ini pada ts_test kita tabel.

Pertama, kita akan menambahkan kolom baru bernama randomcolumn dengan tipe presisi ganda karena PostgreSQL random() fungsi mengembalikan angka dalam presisi ganda.

random=# ALTER TABLE ts_test ADD COLUMN randomcolumn DOUBLE PRECISION;
ALTER TABLE

Kemudian kami akan memperbarui kolom baru menggunakan random() fungsi.

random=# \timing
Timing is on.
random=# BEGIN;
BEGIN
Time: 2.071 ms
random=# UPDATE ts_test SET randomcolumn = RANDOM();
UPDATE 1000000
Time: 8483.741 ms
random=# COMMIT;
COMMIT
Time: 2.615 ms

Metode ini memiliki biaya awal untuk membuat kolom baru dan mengisi kolom baru tersebut dengan nilai acak (0,0-1,0), tetapi ini adalah biaya satu kali. Dalam contoh ini, untuk 1 juta baris, dibutuhkan waktu hampir 8,5 detik.

Mari kita coba amati apakah hasil kita dapat direproduksi dengan membuat kueri 100 baris dengan metode baru kita:

random=# SELECT * FROM ts_test ORDER BY randomcolumn LIMIT 100;
 -------+---------------+----------------------
 13522  | Record #13522  | 6.4261257648468e-08
 671584 | Record #671584 | 6.4261257648468e-07
 714012 | Record #714012 | 1.95764005184174e-06
 162016 | Record #162016 | 3.44449654221535e-06
 106867 | Record #106867 | 3.66196036338806e-06
 865669 | Record #865669 | 3.96883115172386e-06
 927    | Record #927    | 4.65428456664085e-06
 526017 | Record #526017 | 4.65987250208855e-06
 98338  | Record #98338  | 4.91179525852203e-06
 769625 | Record #769625 | 4.91319224238396e-06
 ...
 462484 | Record #462484 | 9.83504578471184e-05
 (100 rows)

Saat kami menjalankan kueri di atas, sebagian besar kami mendapatkan kumpulan hasil yang sama tetapi ini tidak dijamin karena kami menggunakan random() fungsi untuk mengisi randomcolumn nilai dan dalam hal ini lebih dari satu kolom mungkin memiliki nilai yang sama. Untuk memastikan bahwa kami mendapatkan hasil yang sama untuk setiap kali dijalankan, kami harus meningkatkan kueri kami dengan menambahkan kolom ID ke ORDER BY klausa, dengan cara ini kami memastikan bahwa ORDER BY klausa menentukan kumpulan baris yang unik, karena kolom id memiliki indeks kunci utama di atasnya.

Sekarang mari kita jalankan kueri yang ditingkatkan di bawah ini:

random=# SELECT * FROM ts_test ORDER BY randomcolumn, id LIMIT 100;
 id | title | randomcolumn
--------+----------------+----------------------
 13522  | Record #13522  | 6.4261257648468e-08
 671584 | Record #671584 | 6.4261257648468e-07
 714012 | Record #714012 | 1.95764005184174e-06
 162016 | Record #162016 | 3.44449654221535e-06
 106867 | Record #106867 | 3.66196036338806e-06
 865669 | Record #865669 | 3.96883115172386e-06
 927    | Record #927    | 4.65428456664085e-06
 526017 | Record #526017 | 4.65987250208855e-06
 98338  | Record #98338  | 4.91179525852203e-06
 769625 | Record #769625 | 4.91319224238396e-06 
 ...
 462484 | Record #462484 | 9.83504578471184e-05
 (100 rows)

Sekarang kami yakin bahwa kami bisa mendapatkan sampel acak yang dapat direproduksi dengan menggunakan metode ini.

Saatnya melihat EXPLAIN ANALYZE hasil dari kueri terakhir kami:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test ORDER BY randomcolumn, id LIMIT 100;
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=55571.28..55571.53 rows=100 width=26) (actual time=1951.811..1952.039 rows=100 loops=1)
 -> Sort (cost=55571.28..58071.28 rows=1000000 width=26) (actual time=1951.804..1951.891 rows=100 loops=1)
 Sort Key: randomcolumn, id
 Sort Method: top-N heapsort Memory: 32kB
 -> Seq Scan on ts_test (cost=0.00..17352.00 rows=1000000 width=26) (actual time=0.058..995.011 rows=1000000 loops=1)
 Planning time: 0.481 ms
 Execution time: 1952.215 ms
(7 rows)

Untuk membandingkan metode ini dengan TABLESAMPLE metode, mari kita pilih SYSTEM metode dengan REPEATABLE pilihan, karena metode ini memberi kita hasil yang dapat direproduksi.

TABLESAMPLE SYSTEM metode pada dasarnya melakukan pengambilan sampel tingkat blok/halaman; ia membaca dan mengembalikan halaman acak dari disk. Dengan demikian ia memberikan keacakan di tingkat halaman alih-alih tingkat Tuple. Itulah mengapa sulit untuk mengontrol jumlah baris yang diambil dengan tepat. Jika tidak ada halaman yang diambil, kami mungkin tidak mendapatkan hasil apa pun. Pengambilan sampel tingkat halaman juga rentan terhadap efek pengelompokan.

TABLESAMPLE SYNTAX sama untuk BERNOULLI dan SYSTEM metode, kami akan menentukan persentase seperti yang kami lakukan di BERNOULLI metode.

Pengingat Cepat: SYNTAX

SELECT select_expression
FROM table_name
TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ]
...

Untuk mengambil sekitar 100 baris, kita perlu meminta 100 * 100 / 1 000 000 =0,01% dari baris tabel. Jadi persentase kita adalah 0,01.

Sebelum memulai query, mari kita ingat bagaimana REPEATABLE bekerja. Pada dasarnya kita akan memilih parameter seed dan kita akan mendapatkan hasil yang sama untuk setiap kali kita menggunakan seed yang sama dengan kueri sebelumnya. Jika kami menyediakan benih yang berbeda, kueri akan menghasilkan kumpulan hasil yang sangat berbeda.

Mari kita coba melihat hasilnya dengan kueri.

random=# Select * from ts_test tablesample system (0.01) repeatable (60);
 id | title | randomcolumn
--------+----------------+---------------------
 659598 | Record #659598 | 0.964113113470376
 659599 | Record #659599 | 0.531714483164251
 659600 | Record #659600 | 0.477636905387044
 659601 | Record #659601 | 0.861940925940871
 659602 | Record #659602 | 0.545977566856891
 659603 | Record #659603 | 0.326795583125204
 659604 | Record #659604 | 0.469275736715645
 659605 | Record #659605 | 0.734953186474741
 659606 | Record #659606 | 0.41613544523716
 ...
 659732 | Record #659732 | 0.893704727292061
 659733 | Record #659733 | 0.847225237637758
 (136 rows)

Kami mendapatkan 136 baris, karena Anda dapat menganggap jumlah ini tergantung pada berapa banyak baris yang disimpan dalam satu halaman.

Sekarang mari kita coba menjalankan kueri yang sama dengan nomor benih yang sama:

random=# Select * from ts_test tablesample system (0.01) repeatable (60);
 id | title | randomcolumn
--------+----------------+---------------------
 659598 | Record #659598 | 0.964113113470376
 659599 | Record #659599 | 0.531714483164251
 659600 | Record #659600 | 0.477636905387044
 659601 | Record #659601 | 0.861940925940871
 659602 | Record #659602 | 0.545977566856891
 659603 | Record #659603 | 0.326795583125204
 659604 | Record #659604 | 0.469275736715645
 659605 | Record #659605 | 0.734953186474741
 659606 | Record #659606 | 0.41613544523716 
 ...
 659732 | Record #659732 | 0.893704727292061
 659733 | Record #659733 | 0.847225237637758
 (136 rows)

Kami mendapatkan hasil yang sama berkat REPEATABLE pilihan. Sekarang kita dapat membandingkan TABLESAMPLE SYSTEM metode dengan metode kolom acak dengan melihat EXPLAIN ANALYZE keluaran.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE SYSTEM (0.01) REPEATABLE (60);
 QUERY PLAN
---------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test (cost=0.00..5.00 rows=100 width=26) (actual time=0.091..0.230 rows=136 loops=1)
 Sampling: system ('0.01'::real) REPEATABLE ('60'::double precision)
 Planning time: 0.323 ms
 Execution time: 0.398 ms
(4 rows)

SYSTEM metode ini menggunakan pemindaian sampel dan sangat cepat. Satu-satunya kemunduran dari metode ini adalah kami tidak dapat menjamin bahwa persentase yang diberikan akan menghasilkan jumlah baris yang diharapkan.

Kesimpulan

Dalam posting blog ini kami membandingkan TABLESAMPLE standar (SYSTEM , BERNOULLI ) dan tsm_system_rows modul dengan metode acak yang lebih lama.

Di sini Anda dapat meninjau temuan dengan cepat:

  • ORDER BY random() lambat karena menyortir
  • random() <= 0.1 mirip dengan BERNOULLI metode
  • Menambahkan kolom dengan nilai acak membutuhkan pekerjaan awal dan dapat menyebabkan masalah kinerja
  • SYSTEM cepat tetapi sulit untuk mengontrol jumlah baris
  • tsm_system_rows cepat dan dapat mengontrol jumlah baris

Akibatnya tsm_system_rows mengalahkan metode lain untuk memilih hanya beberapa baris acak.

Tapi pemenang sebenarnya adalah TABLESAMPLE diri. Berkat ekstensibilitasnya, ekstensi khusus (yaitu tsm_system_rows , tsm_system_time ) dapat dikembangkan menggunakan TABLESAMPLE fungsi metode.

Catatan pengembang: Informasi lebih lanjut tentang cara menulis metode pengambilan sampel khusus dapat ditemukan di bab Penulisan Tabel Metode Pengambilan Sampel di dokumentasi PostgreSQL.

Catatan untuk masa depan: Kami akan membahas Proyek AXLE dan ekstensi tsm_system_time di TABLESAMPLE berikutnya entri blog.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Postgresql :Koneksi ditolak. Periksa apakah nama host dan port sudah benar dan postmaster menerima koneksi TCP/IP

  2. Masukkan data dalam 3 tabel sekaligus menggunakan Postgres

  3. Ubah pengkodean basis data PostgreSql

  4. MASUKKAN KE ... DARI PILIH ... MENGEMBALIKAN pemetaan id

  5. Berfungsi untuk mengembalikan kumpulan kolom dinamis untuk tabel yang diberikan